All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.royale.compiler.internal.definitions.DefinitionBase Maven / Gradle / Ivy

There is a newer version: 0.9.12
Show newest version
/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.royale.compiler.internal.definitions;

import static org.apache.royale.compiler.common.ISourceLocation.UNKNOWN;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.royale.abc.ABCConstants;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.abc.semantics.Namespace;
import org.apache.royale.abc.semantics.Nsset;
import org.apache.royale.compiler.asdoc.IASDocComment;
import org.apache.royale.compiler.common.ASModifier;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.common.ModifiersSet;
import org.apache.royale.compiler.common.NodeReference;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.constants.IMetaAttributeConstants;
import org.apache.royale.compiler.constants.INamespaceConstants;
import org.apache.royale.compiler.definitions.IAccessorDefinition;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.IDocumentableDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.IPackageDefinition;
import org.apache.royale.compiler.definitions.IScopedDefinition;
import org.apache.royale.compiler.definitions.metadata.IDeprecationInfo;
import org.apache.royale.compiler.definitions.metadata.IMetaTag;
import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.definitions.references.INamespaceResolvedReference;
import org.apache.royale.compiler.definitions.references.IReference;
import org.apache.royale.compiler.filespecs.IFileSpecification;
import org.apache.royale.compiler.internal.common.Counter;
import org.apache.royale.compiler.internal.definitions.metadata.DeprecationInfo;
import org.apache.royale.compiler.internal.definitions.metadata.MetaTag;
import org.apache.royale.compiler.internal.parsing.as.OffsetLookup;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.scopes.ASFileScope;
import org.apache.royale.compiler.internal.scopes.ASProjectScope;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.internal.scopes.ASScopeBase;
import org.apache.royale.compiler.internal.scopes.SWCFileScopeProvider.SWCFileScope;
import org.apache.royale.compiler.mxml.IMXMLTagData;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.scopes.IASScope;
import org.apache.royale.compiler.scopes.IDefinitionSet;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IDefinitionNode;
import org.apache.royale.compiler.tree.as.IDocumentableDefinitionNode;
import org.apache.royale.compiler.tree.as.IExpressionNode;
import org.apache.royale.compiler.units.ICompilationUnit;

