| /* |
| * |
| * 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.config.CompilerDiagnosticsConstants; |
| 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. |
| * <p> |
| * After definitions are added to the symbol table, they should always be |
| * accessed through the read-only definition interfaces (which are |
| * <code>IDefinition</code> and its subinterfaces) rather than through these |
| * implementation classes. |
| * <p> |
| * 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; |
| |
| private static boolean performanceCachingEnabled = false; |
| |
| public static boolean getPerformanceCachingEnabled() |
| { |
| return performanceCachingEnabled; |
| } |
| |
| public static void setPerformanceCachingEnabled(boolean enable) |
| { |
| performanceCachingEnabled = enable; |
| } |
| |
| /** |
| * Constructor. |
| * |
| * @param name The "constructor name" of the definition. |
| * This is currently in an inconsistent form; for example, |
| * for the <code>flash.display.Sprite</code> class definition |
| * constructed from source code it is <code>"Sprite"</code> |
| * but for the same definition constructed from ABC it is |
| * <code>"flash.display.Sprite"</code>. |
| * The "constructor name" will be converted to a "storage name" |
| * and stored in the <code>storageName</code> 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; |
| |
| private IDefinition parentDef = null; |
| |
| /** |
| * 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() |
| { |
| if (getPerformanceCachingEnabled() && parentDef != null) |
| return parentDef; |
| |
| 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(); |
| |
| IDefinition result = scope != null ? scope.getDefinition() : null; |
| if (getPerformanceCachingEnabled()) |
| parentDef = result; |
| return result; |
| } |
| |
| @Override |
| public IDefinition getAncestorOfType(Class<? extends IDefinition> 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. |
| * <p> |
| * 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. |
| * <p> |
| * 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., <code>"Sprite"</code>, |
| * not <code>"flash.display.Sprite"</code>). |
| * <p> |
| * In the case of vector types, the storage name is a base name |
| * like <code>"Vector.<Sprite>"</code> |
| * (not <code>"Vector.<flash.display.Sprite>"</code>), |
| * which is considered a base name despite technically having a dot. |
| * <p> |
| * 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., <code>i</code> for a parameter definition or |
| * <code>"click"</code> for an event definition. |
| * <p> |
| * For package definitions, the storage name is the package name |
| * (e.g., <code>"flash.display"</code>), which is considered |
| * both the base name and the qualified name of a package definition. |
| * <p> |
| * Finally, for definition promises, the storage name is a dotted, |
| * fully-qualified name (e.g., <code>"flash.display.Sprite"</code>, |
| * not <code>"Sprite"</code>). |
| */ |
| 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(); |
| if (currentContainingScope == null) |
| { |
| // With some synthetic definitions you can't find a containint top level definition. |
| // This happens with Vector<T>, for example. In this case, return null |
| return null; |
| } |
| DefinitionBase currentDefinition = definition; |
| |
| IScopedDefinition containingDefinition = currentContainingScope.getContainingDefinition(); |
| while (containingDefinition != null) |
| { |
| currentDefinition = (DefinitionBase)containingDefinition; |
| currentContainingScope = currentDefinition.getContainingASScope(); |
| |
| if (currentContainingScope == null) |
| { |
| // See comment above |
| 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<T> 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; |
| } |
| |
| // instead of increasing the size of "flags" from a short to int, I added |
| // a boolean variable instead. feel free to change this, if desired. -JT |
| private boolean abstractFlag = false; |
| |
| @Override |
| public boolean isAbstract() |
| { |
| return abstractFlag; |
| } |
| |
| public void setAbstract() |
| { |
| abstractFlag = true; |
| } |
| |
| @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 if (modifier == ASModifier.ABSTRACT) |
| { |
| return abstractFlag; |
| } |
| 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); |
| if (abstractFlag) |
| result.addModifier(ASModifier.ABSTRACT); |
| 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 if (modifier == ASModifier.ABSTRACT) |
| setAbstract(); |
| |
| 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 |
| || namespaceReference instanceof INamespaceDefinition.IStaticProtectedNamespaceDefinition; |
| } |
| |
| @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. |
| * <p> |
| * 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<IMetaTag> list = new ArrayList<IMetaTag>(); |
| |
| 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. |
| * <p> |
| * It is called by |
| * <ul> |
| * <li><code>resolveType()</code> in DefinitionBase to resolve a type |
| * annotation such as the "int" on a declaration like |
| * <code>public var foo:int</code>;</li> |
| * <li><code>resolveReturnType()</code> in FunctionDefinition to resolve the |
| * return type of a function declaration;</li> |
| * <li><code>resolveBaseClass()</code> in ClassDefinition to resolve |
| * <code>extends</code> clause of a class declaration;</li> |
| * <li><code>resolveImplementedInterfaces()</code> in ClassDefinition to |
| * resolve the <code>implements</code> clause of a class declaration;</li> |
| * <li><code>resolveTypeParameters()</code> in ParameterizedClassDefinition |
| * to resolve the type of a Vector;</li> |
| * <li><code>resolveExtendedInterfaces()</code> in InterfaceDefinition to |
| * resolve the <code>extends</code> clause of an interface declaration.</li> |
| * </ul> |
| * |
| * @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) |
| { |
| bindable = isClassBindable(); |
| } |
| return bindable; |
| } |
| |
| public boolean isClassBindable() { |
| boolean classBindable = false; |
| IDefinition containing = getParent(); |
| if (containing instanceof IClassDefinition |
| && this.getNamespaceReference() == NamespaceDefinition.getPublicNamespaceDefinition()) |
| { |
| DefinitionBase containingBase = (DefinitionBase)containing; |
| classBindable = containingBase.isBindableLocally(); |
| } |
| return classBindable; |
| } |
| |
| /** |
| * @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<String> getBindableEventNames() |
| { |
| // First get any from this definition |
| // Use a Set<> temporarily, so we can do an easy union |
| Set<String> eventNames = new HashSet<String>(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<String>(eventNames); |
| } |
| |
| /** |
| * @return a collection of Bindable event names, if any were specified in |
| * the metadata |
| */ |
| private List<String> getBindableEventNamesLocally() |
| { |
| IMetaTag[] bindableTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_BINDABLE); |
| |
| if (bindableTags != null) |
| { |
| List<String> events = new ArrayList<String>(); |
| 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 <code>var</code> with <code>[Embed(...)]</code> |
| * metadata). |
| * <p> |
| * Adding such a class to the project scope in the middle of a compilation |
| * will not cause the <code>validImports</code> 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 <code>[ArrayElementType("...")]</code> |
| * metadata on a variable/getter/setter of type Array. </p> This method is |
| * in DefinitionBase because it is common to VariableDefinition, |
| * GetterDefinition, and SetterDefinition. |
| */ |
| public String getArrayElementType(ICompilerProject project) |
| { |
| if (getTypeAsDisplayString().equals(IASLanguageConstants.Array) |
| || IASLanguageConstants.Array.equals(getInstanceType(project))) |
| { |
| return getPropertyMetaTagValue( |
| (RoyaleProject)project, IMetaAttributeConstants.ATTRIBUTE_ARRAYELEMENTTYPE); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the type specified by <code>[InstanceType("...")]</code> metadata |
| * on a variable/getter/setter of type mx.core.IDeferredInstance. |
| * <p> |
| * 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 <code>[PercentProxy("...")]</code> |
| * metadata on a variable/getter/setter. </p> 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 <code>true</code> if there is <code>[RichTextContent]</code> |
| * metadata on a variable/getter/setter. |
| * <p> |
| * 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 <code>true</code> if there is <code>[CollapseWhiteSpace]</code> |
| * metadata on a variable/getter/setter. |
| * <p> |
| * 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 <code>true</code> if there is <code>[Inspectable(...)]</code> |
| * metadata on a vairable/getter/setter that specifies |
| * <code>format="Color"</code>. |
| * <p> |
| * 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() |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COUNTER) == CompilerDiagnosticsConstants.COUNTER) |
| System.out.println("DefinitionBase incrementing Counter for " + getClass().getSimpleName()); |
| Counter counter = Counter.getInstance(); |
| counter.incrementCount(getClass().getSimpleName()); |
| counter.incrementCount("definitions"); |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COUNTER) == CompilerDiagnosticsConstants.COUNTER) |
| System.out.println("DefinitionBase done incrementing Counter for " + getClass().getSimpleName()); |
| } |
| |
| // |
| // 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; |
| } |
| } |