/******************************************************************************* * Copyright (c) 2015 Ericsson * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.struct; import static org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.TsdlUtils.childTypeError; import java.util.List; import org.antlr.runtime.tree.CommonTree; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.ctf.core.event.metadata.DeclarationScope; import org.eclipse.tracecompass.ctf.core.event.types.StructDeclaration; import org.eclipse.tracecompass.ctf.core.trace.CTFTrace; import org.eclipse.tracecompass.ctf.parser.CTFParser; import org.eclipse.tracecompass.internal.ctf.core.event.metadata.AbstractScopedCommonTreeParser; import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException; import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.AlignmentParser; import org.eclipse.tracecompass.internal.ctf.core.event.types.StructDeclarationFlattener; /** * * Structures are aligned on the largest alignment required by basic types * contained within the structure. (This follows the ISO/C standard for * structures) <br> * TSDL metadata representation of a named structure: * * <pre> struct name { field_type field_name; field_type field_name; // ... }; * </pre> * * Example: * * <pre> struct example { integer { // nameless type size = 16; signed = true; align = 16; } first_field_name; uint64_t second_field_name; // named type declared in the metadata }; * </pre> * * The fields are placed in a sequence next to each other. They each possess a * field name, which is a unique identifier within the structure. The identifier * is not allowed to use any [reserved keyword](#specC.1.2). Replacing reserved * keywords with underscore-prefixed field names is <strong>recommended</strong> * . Fields starting with an underscore should have their leading underscore * removed by the CTF trace readers. <br> * A nameless structure can be declared as a field type or as part of a * `typedef`: * * <pre> struct { // ... } * * </pre> * * Alignment for a structure compound type can be forced to a minimum value by * adding an `align` specifier after the declaration of a structure body. This * attribute is read as: `align(value)`. The value is specified in bits. The * structure will be aligned on the maximum value between this attribute and the * alignment required by the basic types contained within the structure. e.g. * * <pre> struct { // ... } align(32) } * </pre> * * @author Matthew Khouzam * @author Efficios - Javadoc preamble */ public final class StructParser extends AbstractScopedCommonTreeParser { /** * Parameter object * * @author Matthew Khouzam * */ @NonNullByDefault public static final class Param implements ICommonTreeParserParameter { private final DeclarationScope fDeclarationScope; private final @Nullable CommonTree fIdentifier; private final CTFTrace fTrace; /** * Constructor * * @param trace * the trace * @param identifier * the identifier * @param scope * the current scope */ public Param(CTFTrace trace, @Nullable CommonTree identifier, DeclarationScope scope) { fTrace = trace; fIdentifier = identifier; fDeclarationScope = scope; } } /** * The instance */ public static final StructParser INSTANCE = new StructParser(); private StructParser() { } /** * Parse the struct AST node, So everything in "struct a {...};" * * @param struct * the struct AST node * @param param * the parameter object of {@link Param} type. * @return a {@link StructDeclaration} that is fully populated * @throws ParseException * the AST is malformed */ @Override public StructDeclaration parse(CommonTree struct, ICommonTreeParserParameter param) throws ParseException { if (!(param instanceof Param)) { throw new IllegalArgumentException("Param must be a " + Param.class.getCanonicalName()); //$NON-NLS-1$ } final DeclarationScope scope = ((Param) param).fDeclarationScope; CommonTree identifier = ((Param) param).fIdentifier; List<CommonTree> children = struct.getChildren(); /* The return value */ StructDeclaration structDeclaration = null; /* Name */ String structName = null; boolean hasName = false; /* Body */ CommonTree structBody = null; boolean hasBody = false; /* Align */ long structAlign = 0; /* Loop on all children and identify what we have to work with. */ for (CommonTree child : children) { switch (child.getType()) { case CTFParser.STRUCT_NAME: { hasName = true; CommonTree structNameIdentifier = (CommonTree) child.getChild(0); structName = structNameIdentifier.getText(); break; } case CTFParser.STRUCT_BODY: { hasBody = true; structBody = child; break; } case CTFParser.ALIGN: { CommonTree structAlignExpression = (CommonTree) child.getChild(0); structAlign = AlignmentParser.INSTANCE.parse(structAlignExpression, null); break; } default: throw childTypeError(child); } } if (!hasName && identifier != null) { structName = identifier.getText(); hasName = true; } /* * If a struct has just a body and no name (just like the song, * "A Struct With No Name" by America (sorry for that...)), it's a * definition of a new type, so we create the type declaration and * return it. We can't add it to the declaration scope since there is no * name, but that's what we want because it won't be possible to use it * again to declare another field. * * If it has just a name, we look it up in the declaration scope and * return the associated declaration. If it is not found in the * declaration scope, it means that a struct with that name has not been * declared, which is an error. * * If it has both, then we create the type declaration and register it * to the current scope. * * If it has none, then what are we doing here ? */ if (hasBody) { /* * If struct has a name, check if already defined in the current * scope. */ if (hasName && (scope.lookupStruct(structName) != null)) { throw new ParseException("struct " + structName //$NON-NLS-1$ + " already defined."); //$NON-NLS-1$ } /* Create the declaration */ structDeclaration = new StructDeclaration(structAlign); CTFTrace trace = ((Param) param).fTrace; /* Parse the body */ StructBodyParser.INSTANCE.parse(structBody, new StructBodyParser.Param(structDeclaration, trace, structName, scope)); /* If struct has name, add it to the current scope. */ if (hasName) { scope.registerStruct(structName, structDeclaration); } } else /* !hasBody */ { if (hasName) { /* Name and !body */ /* Lookup the name in the current scope. */ structDeclaration = scope.lookupStructRecursive(structName); /* * If not found, it means that a struct with such name has not * been defined */ if (structDeclaration == null) { throw new ParseException("struct " + structName //$NON-NLS-1$ + " is not defined"); //$NON-NLS-1$ } } else { /* !Name and !body */ /* We can't do anything with that. */ throw new ParseException("struct with no name and no body"); //$NON-NLS-1$ } } return StructDeclarationFlattener.tryFlattenStruct(structDeclaration); } }