1 /*******************************************************************************
2 * Copyright (c) 2015 Ericsson
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
9 package org
.eclipse
.tracecompass
.internal
.ctf
.core
.event
.metadata
.tsdl
.variant
;
11 import static org
.eclipse
.tracecompass
.internal
.ctf
.core
.event
.metadata
.tsdl
.TsdlUtils
.childTypeError
;
13 import java
.util
.HashSet
;
14 import java
.util
.List
;
17 import org
.antlr
.runtime
.tree
.CommonTree
;
18 import org
.eclipse
.jdt
.annotation
.NonNullByDefault
;
19 import org
.eclipse
.tracecompass
.ctf
.core
.event
.metadata
.DeclarationScope
;
20 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.EnumDeclaration
;
21 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.IDeclaration
;
22 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.VariantDeclaration
;
23 import org
.eclipse
.tracecompass
.ctf
.core
.trace
.CTFTrace
;
24 import org
.eclipse
.tracecompass
.ctf
.parser
.CTFParser
;
25 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.event
.metadata
.AbstractScopedCommonTreeParser
;
26 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.event
.metadata
.ParseException
;
30 * A CTF variant is a selection between different types. A CTF variant must
31 * always be defined within the scope of a structure or within fields contained
32 * within a structure (defined recursively). A _tag_ enumeration field must
33 * appear in either the same static scope, prior to the variant field (in field
34 * declaration order), in an upper static scope, or in an upper dynamic scope
35 * (see [Static and dynamic scopes](#spec7.3.2)). The type selection is
36 * indicated by the mapping from the enumeration value to the string used as
37 * variant type selector. The field to use as tag is specified by the
38 * `tag_field`, specified between `< >` after the `variant` keyword for unnamed
39 * variants, and after _variant name_ for named variants. It is not required
40 * that each enumeration mapping appears as variant type tag field. It is also
41 * not required that each variant type tag appears as enumeration mapping.
42 * However, it is required that any enumeration mapping encountered within a
43 * stream has a matching variant type tag field. <br>
44 * The alignment of the variant is the alignment of the type as selected by the
45 * tag value for the specific instance of the variant. The size of the variant
46 * is the size as selected by the tag value for the specific instance of the
48 * The alignment of the type containing the variant is independent of the
49 * variant alignment. For instance, if a structure contains two fields, a 32-bit
50 * integer, aligned on 32 bits, and a variant, which contains two choices:
51 * either a 32-bit field, aligned on 32 bits, or a 64-bit field, aligned on 64
52 * bits, the alignment of the outmost structure will be 32-bit (the alignment of
53 * its largest field, disregarding the alignment of the variant). The alignment
54 * of the variant will depend on the selector: if the variant's 32-bit field is
55 * selected, its alignment will be 32-bit, or 64-bit otherwise. It is important
56 * to note that variants are specifically tailored for compactness in a stream.
57 * Therefore, the relative offsets of compound type fields can vary depending on
58 * the offset at which the compound type starts if it contains a variant that
59 * itself contains a type with alignment larger than the largest field contained
60 * within the compound type. This is caused by the fact that the compound type
61 * may contain the enumeration that select the variant's choice, and therefore
62 * the alignment to be applied to the compound type cannot be determined before
63 * encountering the enumeration. <br>
64 * Each variant type selector possess a field name, which is a unique identifier
65 * within the variant. The identifier is not allowed to use any [reserved
66 * keyword](#C.1.2). Replacing reserved keywords with underscore-prefixed field
67 * names is recommended. Fields starting with an underscore should have their
68 * leading underscore removed by the CTF trace readers. <br>
69 * A named variant declaration followed by its definition within a structure
81 enum : integer_type { sel1, sel2, sel3, <em>more</em> } tag_field;
83 variant name <tag_field> v;
87 * An unnamed variant definition within a structure is expressed by the
88 * following TSDL metadata: <br>
92 enum : integer_type { sel1, sel2, sel3, <em>more</em> } tag_field;
103 * Example of a named variant within a sequence that refers to a single tag
114 enum : uint2_t { a, b, c } choice;
116 variant example <choice> v[seqlen];
120 * Example of an unnamed variant: <br>
124 enum : uint2_t { a, b, c, d } choice;
126 // Unrelated fields can be added between the variant and its tag
140 * Example of an unnamed variant within an array: <br>
144 enum : uint2_t { a, b, c } choice;
154 * Example of a variant type definition within a structure, where the defined
155 * type is then declared within an array of structures. This variant refers to a
156 * tag located in an upper static scope. This example clearly shows that a
157 * variant type definition referring to the tag `x` uses the closest preceding
158 * field from the static scope of the type definition. <br>
162 enum : uint2_t { a, b, c, d } x;
164 // "x" refers to the preceding "x" enumeration in the
165 // static scope of the type definition.
167 typedef variant <x> {
174 enum : int { x, y, z } x; // This enumeration is not used by "v".
176 // "v" uses the "enum : uint2_t { a, b, c, d }" tag.
182 * @author Matthew Khouzam
183 * @author Efficios - Javadoc preamble
187 public final class VariantParser
extends AbstractScopedCommonTreeParser
{
190 * Parameter object with a trace and current scope
192 * @author Matthew Khouzam
196 public static final class Param
implements ICommonTreeParserParameter
{
197 private final DeclarationScope fDeclarationScope
;
198 private final CTFTrace fTrace
;
208 public Param(CTFTrace trace
, DeclarationScope scope
) {
210 fDeclarationScope
= scope
;
217 public final static VariantParser INSTANCE
= new VariantParser();
219 private VariantParser() {
226 * the variant AST node
228 * the {@link Param} parameter object
229 * @return a populated {@link VariantDeclaration}
230 * @throws ParseException
231 * the AST is malformed
234 public VariantDeclaration
parse(CommonTree variant
, ICommonTreeParserParameter param
) throws ParseException
{
235 if (!(param
instanceof Param
)) {
236 throw new IllegalArgumentException("Param must be a " + Param
.class.getCanonicalName()); //$NON-NLS-1$
238 final DeclarationScope scope
= ((Param
) param
).fDeclarationScope
;
240 List
<CommonTree
> children
= variant
.getChildren();
241 VariantDeclaration variantDeclaration
= null;
243 boolean hasName
= false;
244 String variantName
= null;
246 boolean hasBody
= false;
247 CommonTree variantBody
= null;
249 boolean hasTag
= false;
250 String variantTag
= null;
252 for (CommonTree child
: children
) {
253 switch (child
.getType()) {
254 case CTFParser
.VARIANT_NAME
:
258 CommonTree variantNameIdentifier
= (CommonTree
) child
.getChild(0);
260 variantName
= variantNameIdentifier
.getText();
263 case CTFParser
.VARIANT_TAG
:
267 CommonTree variantTagIdentifier
= (CommonTree
) child
.getChild(0);
269 variantTag
= variantTagIdentifier
.getText();
272 case CTFParser
.VARIANT_BODY
:
280 throw childTypeError(child
);
286 * If variant has a name, check if already defined in the current
290 && (scope
.lookupVariant(variantName
) != null)) {
291 throw new ParseException("variant " + variantName
//$NON-NLS-1$
292 + " already defined."); //$NON-NLS-1$
295 /* Create the declaration */
296 variantDeclaration
= new VariantDeclaration();
298 CTFTrace trace
= ((Param
) param
).fTrace
;
300 VariantBodyParser
.INSTANCE
.parse(variantBody
, new VariantBodyParser
.Param(variantDeclaration
, trace
, variantName
, scope
));
302 /* If variant has name, add it to the current scope. */
304 scope
.registerVariant(variantName
, variantDeclaration
);
306 } else /* !hasBody */ {
310 /* Lookup the name in the current scope. */
311 variantDeclaration
= scope
.lookupVariantRecursive(variantName
);
314 * If not found, it means that a struct with such name has not
317 if (variantDeclaration
== null) {
318 throw new ParseException("variant " + variantName
//$NON-NLS-1$
319 + " is not defined"); //$NON-NLS-1$
322 /* !Name and !body */
324 /* We can't do anything with that. */
325 throw new ParseException("variant with no name and no body"); //$NON-NLS-1$
330 variantDeclaration
.setTag(variantTag
);
332 IDeclaration decl
= scope
.lookupIdentifierRecursive(variantTag
);
334 throw new ParseException("Variant tag not found: " + variantTag
); //$NON-NLS-1$
336 if (!(decl
instanceof EnumDeclaration
)) {
337 throw new ParseException("Variant tag must be an enum: " + variantTag
); //$NON-NLS-1$
339 EnumDeclaration tagDecl
= (EnumDeclaration
) decl
;
340 Set
<String
> intersection
= new HashSet
<>(tagDecl
.getLabels());
341 intersection
.retainAll(variantDeclaration
.getFields().keySet());
342 if (intersection
.isEmpty()) {
343 throw new ParseException("Variant contains no values of the tag, impossible to use: " + variantName
); //$NON-NLS-1$
347 return variantDeclaration
;