blob: cc70d76e228a298be51f9fb886f66ec217f99c39 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2017 Ericsson, Ecole Polytechnique de Montreal and others
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors: Matthew Khouzam - Initial Design and Grammar
* Contributors: Simon Marchi - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.ctf.core.event.metadata;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.ctf.core.event.types.EnumDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.StructDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.VariantDeclaration;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException;
/**
* <b><u>DeclarationScope</u></b>
* <p>
* A DeclarationScope keeps track of the various CTF declarations for a given
* scope.
*
* TODO: The notion of "symbols" and the notion of "scope" are misused in this
* parser, which leads to inefficient tree management. It should be cleaned up.
*
* @author Matthew Khouzam
* @author Simon Marchi
* @since 1.1
*
*/
public class DeclarationScope {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
private static final AtomicLong UNIQUE_ID_FACTORY = new AtomicLong(0);
private final DeclarationScope fParentScope;
private @NonNull Map<String, DeclarationScope> fChildren = new HashMap<>();
private final Map<String, StructDeclaration> fStructs = new HashMap<>();
private final Map<String, EnumDeclaration> fEnums = new HashMap<>();
private final Map<String, VariantDeclaration> fVariants = new HashMap<>();
private final Map<String, IDeclaration> fTypes = new HashMap<>();
private final Map<String, IDeclaration> fIdentifiers = new HashMap<>();
private String fName;
private long fUniqueId;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Creates a declaration scope with no parent.
*/
public DeclarationScope() {
this(null, (String) null);
}
/**
* Creates a declaration scope with the specified parent.
*
* @param parentScope
* The parent of the newly created scope.
* @param name
* scope name
*/
public DeclarationScope(DeclarationScope parentScope, String name) {
fParentScope = parentScope;
fName = name;
fUniqueId = UNIQUE_ID_FACTORY.getAndIncrement();
if (parentScope != null) {
parentScope.registerChild(name, this);
}
}
/**
* Copy constructor with a specified parent and a scope to copy
*
* @param parentScope
* the parent
* @param source
* the scope to copy
*/
private DeclarationScope(DeclarationScope parentScope, DeclarationScope source) {
this(parentScope, source.fName);
fUniqueId = source.fUniqueId;
fStructs.putAll(source.fStructs);
fEnums.putAll(source.fEnums);
fVariants.putAll(source.fVariants);
fTypes.putAll(source.fTypes);
fIdentifiers.putAll(source.fIdentifiers);
/*
* Copy all the children recursively
*/
for (DeclarationScope child : source.fChildren.values()) {
addChild(child);
}
}
private void registerChild(String name, DeclarationScope declarationScope) {
fChildren.put(name, declarationScope);
}
private boolean checkValid(DeclarationScope childSource) {
long uniqueId = childSource.fUniqueId;
DeclarationScope parent = this;
while (parent != null) {
if (uniqueId == parent.fUniqueId) {
return false;
}
parent = parent.getParentScope();
}
return true;
}
// ------------------------------------------------------------------------
// Getters/Setters/Predicates
// ------------------------------------------------------------------------
/**
* Returns the parent of the current scope.
*
* @return The parent scope.
*/
public DeclarationScope getParentScope() {
return fParentScope;
}
/**
* Sets the name of the scope
*
* @param name
* the name
*/
public void setName(String name) {
if (hasParent()) {
fParentScope.fChildren.remove(fName);
fParentScope.fChildren.put(name, this);
fName = name;
}
}
// ------------------------------------------------------------------------
// Registration operations
// ------------------------------------------------------------------------
/**
* Registers a type declaration.
*
* @param name
* The name of the type.
* @param declaration
* The type declaration.
* @throws ParseException
* if a type with the same name has already been defined.
*/
public void registerType(String name, IDeclaration declaration)
throws ParseException {
/* Check if the type has been defined in the current scope */
if (fTypes.containsKey(name)) {
throw new ParseException("Type has already been defined:" + name); //$NON-NLS-1$
}
/* Add it to the register. */
fTypes.put(name, declaration);
}
/**
* Registers an identifier declaration.
*
* @param name
* name of the identifier
* @param declaration
* the identfier's declaration
* @throws ParseException
* if an identifier with the same name has already been defined.
*/
public void registerIdentifier(String name, IDeclaration declaration) throws ParseException {
/* Check if the type has been defined in the current scope */
if (fIdentifiers.containsKey(name)) {
throw new ParseException("Identifier has already been defined:" + name); //$NON-NLS-1$
}
/* Add it to the register. */
fIdentifiers.put(name, declaration);
}
/**
* Registers a struct declaration.
*
* @param name
* The name of the struct.
* @param declaration
* The declaration of the struct.
* @throws ParseException
* if a struct with the same name has already been registered.
*/
public void registerStruct(String name, StructDeclaration declaration)
throws ParseException {
/* Check if the struct has been defined in the current scope. */
if (fStructs.containsKey(name)) {
throw new ParseException("Struct has already been defined:" + name); //$NON-NLS-1$
}
/* Add it to the register. */
fStructs.put(name, declaration);
/* It also defined a new type, so add it to the type declarations. */
String structPrefix = "struct "; //$NON-NLS-1$
registerType(structPrefix + name, declaration);
}
/**
* Registers an enum declaration.
*
* @param name
* The name of the enum.
* @param declaration
* The declaration of the enum.
* @throws ParseException
* if an enum with the same name has already been registered.
*/
public void registerEnum(String name, EnumDeclaration declaration)
throws ParseException {
/* Check if the enum has been defined in the current scope. */
if (lookupEnum(name) != null) {
throw new ParseException("Enum has already been defined:" + name); //$NON-NLS-1$
}
/* Add it to the register. */
fEnums.put(name, declaration);
/* It also defined a new type, so add it to the type declarations. */
String enumPrefix = "enum "; //$NON-NLS-1$
registerType(enumPrefix + name, declaration);
}
/**
* Registers a variant declaration.
*
* @param name
* The name of the variant.
* @param declaration
* The declaration of the variant.
* @throws ParseException
* if a variant with the same name has already been registered.
*/
public void registerVariant(String name, VariantDeclaration declaration)
throws ParseException {
/* Check if the variant has been defined in the current scope. */
final VariantDeclaration lookupVariant = lookupVariant(name);
if (declaration.equals(lookupVariant)) {
return;
}
if (lookupVariant != null) {
throw new ParseException("Variant has already been defined:" + name); //$NON-NLS-1$
}
/* Add it to the register. */
fVariants.put(name, declaration);
/* It also defined a new type, so add it to the type declarations. */
String variantPrefix = "variant "; //$NON-NLS-1$
registerType(variantPrefix + name, declaration);
}
// ------------------------------------------------------------------------
// Lookup operations
// ------------------------------------------------------------------------
/**
* Lookup the children scopes of this scope
*
* @param name
* the child to lookup
* @return the scope or null
*/
public @Nullable DeclarationScope lookupChild(String name) {
return fChildren.get(name);
}
/**
* Lookup the children scopes of this scope and this scope's parents
*
* @param name
* the child to lookup
* @return the scope or null
*/
public @Nullable DeclarationScope lookupChildRecursive(String name) {
final DeclarationScope declarationScope = fChildren.get(name);
if (declarationScope == null && fParentScope != null) {
return fParentScope.lookupChildRecursive(name);
}
return declarationScope;
}
/**
* Looks up a type declaration in the current scope.
*
* @param name
* The name of the type to search for.
* @return The type declaration, or null if no type with that name has been
* defined.
*/
public IDeclaration lookupType(String name) {
return fTypes.get(name);
}
/**
* Looks up a type declaration in the current scope and recursively in the
* parent scopes.
*
* @param name
* The name of the type to search for.
* @return The type declaration, or null if no type with that name has been
* defined.
*/
public IDeclaration lookupTypeRecursive(String name) {
IDeclaration declaration = lookupType(name);
if (declaration != null) {
return declaration;
} else if (hasParent()) {
return fParentScope.lookupTypeRecursive(name);
} else {
return null;
}
}
/**
* Looks up a struct declaration.
*
* @param name
* The name of the struct to search for.
* @return The struct declaration, or null if no struct with that name has
* been defined.
*/
public StructDeclaration lookupStruct(String name) {
return fStructs.get(name);
}
/**
* Looks up a struct declaration in the current scope and recursively in the
* parent scopes.
*
* @param name
* The name of the struct to search for.
* @return The struct declaration, or null if no struct with that name has
* been defined.
*/
public StructDeclaration lookupStructRecursive(String name) {
StructDeclaration declaration = lookupStruct(name);
if (declaration != null) {
return declaration;
} else if (hasParent()) {
return fParentScope.lookupStructRecursive(name);
} else {
return null;
}
}
/**
* Looks up an enum declaration.
*
* @param name
* The name of the enum to search for.
* @return The enum declaration, or null if no enum with that name has been
* defined.
*/
public EnumDeclaration lookupEnum(String name) {
return fEnums.get(name);
}
/**
* Looks up an enum declaration in the current scope and recursively in the
* parent scopes.
*
* @param name
* The name of the enum to search for.
* @return The enum declaration, or null if no enum with that name has been
* defined.
*/
public EnumDeclaration lookupEnumRecursive(String name) {
EnumDeclaration declaration = lookupEnum(name);
if (declaration != null) {
return declaration;
} else if (hasParent()) {
return fParentScope.lookupEnumRecursive(name);
} else {
return null;
}
}
/**
* Looks up a variant declaration.
*
* @param name
* The name of the variant to search for.
* @return The variant declaration, or null if no variant with that name has
* been defined.
*/
public VariantDeclaration lookupVariant(String name) {
return fVariants.get(name);
}
/**
* Looks up a variant declaration in the current scope and recursively in
* the parent scopes.
*
* @param name
* The name of the variant to search for.
* @return The variant declaration, or null if no variant with that name has
* been defined.
*/
public VariantDeclaration lookupVariantRecursive(String name) {
VariantDeclaration declaration = lookupVariant(name);
if (declaration != null) {
return declaration;
} else if (hasParent()) {
return fParentScope.lookupVariantRecursive(name);
} else {
return null;
}
}
private boolean hasParent() {
return fParentScope != null;
}
/**
* Lookup query for an identifier in this scope.
*
* @param identifier
* the name of the identifier to search for. In the case of int
* x; it would be "x"
* @return the declaration of the type associated to that identifier
*/
public IDeclaration lookupIdentifier(String identifier) {
return fIdentifiers.get(identifier);
}
/**
* Lookup query for an identifier through this scope and its ancestors. An
* ancestor scope is a scope in which this scope is nested.
*
* @param identifier
* the name of the identifier to search for. In the case of int
* x; it would be "x"
* @return the declaration of the type associated to that identifier
*/
public IDeclaration lookupIdentifierRecursive(String identifier) {
if (identifier.contains(".")) { //$NON-NLS-1$
String[] scopes = identifier.split("\\."); //$NON-NLS-1$
return lookupIdentifierRecursive(scopes);
}
IDeclaration declaration = lookupIdentifier(identifier);
if (declaration != null) {
return declaration;
} else if (hasParent()) {
return fParentScope.lookupIdentifierRecursive(identifier);
}
return null;
}
private IDeclaration lookupIdentifierRecursive(String[] scopes) {
if (scopes.length < 1) {
return null;
}
// find first element
DeclarationScope scope = this;
scope = lookupRoot(scopes, scope);
if (scope == null) {
return null;
}
for (int i = 1; i < scopes.length; i++) {
final String scopeName = scopes[i];
if (scope == null) {
return null;
}
IDeclaration declaration = lookupIdentifierElement(scope, scopeName, Arrays.copyOfRange(scopes, i, scopes.length));
if (declaration != null) {
return declaration;
}
scope = scope.fChildren.get(scopeName);
}
return null;
}
private static DeclarationScope lookupRoot(String[] scopes, DeclarationScope originScope) {
DeclarationScope scope = originScope;
final String rootElement = scopes[0];
while (!rootElement.equals(scope.fName)) {
if (!scope.hasParent()) {
return scope.fChildren.get(rootElement);
}
scope = scope.getParentScope();
}
return scope;
}
private IDeclaration lookupIdentifierElement(DeclarationScope scope, String scopeName, String[] scopes) {
if (scope.fStructs.containsKey(scopeName)) {
return lookupStructScope(scope, scopeName, scopes);
} else if (scope.fTypes.containsKey(scopeName)) {
return scope.fTypes.get(scopeName);
} else if (scope.fEnums.containsKey(scopeName)) {
return scope.fEnums.get(scopeName);
} else if (scope.fIdentifiers.containsKey(scopeName)) {
return scope.fIdentifiers.get(scopeName);
}
return null;
}
private IDeclaration lookupStructScope(DeclarationScope scope, String scopeName, String[] scopes) {
final IDeclaration structDeclaration = scope.fStructs.get(scopeName);
if (scopes.length <= 1) {
return structDeclaration;
}
return lookupIdentifierStructElement((StructDeclaration) structDeclaration, scopes[1], Arrays.copyOfRange(scopes, 2, scopes.length));
}
private IDeclaration lookupIdentifierStructElement(StructDeclaration structDeclaration, String string, String[] children) {
IDeclaration field = structDeclaration.getField(string);
if (children == null || children.length <= 0) {
return field;
}
if (field instanceof StructDeclaration) {
StructDeclaration fieldStructDeclaration = (StructDeclaration) field;
return lookupIdentifierStructElement(fieldStructDeclaration, children[0], Arrays.copyOfRange(children, 1, children.length));
}
return null;
}
/**
* Get all the type names of this scope.
*
* @return The type names
*/
public Set<String> getTypeNames() {
return fTypes.keySet();
}
/**
* Replace a type with a new one.
*
* @param name
* The name of the type
* @param newType
* The type
* @throws ParseException
* If the type does not exist.
*/
public void replaceType(String name, IDeclaration newType) throws ParseException {
if (fTypes.containsKey(name)) {
fTypes.put(name, newType);
} else {
throw new ParseException("Trace does not contain type:" + name); //$NON-NLS-1$
}
}
/**
* Add a child scope
*
* @param scope
* the child
*/
public void addChild(DeclarationScope scope) {
if (checkValid(scope)) {
DeclarationScope scopeCopy = new DeclarationScope(this, scope);
registerChild(scopeCopy.fName, scopeCopy);
}
}
@Override
public String toString() {
return "Scope : " + fName + " children: " + fChildren.size() + (fParentScope == null ? "" : (" parent " + fParentScope.fName)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}