/**
 * This class is the abstract base class for all ActionScript definitions in the
 * symbol table.
 * 

* After definitions are added to the symbol table, they should always be * accessed through the read-only definition interfaces (which are * IDefinition and its subinterfaces) rather than through these * implementation classes. *

* Note that this class also implements {@link IDefinitionSet} so that a * scope's map can point to a single definition acting as it own * definition-set-of-size-1. */ public abstract class DefinitionBase implements IDocumentableDefinition, IDefinitionSet { // Mask constants for bitflags in the 'flags' field, // a short value that we use in place of multiple boolean fields. protected static final short FLAG_CAST_FUNCTION = 1 << 0; protected static final short FLAG_CONSTRUCTOR = 1 << 1; protected static final short FLAG_DYNAMIC = 1 << 2; protected static final short FLAG_FINAL = 1 << 3; protected static final short FLAG_IMPLICIT = 1 << 4; protected static final short FLAG_NATIVE = 1 << 5; protected static final short FLAG_OVERRIDE = 1 << 6; protected static final short FLAG_REST = 1 << 7; protected static final short FLAG_STATIC = 1 << 8; protected static final short FLAG_DEFAULT = 1 << 9; protected static final short FLAG_CONTINGENT = 1 << 10; protected static final short FLAG_GENERATED_EMBED_CLASS = 1 << 11; protected static final short FLAG_HAS_INIT = 1 << 12; protected static final short FLAG_DEPRECATED = 1 << 13; protected static final short FLAG_DECLARED_IN_CONTROL_FLOW = 1 << 14; /** * Constructor. * * @param name The "constructor name" of the definition. * This is currently in an inconsistent form; for example, * for the flash.display.Sprite class definition * constructed from source code it is "Sprite" * but for the same definition constructed from ABC it is * "flash.display.Sprite". * The "constructor name" will be converted to a "storage name" * and stored in the storageName field. */ // TODO Fix construction of definitions so that // the storage name is always passed in. public DefinitionBase(String name) { assert name != null; this.storageName = toStorageName(name); if (Counter.COUNT_DEFINITIONS) countDefinitions(); } // The scope that contains this definition. // This field is set when the addDefinition() method of ASScope // calls the package-private setContainingScope() method of this class. private IASScope containingScope; // Bit flags for this definition, such as whether it is implicit. protected short flags; // The namespace reference for this definition. // For most definitions this will be non-null, even if no namespace was specified // in the source code. For example, if the namespace is omitted on a member of a class, // "internal" is implied; therefore this field will be a reference to the internal // namespace for the package containing the class. However, some types of definitions, // such as a for a function parameter, do have a null namespace reference. private INamespaceResolvedReference namespaceReference; // The name stored for this definition. See getStorageName() for details. private final String storageName; // The type reference for this definition. See getTypeReference() for details. // Note that this is an IReference, which is basically a reference-by-name // to a definition, which can resolve to different definitions in different projects. private IReference typeReference; // metaTags is never null. As an optimization, it re-uses a singleton empty array when // it has nothing in it private IMetaTag[] metaTags = singletonEmptyMetaTags; protected static final IMetaTag[] singletonEmptyMetaTags = new IMetaTag[0]; // Hold a reference to the node this definition came from // (NodeReference only holds onto the node weakly, so we don't have to worry about leaks). protected NodeReference nodeRef = NodeReference.noReference; private int absoluteNameStart = 0; private int absoluteNameEnd = 0; /** * Called by {@code MXMLScopeBuilder} when building definitions from * {@code MXMLData}. Creates a nodeless {@code NodeReference} for this * definition that keeps track of the file and offset for the MXML tag that * produced this definition. Later, after the MXML tree is produced, this * {@code NodeReference} can be used to locate the appropriate tree node. * * @param tag The {@code MXMLTagData} that is producing this definition. */ public void setLocation(IMXMLTagData tag) { nodeRef = new NodeReference(tag.getSource(), tag.getAbsoluteStart()); } /** * Attaches a weak reference to the source node that produced this * definition, and copies over enough source-location information to * re-associate the node if it is garbage-collected. * * @param node The {@link IDefinitionNode} that is producing * this definition. */ public void setNode(IDefinitionNode node) { if (node == null) { nodeRef = NodeReference.noReference; absoluteNameStart = UNKNOWN; absoluteNameEnd = UNKNOWN; } else { nodeRef = new NodeReference(node); IExpressionNode nameNode = node.getNameExpressionNode(); if (nameNode != null) { absoluteNameStart = nameNode.getAbsoluteStart(); absoluteNameEnd = nameNode.getAbsoluteEnd(); } else { absoluteNameStart = UNKNOWN; absoluteNameEnd = UNKNOWN; } } } /** * This method is used to set the name location when there is no name node * from which it can be determined. */ public void setNameLocation(int absoluteNameStart, int absoluteNameEnd) { this.absoluteNameStart = absoluteNameStart; this.absoluteNameEnd = absoluteNameEnd; } @Override public IASScope getContainingScope() { return containingScope; } public void setContainingScope(IASScope scope) { containingScope = scope; } @Override public IDefinition getParent() { IASScope scope = getContainingScope(); // Walk up the scope chain until we find a scope that has // a containing definition. The following types of scope do // not have an immediate containing definition: // CatchScope's // WithScope's // FunctionScope's for MXML event handler's. while ((scope != null) && (scope.getDefinition() == null)) scope = scope.getContainingScope(); return scope != null ? scope.getDefinition() : null; } @Override public IDefinition getAncestorOfType(Class ancestorType) { IDefinition definition = getParent(); while (definition != null && !(ancestorType.isInstance(definition))) definition = definition.getParent(); return definition; } // TODO Eliminate this method and its overrides // by constructing the definition with its storage name. protected String toStorageName(String name) { // Most constructors pass in either a base name or a qname, // which we convert to a base name. // However, this method is overridin in DefinitionPromise, // PackageDefinition, and AppliedVectorDefinition to NOT // reduce the constructor name to a base name. int i = name.lastIndexOf('.'); return i == -1 ? name : name.substring(i + 1); } /** * Gets the name that is actually stored for this definition. *

* This is not a public API, and is not in the {@link IDefinition} * interface, because different types of definitions store different * types of names. However, the storage name of a definition does * not depend on whether it came from source code or from ABC. *

* For class, interface, function, variable, constant, and namespace * definitions -- whose base name and the qualified name can differ * when the definition is at package scope -- the storage name is the * base name (e.g., "Sprite", * not "flash.display.Sprite"). *

* In the case of vector types, the storage name is a base name * like "Vector.<Sprite>" * (not "Vector.<flash.display.Sprite>"), * which is considered a base name despite technically having a dot. *

* For parameter definitions -- which cannot be at package scope - * and for event, style, and effect definitions -- which as metadata * don't live in any scope -- there is no distinction between the base * name and the qualified name; the storage name is the same as both * of these. E.g., i for a parameter definition or * "click" for an event definition. *

* For package definitions, the storage name is the package name * (e.g., "flash.display"), which is considered * both the base name and the qualified name of a package definition. *

* Finally, for definition promises, the storage name is a dotted, * fully-qualified name (e.g., "flash.display.Sprite", * not "Sprite"). */ protected final String getStorageName() { return storageName; } /** */ public IFileSpecification getFileSpecification() { return nodeRef.getFileSpecification(); } @Override public String getSourcePath() { IFileSpecification fileSpec = getFileSpecification(); return fileSpec != null ? fileSpec.getPath() : null; } private OffsetLookup getOffsetLookup() { final ASFileScope fileScope = getFileScope(); if (fileScope == null) return null; return fileScope.getOffsetLookup(); } @Override public int getStart() { // Because the starting offset is what is used // connect a node to a definition, // we can't just call getStart() on getNode(); // because this leads to deadlock. // Instead, we have to use the starting offset // stored in the NodeReference. if (nodeRef == null) return UNKNOWN; final int absoluteStart = nodeRef.getAbsoluteStart(); if (absoluteStart == UNKNOWN) return UNKNOWN; final OffsetLookup offsetLookup = getOffsetLookup(); if (offsetLookup == null) return absoluteStart; return offsetLookup.getLocalOffset(absoluteStart); } @Override public int getEnd() { final IDefinitionNode node = getNode(); if (node == null) return UNKNOWN; return node.getEnd(); } @Override public int getLine() { final IDefinitionNode node = getNode(); if (node == null) return UNKNOWN; return node.getLine(); } @Override public int getColumn() { final IDefinitionNode node = getNode(); if (node == null) return UNKNOWN; return node.getColumn(); } @Override public int getAbsoluteStart() { // Because the starting offset is what is used // connect a node to a definition, // we can't just call getAbsoluteStart() on getNode(); // because this leads to deadlock. // Instead, we have to use the starting offset // stored in the NodeReference. if (nodeRef == null) return UNKNOWN; return nodeRef.getAbsoluteStart(); } @Override public int getAbsoluteEnd() { final IDefinitionNode node = getNode(); if (node == null) return UNKNOWN; return node.getAbsoluteEnd(); } private IExpressionNode getNameNode() { IDefinitionNode node = getNode(); if (node == null) return null; return node.getNameExpressionNode(); } @Override public int getNameStart() { if (absoluteNameStart == UNKNOWN) return UNKNOWN; OffsetLookup offsetLookup = getOffsetLookup(); if (offsetLookup == null) return absoluteNameStart; return offsetLookup.getLocalOffset(absoluteNameStart); } @Override public int getNameEnd() { if (absoluteNameEnd == UNKNOWN) return UNKNOWN; OffsetLookup offsetLookup = getOffsetLookup(); if (offsetLookup == null) return absoluteNameEnd; return offsetLookup.getLocalOffset(absoluteNameEnd); } /** * Get line number for the name of this definition, if it can be determined. * * @return the line number, or -1 if we couldn't figure it out */ @Override public int getNameLine() { final IExpressionNode nameNode = getNameNode(); if (nameNode == null) return UNKNOWN; return nameNode.getLine(); } /** * Get column number for the name of this definition, if it can be * determined. * * @return the column number, or -1 if we couldn't figure it out */ @Override public int getNameColumn() { final IExpressionNode nameNode = getNameNode(); if (nameNode == null) return UNKNOWN; return nameNode.getColumn(); } @Override public String getContainingFilePath() { ASFileScope fileScope = getFileScope(); if (fileScope == null) return null; return fileScope.getContainingPath(); } @Override public String getContainingSourceFilePath(final ICompilerProject project) { // If node reference is set, find the source file name from // offset lookup if (nodeRef != NodeReference.noReference) { final ASFileScope fileScope = getFileScope(); if (fileScope != null && fileScope.getOffsetLookup() != null) { return fileScope.getOffsetLookup().getFilename(getAbsoluteStart()); } } ASScope containingScope = (ASScope)getContainingScope(); if (containingScope != null) return containingScope.getContainingSourcePath(getQualifiedName(), project); return null; } private static String namespaceReferenceToPackageName(INamespaceReference nsRef) { if (nsRef instanceof NamespaceDefinition.INamespaceWithPackageName) return ((NamespaceDefinition.INamespaceWithPackageName)nsRef).getNamespacePackageName(); return null; } /** * * @param definition is the definition whose containing top level definition we want * @return the top level deinition, or null if none exists. */ private static DefinitionBase getContainingToplevelDefinition(DefinitionBase definition) { ASScope currentContainingScope = definition.getContainingASScope(); DefinitionBase currentDefinition = definition; IScopedDefinition containingDefinition = currentContainingScope.getContainingDefinition(); while (containingDefinition != null) { currentDefinition = (DefinitionBase)containingDefinition; currentContainingScope = currentDefinition.getContainingASScope(); // With some synthetic definitions you can't find a containint top level definition. // This happens with Vector, for example. In this case, return null if (currentContainingScope == null) return null; containingDefinition = currentContainingScope.getContainingDefinition(); } assert currentDefinition != null; return currentDefinition; } @Override public String getPackageName() { // Ugghhh!! This method is supposed to return the fully qualified name // of the package that contains this definition. The method works even // if this definition is not a top level definition. // Sub-classes that will never have a containing scope ( like DefinitionPromise's ) // should overload this method. assert getContainingScope() != null : "This method should not be called until we have a containing scope.!"; // If we get here, we have a containing scope, so we can just walk the scope // chain until we get to a definition that is a direct child of a package or file // scope. DefinitionBase containingToplevelDefinition = getContainingToplevelDefinition(this); if (containingToplevelDefinition == null) { // If there is no containing top level definition they return the top level package. // In the case of member functions of the Vector class, this will be correct return ""; } assert (!(containingToplevelDefinition instanceof FunctionDefinition)) || (!((FunctionDefinition)containingToplevelDefinition).isConstructor()) : "Constructors should always be contained by class definitions!"; INamespaceReference toplevelDefinitionNSRef = containingToplevelDefinition.getNamespaceReference(); assert toplevelDefinitionNSRef != null; String packageNameFromNSRef = namespaceReferenceToPackageName(toplevelDefinitionNSRef); if (packageNameFromNSRef != null) return packageNameFromNSRef; assert getBaseName().indexOf('.') == -1; return ""; } @Override public String getBaseName() { // Most definitions store their base name, so this base method // simply returns it. However, a few subclasses override this. return storageName; } @Override public String getQualifiedName() { // For top-level definitions, report the package name concatenated // with the base name. If it's not in a package (e.g. a file private // definition), then the package name will be "". if (isTopLevelDefinition()) { String packageName = getPackageName(); if (packageName != null && !packageName.isEmpty()) return packageName + "." + storageName; } return storageName; } /** * Is this definition a toplevel definition * * @return true if this definition is declared at file scope, or package * scope. */ public boolean isTopLevelDefinition() { return getParent() instanceof IPackageDefinition || getParent() == null; } @Override public boolean isDynamic() { return (flags & FLAG_DYNAMIC) != 0; } public void setDynamic() { flags |= FLAG_DYNAMIC; } @Override public boolean isFinal() { return (flags & FLAG_FINAL) != 0; } public void setFinal() { flags |= FLAG_FINAL; } @Override public boolean isNative() { return (flags & FLAG_NATIVE) != 0; } public void setNative() { flags |= FLAG_NATIVE; } @Override public boolean isOverride() { return (flags & FLAG_OVERRIDE) != 0; } public void setOverride() { flags |= FLAG_OVERRIDE; } public void unsetOverride() { if (isOverride()) flags -= FLAG_OVERRIDE; } @Override public boolean isStatic() { return (flags & FLAG_STATIC) != 0; } public void setStatic() { flags |= FLAG_STATIC; } @Override public boolean hasModifier(ASModifier modifier) { // Note: These get checked in decreasing order of frequency of use. if (modifier == ASModifier.OVERRIDE) { return (flags & FLAG_OVERRIDE) != 0; } else if (modifier == ASModifier.STATIC) { return (flags & FLAG_STATIC) != 0; } else if (modifier == ASModifier.FINAL) { return (flags & FLAG_FINAL) != 0; } else if (modifier == ASModifier.DYNAMIC) { return (flags & FLAG_DYNAMIC) != 0; } else if (modifier == ASModifier.NATIVE) { return (flags & FLAG_NATIVE) != 0; } else if (modifier == ASModifier.VIRTUAL) { // Ignore "virtual" modifier. return false; } else { assert false : "Unknown modifier: " + modifier; return false; } } @Override public ModifiersSet getModifiers() { ModifiersSet result = new ModifiersSet(); if ((flags & FLAG_OVERRIDE) != 0) result.addModifier(ASModifier.OVERRIDE); if ((flags & FLAG_STATIC) != 0) result.addModifier(ASModifier.STATIC); if ((flags & FLAG_FINAL) != 0) result.addModifier(ASModifier.FINAL); if ((flags & FLAG_DYNAMIC) != 0) result.addModifier(ASModifier.DYNAMIC); if ((flags & FLAG_NATIVE) != 0) result.addModifier(ASModifier.NATIVE); return result; } public void setModifier(ASModifier modifier) { // Note: These get checked in decreasing order of frequency of use. if (modifier == ASModifier.OVERRIDE) flags |= FLAG_OVERRIDE; else if (modifier == ASModifier.STATIC) flags |= FLAG_STATIC; else if (modifier == ASModifier.FINAL) flags |= FLAG_FINAL; else if (modifier == ASModifier.DYNAMIC) flags |= FLAG_DYNAMIC; else if (modifier == ASModifier.NATIVE) flags |= FLAG_NATIVE; else assert false; } @Override public boolean isPublic() { return namespaceReference instanceof INamespaceDefinition.IPublicNamespaceDefinition; } @Override public boolean isPrivate() { return namespaceReference instanceof INamespaceDefinition.IPrivateNamespaceDefinition; } @Override public boolean isProtected() { return namespaceReference instanceof INamespaceDefinition.IProtectedNamespaceDefinition; } @Override public boolean isInternal() { return namespaceReference instanceof INamespaceDefinition.IInternalNamespaceDefinition; } @Override public boolean hasNamespace(INamespaceReference namespace, ICompilerProject project) { // If the namespaces being compared are language namespaces // (which have singleton NamespaceReferences) // then we can just compare the NamespaceReferences. if (namespace.isLanguageNamespace() && this.namespaceReference.isLanguageNamespace()) { return namespace == this.namespaceReference; } // Otherwise, the namespaces are equivalent if they resolve to // equivalent namespaceReference definitions (i.e., two with the same URI). INamespaceDefinition ns1 = namespace.resolveNamespaceReference(project); INamespaceDefinition ns2 = this.namespaceReference.resolveNamespaceReference(project); return ((ns1 != null && ns2 != null) ? ns1.equals(ns2) : false); } @Override public INamespaceReference getNamespaceReference() { return namespaceReference; } public void setNamespaceReference(INamespaceReference value) { namespaceReference = (INamespaceResolvedReference)value; NamespaceDefinition.setContainingDefinitionOfReference(value, this); } public void setPublic() { setNamespaceReference(NamespaceDefinition.getPublicNamespaceDefinition()); } /** * Utility to mark a definition as bindable. This method should only ever be * called during construction or initialization of a definition. */ public void setBindable() { MetaTag bindableMetaTag = new MetaTag(this, IMetaAttributeConstants.ATTRIBUTE_BINDABLE, new IMetaTagAttribute[0]); addMetaTag(bindableMetaTag); } @Override public INamespaceDefinition resolveNamespace(ICompilerProject project) { return namespaceReference != null ? namespaceReference.resolveNamespaceReference(project) : null; } @Override public String getTypeAsDisplayString() { return typeReference != null ? typeReference.getDisplayString() : ""; } @Override public IReference getTypeReference() { return typeReference; } /** * Sets the type reference for this definition. * * @param typeReference An [@Link IReference} to a class or interface. */ public void setTypeReference(IReference typeReference) { this.typeReference = typeReference; } /** * Gets the {@link DependencyType} that should be used when resolving the * type of this definition. *

* This method is intended to be overridden by sub-classes. * * @return The {@link DependencyType} that should be used when resolving the * type of this variable definition */ protected DependencyType getTypeDependencyType() { return DependencyType.SIGNATURE; } @Override public TypeDefinitionBase resolveType(ICompilerProject project) { DependencyType dt = getTypeDependencyType(); return resolveType(typeReference, project, dt); } @Override public boolean isImplicit() { return (flags & FLAG_IMPLICIT) != 0; } public void setImplicit() { flags |= FLAG_IMPLICIT; } @Override public IMetaTag[] getAllMetaTags() { return metaTags; } protected void addMetaTag(IMetaTag metaTag) { IMetaTag[] newMetaTags = new IMetaTag[metaTags.length + 1]; System.arraycopy(metaTags, 0, newMetaTags, 0, metaTags.length); newMetaTags[metaTags.length] = metaTag; setMetaTags(newMetaTags); } public void setMetaTags(IMetaTag[] newMetaTags) { if (newMetaTags == null) { metaTags = singletonEmptyMetaTags; } else { this.metaTags = newMetaTags; // Use a flag bit to keep track of whether there is a [Deprecated] tag. for (IMetaTag metaTag : metaTags) { String tagName = metaTag.getTagName(); if (tagName.equals(IMetaAttributeConstants.ATTRIBUTE_DEPRECATED)) setDeprecated(); } } } @Override public IMetaTag[] getMetaTagsByName(String name) { // We should never return null from this function - callers do not expect it. // So allocate an empty array here, even if metaTags is null List list = new ArrayList(); for (IMetaTag tag : metaTags) { if (tag.getTagName().equals(name)) list.add(tag); } return list.toArray(new IMetaTag[0]); } @Override public boolean hasMetaTagByName(String name) { return getMetaTagByName(name) != null; } @Override public IMetaTag getMetaTagByName(String name) { for (IMetaTag tag : metaTags) { if (tag.getTagName().equals(name)) return tag; } return null; } @Override public IASDocComment getExplicitSourceComment() { IDefinitionNode node = getNode(); if (node instanceof IDocumentableDefinitionNode) return ((IDocumentableDefinitionNode)node).getASDocComment(); return null; } @Override public boolean hasExplicitComment() { IDefinitionNode node = getNode(); if (node instanceof IDocumentableDefinitionNode) return ((IDocumentableDefinitionNode)node).hasExplicitComment(); return false; } public ASFileScope getFileScope() { ASScope s = getContainingASScope(); if (s == null) return null; return s.getFileScope(); } @Override public IDefinitionNode getNode() { // If this definition didn't come from source, return null. if (nodeRef == NodeReference.noReference) return null; ASScope containingScope = getContainingASScope(); if (containingScope == null) return null; ASFileScope fileScope = containingScope.getFileScope(); if (fileScope == null) return null; IASNode node = nodeRef.getNode(fileScope.getWorkspace(), getContainingASScope()); if (!(node instanceof IDefinitionNode)) return null; return (IDefinitionNode)node; } /** * Static version of {@code resolveType()} to help sub-classes implement * their overrides of {@link #resolveType(String, ICompilerProject, DependencyType)}. * * @param context {@link DefinitionBase} relative to which the resolve should * be done. */ protected static TypeDefinitionBase resolveType(DefinitionBase context, String typeName, ICompilerProject project, DependencyType dt) { // TODO - should probably return the Definition for "*" once we have an easy way to get that // TODO - definition - this works for now to prevent NPEs if (typeName == null) return null; CompilerProject compilerProject = (CompilerProject)project; ASScope containingScope = (ASScope)(context.containingScope); // TODO at some point this method should take some sort of name object. if (containingScope != null) { int lastIndexOfDot = typeName.lastIndexOf('.'); IDefinition foundDefinition = null; if (lastIndexOfDot != -1) { String unqualifiedName = typeName.substring(lastIndexOfDot + 1); String packageName = typeName.substring(0, lastIndexOfDot); INamespaceDefinition packageNS = ((CompilerProject)project).getWorkspace().getPackageNamespaceDefinitionCache().get(packageName, false); foundDefinition = containingScope.findPropertyQualified(compilerProject, packageNS, unqualifiedName, dt, true); } else { foundDefinition = containingScope.findProperty(compilerProject, typeName, dt, true); } assert (foundDefinition == null) || foundDefinition.isInProject(project); if (foundDefinition instanceof TypeDefinitionBase) return (TypeDefinitionBase)foundDefinition; } return null; } protected static TypeDefinitionBase resolveType(DefinitionBase context, IReference typeRef, ICompilerProject project, DependencyType dt) { // No type means '*' if (typeRef == null) return (TypeDefinitionBase)project.getBuiltinType(IASLanguageConstants.BuiltinType.ANY_TYPE); IDefinition foundDefinition = typeRef.resolve(project, (ASScope)context.containingScope, dt, true); assert (foundDefinition == null) || foundDefinition.isInProject(project); return foundDefinition instanceof TypeDefinitionBase ? (TypeDefinitionBase)foundDefinition : null; } /** * This helper method resolves type references in definitions (i.e., Strings * such as "int", "Sprite", or "flash.events.IEventDispatcher" that are * stored as references to a class or interface) to the definition of the * specified type. *

* It is called by *

    *
  • resolveType() in DefinitionBase to resolve a type * annotation such as the "int" on a declaration like * public var foo:int;
  • *
  • resolveReturnType() in FunctionDefinition to resolve the * return type of a function declaration;
  • *
  • resolveBaseClass() in ClassDefinition to resolve * extends clause of a class declaration;
  • *
  • resolveImplementedInterfaces() in ClassDefinition to * resolve the implements clause of a class declaration;
  • *
  • resolveTypeParameters() in ParameterizedClassDefinition * to resolve the type of a Vector;
  • *
  • resolveExtendedInterfaces() in InterfaceDefinition to * resolve the extends clause of an interface declaration.
  • *
* * @param typeName The name of the type to resolve. * @param project The compiler project. * @param dt The type of dependency to be created. * @return The definition of the resolved type. */ protected TypeDefinitionBase resolveType(String typeName, ICompilerProject project, DependencyType dt) { return resolveType(this, typeName, project, dt); } public TypeDefinitionBase resolveType(IReference typeRef, ICompilerProject project, DependencyType dt) { return resolveType(this, typeRef, project, dt); } protected String getLocationString() { StringBuilder sb = new StringBuilder(); int start = getStart(); int end = getEnd(); if (start != -1 && end != -1) { sb.append(start); sb.append('-'); sb.append(end); } sb.append(' '); String path = getContainingFilePath(); if (path != null) sb.append(path); return sb.toString(); } /** * Generate an AET Name object for this definition, and log any Problems * encountered while constructing the Name, to the list of problems passed * in. * * @param project the Project to use when resolving the Definitions * Namespace * @return An AET Name object that represents the qualified name of this * definition. Will return null if errors are encountered while constructing * the name (such as, the Namespace of the Definition is unresolved). */ public Name getMName(ICompilerProject project) { Name name = null; Namespace qual = null; if (namespaceReference != null) { qual = namespaceReference.resolveAETNamespace(project); } else { assert false : "All definitions should have a namespaceReference qualifier."; ASScope scope = getContainingASScope(); if (scope != null) qual = ((NamespaceDefinition)NamespaceDefinition.getDefaultNamespaceDefinition(scope)).getAETNamespace(); } // if we can't figure out the namespaceReference, we can't generate a valid name if (qual != null) name = new Name(ABCConstants.CONSTANT_Qname, new Nsset(qual), getBaseName()); return name; } protected final ASScope getContainingASScope() { IASScope s = getContainingScope(); ASScope scope = s instanceof ASScope ? (ASScope)s : null; return scope; } protected String getNamespaceReferenceAsString() { INamespaceReference namespaceReference = getNamespaceReference(); return namespaceReference != null ? namespaceReference.toString() : INamespaceConstants.internal_; } /** * * @return false if and only if we can reject the match due to one or the other or both * being vectors. */ private boolean checkVectorMatch(DefinitionBase node) { IASScope thisScope = getContainingScope(); IASScope nodeScope = node.getContainingScope(); if (thisScope != null && nodeScope != null) { IScopedDefinition thisDef = thisScope.getDefinition(); IScopedDefinition nodeDef = nodeScope.getDefinition(); if (thisDef instanceof AppliedVectorDefinition && nodeDef instanceof AppliedVectorDefinition) { String thisElementQName = ((AppliedVectorDefinition)thisDef).getQualifiedName(); String nodeElementQName = ((AppliedVectorDefinition)nodeDef).getQualifiedName(); if (thisElementQName==null && nodeElementQName==null) return true; // I don't know if this can happen, but it does then they "match" if (thisElementQName == null) return false; return thisElementQName.equals(nodeElementQName); } } return true; // we could not reject this match due to vector specific issues } public boolean matches(DefinitionBase node) { if (node == null) return false; if (node.getClass() != getClass()) return false; if (node == this) return true; INamespaceReference namespace = getNamespaceReference(); if (namespace == null && node.getNamespaceReference() != null) return false; if (namespace != null && namespace.getBaseName().compareTo(node.getNamespaceReference().getBaseName()) != 0) { return false; } if (node.getBaseName().compareTo(getBaseName()) != 0) return false; if (!checkVectorMatch(node)) return false; String packageName = node.getPackageName(); String packageName2 = getPackageName(); if (packageName == null && packageName2 != null) return false; if (packageName != null && packageName2 != null && packageName.compareTo(packageName2) != 0) return false; ASScope leftScope = node.getContainingASScope(); if (leftScope != null) { leftScope = leftScope.getFileScope(); } ASScope rightScope = getContainingASScope(); if (rightScope != null) { rightScope = rightScope.getFileScope(); } if (leftScope instanceof SWCFileScope || rightScope instanceof SWCFileScope) { return true; //we can't verify path because we might be a definition from a library } else if (leftScope instanceof ASFileScope && rightScope instanceof ASFileScope) { if (((ASFileScope)leftScope).getContainingPath().compareTo(((ASFileScope)rightScope).getContainingPath()) != 0) { return false; } } else { //For normal case, just compare the full containing file path //Note that for some synthetic definitions (like Vector), this path will be null String nodePath = node.getContainingFilePath(); String thisPath = this.getContainingFilePath(); if (nodePath == null) { if (thisPath != null) return false; } else if (nodePath.compareTo(thisPath) != 0) return false; } if (node.getTypeAsDisplayString().compareTo(getTypeAsDisplayString()) != 0) return false; return true; } /** * Whether this definition was specified as "Bindable" in its metadata */ private boolean isBindableLocally() { return getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_BINDABLE) != null; } /** * Whether this definition was specified as "Bindable" in its metadata, or * inherits bindability from the containing class * * @return true if this definition is Bindable. */ @Override public boolean isBindable() { // A variable is considered bindable if it is marked bindable, // or if it is a public var in a class marked bindable boolean bindable = isBindableLocally(); if (!bindable) { IDefinition containing = getParent(); if (containing instanceof IClassDefinition && this.getNamespaceReference() == NamespaceDefinition.getPublicNamespaceDefinition()) { DefinitionBase containingBase = (DefinitionBase)containing; bindable = containingBase.isBindableLocally(); } } return bindable; } /** * @return a collection of Bindable event names, if any were specified in * the metadata, as well as any inherited from the class */ @Override public List getBindableEventNames() { // First get any from this definition // Use a Set<> temporarily, so we can do an easy union Set eventNames = new HashSet(getBindableEventNamesLocally()); // Now "or" in any from the containing class IDefinition containing = getParent(); if (containing instanceof IClassDefinition && this.getNamespaceReference() == NamespaceDefinition.getPublicNamespaceDefinition()) { DefinitionBase containingBase = (DefinitionBase)containing; eventNames.addAll(containingBase.getBindableEventNamesLocally()); } // Convert to list, as per the return type in IDefinition return new ArrayList(eventNames); } /** * @return a collection of Bindable event names, if any were specified in * the metadata */ private List getBindableEventNamesLocally() { IMetaTag[] bindableTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_BINDABLE); if (bindableTags != null) { List events = new ArrayList(); for (IMetaTag bindableTag : bindableTags) { String eventName = bindableTag.getAttributeValue(IMetaAttributeConstants.NAME_BINDABLE_EVENT); if (eventName == null && bindableTag.getAllAttributes().length == 1) { Boolean isStyle = false; eventName = bindableTag.getAllAttributes()[0].getKey(); if (eventName != null && eventName.equals("style")) isStyle = true; eventName = bindableTag.getAllAttributes()[0].getValue(); if (isStyle && eventName.equals("true")) eventName = "isStyle"; // hack: fake event name "isStyle" } if (eventName != null) events.add(eventName); } return events; } return Collections.emptyList(); } /** * @return a collection of Bindable event names, if any were specified in * the metadata */ @Override public boolean isBindableStyle() { IMetaTag[] bindableTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_BINDABLE); if (bindableTags != null) { for (IMetaTag bindableTag : bindableTags) { String style = bindableTag.getAttributeValue(IMetaAttributeConstants.NAME_BINDABLE_STYLE); if (style != null && IASLanguageConstants.TRUE.equals(style)) return true; } } return false; } @Override public boolean isContingent() { return (flags & FLAG_CONTINGENT) != 0; } /** * Marks the definition as contingent. Meaning that it's added to the symbol * table as a placeholder, but codegen will decide whether or not it needs * to actually be created. */ protected void setContingent() { flags |= FLAG_CONTINGENT; } @Override public boolean isGeneratedEmbedClass() { return (flags & FLAG_GENERATED_EMBED_CLASS) != 0; } /** * Marks a class definition as being auto-generated for an embedded asset * (i.e., one created for a var with [Embed(...)] * metadata). *

* Adding such a class to the project scope in the middle of a compilation * will not cause the validImports set to be rebuilt. */ public void setGeneratedEmbedClass() { flags |= FLAG_GENERATED_EMBED_CLASS; } @Override public boolean isContingentNeeded(ICompilerProject project) { return ((ASScopeBase)getContainingScope()).isContingentDefinitionNeeded(project, this); } protected IMetaTag getSkinPart() { return getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_SKIN_PART); } protected boolean isRequiredSkinPart(IMetaTag skinPart) { // if the required value is not set, it's not required, as skin parts are // optional by default. String isRequired = skinPart.getAttributeValue(IMetaAttributeConstants.NAME_SKIN_PART_REQUIRED); if (isRequired == null) return false; if (!isRequired.equals(IMetaAttributeConstants.VALUE_SKIN_PART_REQUIRED_TRUE)) return false; return true; } /** * Returns the type specified by [ArrayElementType("...")] * metadata on a variable/getter/setter of type Array.

This method is * in DefinitionBase because it is common to VariableDefinition, * GetterDefinition, and SetterDefinition. */ public String getArrayElementType(ICompilerProject project) { if (getTypeAsDisplayString().equals(IASLanguageConstants.Array)) { return getPropertyMetaTagValue( (RoyaleProject)project, IMetaAttributeConstants.ATTRIBUTE_ARRAYELEMENTTYPE); } return null; } /** * Returns the type specified by [InstanceType("...")] metadata * on a variable/getter/setter of type mx.core.IDeferredInstance. *

* This method is in DefinitionBase because it is common to * VariableDefinition, GetterDefinition, and SetterDefinition. */ public String getInstanceType(ICompilerProject project) { if (getTypeAsDisplayString().equals(((RoyaleProject)project).getDeferredInstanceInterface())) { return getPropertyMetaTagValue( (RoyaleProject)project, IMetaAttributeConstants.ATTRIBUTE_INSTANCETYPE); } return null; } /** * Returns the property name specified by [PercentProxy("...")] * metadata on a variable/getter/setter.

This method is in * DefinitionBase because it is common to VariableDefinition, * GetterDefinition, and SetterDefinition. */ public String getPercentProxy(ICompilerProject project) { IMetaTag metaTag = getPropertyMetaTag( (RoyaleProject)project, IMetaAttributeConstants.ATTRIBUTE_PERCENT_PROXY); return metaTag != null ? metaTag.getValue() : null; } /** * Returns true if there is [RichTextContent] * metadata on a variable/getter/setter. *

* This method is in DefinitionBase because it is common to * VariableDefinition, GetterDefinition, and SetterDefinition. */ public boolean hasRichTextContent(ICompilerProject project) { IMetaTag metaTag = getPropertyMetaTag( (RoyaleProject)project, IMetaAttributeConstants.ATTRIBUTE_RICHTEXTCONTENT); return metaTag != null; } /** * Returns true if there is [CollapseWhiteSpace] * metadata on a variable/getter/setter. *

* This method is in DefinitionBase because it is common to * VariableDefinition, GetterDefinition, and SetterDefinition. */ public boolean hasCollapseWhiteSpace(ICompilerProject project) { IMetaTag metaTag = getPropertyMetaTag( (RoyaleProject)project, IMetaAttributeConstants.ATTRIBUTE_COLLAPSEWHITESPACE); return metaTag != null; } /** * Returns true if there is [Inspectable(...)] * metadata on a vairable/getter/setter that specifies * format="Color". *

* This method is in DefinitionBase because it is common to * VariableDefinition, GetterDefinition, and SetterDefinition. */ public boolean isColor(ICompilerProject project) { IMetaTag metaTag = getPropertyMetaTag( (RoyaleProject)project, IMetaAttributeConstants.ATTRIBUTE_INSPECTABLE); if (metaTag == null) return false; String format = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_INSPECTABLE_FORMAT); return format.equals(IMetaAttributeConstants.VALUE_INSPECTABLE_FORMAT_COLOR); } private IMetaTag getPropertyMetaTag(RoyaleProject project, String metadataName) { // Look for metadata with the specified name. IMetaTag metaTag = getMetaTagByName(metadataName); // If we don't find it, and we're a getter or setter, // look on the corresponding setter or getter. if (metaTag == null && this instanceof IAccessorDefinition) { IAccessorDefinition correspondingAccessor = ((IAccessorDefinition)this).resolveCorrespondingAccessor(project); if (correspondingAccessor != null) metaTag = correspondingAccessor.getMetaTagByName(metadataName); } return metaTag; } /** * This helper method is called by {@code getArrayElementType()} and * {@code getInstanceType()}. */ private String getPropertyMetaTagValue(RoyaleProject project, String metadataName) { // Look for metadata with the specified name. IMetaTag metaTag = getPropertyMetaTag(project, metadataName); // If we found the metadata, return the type that it specifies. // There should be one keyless attribute, and we want its value. return metaTag != null ? metaTag.getValue() : null; } /** * Determines if this definition is in a package namespaceReference. This is here, so * that the various sub-classes can call it from get*Classification() * methods. * * @return true, if the namespaceReference for this definition is some kind of * package namespaceReference. */ protected boolean inPackageNamespace() { INamespaceReference nsRef = getNamespaceReference(); assert nsRef != null : "All definitions should have a namespaceReference reference!"; if (nsRef.isPublicOrInternalNamespace()) return true; return false; } @Override public boolean isDeprecated() { return (flags & FLAG_DEPRECATED) != 0; } /** * Sets a bitflag to indicate that this definition has [Deprecated] metadata. * Checking this flag is faster than walking through the metadata multiple times. */ private void setDeprecated() { flags |= FLAG_DEPRECATED; } @Override public IDeprecationInfo getDeprecationInfo() { for (IMetaTag metaTag : metaTags) { if (metaTag.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_DEPRECATED)) { String replacement = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_DEPRECATED_REPLACEMENT); String since = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_DEPRECATED_SINCE); String message = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_DEPRECATED_MESSAGE); return new DeprecationInfo(replacement, since, message); } } return null; } /** * Used only in asserts. */ public boolean verify() { // Verify the name. assert getBaseName() != null : "Definition has null name"; // TODO: Verify the source location eventually. // assert getSourcePath() != null : "Definition has null source path"; // assert getStart() != UNKNOWN : "Definition has unknown start"; // assert getEnd() != UNKNOWN : "Definition has unknown end"; // assert getLine() != UNKNOWN : "Definition has unknown line"; // assert getColumn() != UNKNOWN : "Definition has unknown column"; return true; } /** * Used only for debugging. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); buildString(sb, false); return sb.toString(); } /** * Used only for debugging, as part of {@link #toString()}. */ public void buildString(StringBuilder sb, boolean withLocation) { buildInnerString(sb); if (withLocation) { sb.append(' '); sb.append(getLocationString()); } } /** * Used only for debugging, as part of {@link #toString()}. */ protected void buildInnerString(StringBuilder sb) { } /** * Counts various types of definitions that are created, * as well as the total number of definitions. */ private void countDefinitions() { Counter counter = Counter.getInstance(); counter.incrementCount(getClass().getSimpleName()); counter.incrementCount("definitions"); } // // IDefinitionSet methods // @Override public boolean isEmpty() { return false; } @Override public int getSize() { return 1; } @Override public int getMaxSize() { return 1; } @Override public IDefinition getDefinition(int i) { assert i == 0; return this; } @Override public boolean isInProject(ICompilerProject project) { final ICompilationUnit compilationUnit = ((ASProjectScope)project.getScope()).getCompilationUnitForDefinition(this); if (compilationUnit != null) { if (compilationUnit.getProject() == project) return true; else return false; } if (AppliedVectorDefinition.isVectorScope(getContainingASScope())) { IDefinition parentDef = getParent(); return parentDef.isInProject(project); } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy