/* * * 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.flex.compiler.internal.tree.as; import static org.apache.flex.abc.ABCConstants.CONSTANT_Multiname; import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameA; import static org.apache.flex.abc.ABCConstants.CONSTANT_Qname; import static org.apache.flex.abc.ABCConstants.CONSTANT_QnameA; import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQname; import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQnameA; import java.util.Collection; import java.util.EnumSet; import java.util.Set; import antlr.Token; import com.google.common.collect.ImmutableSet; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.semantics.Nsset; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.config.Configuration; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.INamespaceDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.IQualifiers; import org.apache.flex.compiler.definitions.IVariableDefinition.VariableClassification; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.definitions.references.IReference; import org.apache.flex.compiler.definitions.references.ReferenceFactory; import org.apache.flex.compiler.internal.definitions.AmbiguousDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinitionBase; import org.apache.flex.compiler.internal.definitions.DefinitionBase; import org.apache.flex.compiler.internal.definitions.FunctionDefinition; import org.apache.flex.compiler.internal.definitions.InterfaceDefinition; import org.apache.flex.compiler.internal.definitions.NamespaceDefinition; import org.apache.flex.compiler.internal.definitions.VariableDefinition; import org.apache.flex.compiler.internal.projects.FlexProject; import org.apache.flex.compiler.internal.scopes.ASScope; import org.apache.flex.compiler.internal.semantics.PostProcessStep; import org.apache.flex.compiler.internal.semantics.SemanticUtils; import org.apache.flex.compiler.internal.tree.as.metadata.DefaultPropertyTagNode; import org.apache.flex.compiler.internal.tree.as.metadata.EffectTagNode; import org.apache.flex.compiler.internal.tree.as.metadata.EventTagNode; import org.apache.flex.compiler.internal.tree.as.metadata.MetaTagsNode; import org.apache.flex.compiler.internal.tree.as.metadata.StyleTagNode; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.parsing.IASToken; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.tree.ASTNodeID; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IDefinitionNode; import org.apache.flex.compiler.tree.as.IFileNode; import org.apache.flex.compiler.tree.as.IIdentifierNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode; import org.apache.flex.compiler.tree.as.IScopedDefinitionNode; import org.apache.flex.compiler.tree.as.IScopedNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind; import org.apache.flex.compiler.tree.metadata.IMetaTagNode; import org.apache.flex.compiler.tree.mxml.IMXMLMetadataNode; import org.apache.flex.compiler.workspaces.IWorkspace; /** * ActionScript parse tree node representing an identifier */ public class IdentifierNode extends ExpressionNodeBase implements IIdentifierNode { /** * Create a dummy {@code IdentifierNode} after the given token. This is used * to repair the AST where an identifier node is expected. * * @param token The empty ID node will have start offset following this * token. * @return Dummy identifier node. */ public static IdentifierNode createEmptyIdentifierNodeAfterToken(final Token token) { final IdentifierNode result = new IdentifierNode(""); result.startAfter(token); result.endAfter(token); return result; } /** * Determine if the definition passed in is one of the XML types (XML or * XMLList) These classes are unrelated, but behave in similar manners. * * @param def the {@link IDefinition} to check * @param project the {@link ICompilerProject} in which to look up types * @return true if definition is the built-in XML or XMLList type. */ public static boolean isXMLish(IDefinition def, ICompilerProject project) { IDefinition xmlDef = project.getBuiltinType(IASLanguageConstants.BuiltinType.XML); IDefinition xmlListDef = project.getBuiltinType(IASLanguageConstants.BuiltinType.XMLLIST); return (xmlDef != null && def == xmlDef) || (xmlListDef != null && def == xmlListDef); } /** * Constructor. */ public IdentifierNode(String name) { this.name = name; } /** * Constructor. */ public IdentifierNode(String name, Token token) { this.name = name; span(token); } /** * Constructor. */ public IdentifierNode(String name, IASToken token) { this.name = name; span(token); } /** * Constructor. */ public IdentifierNode(IASToken token) { name = token.getText(); span(token); } /** * Copy constructor. * * @param other The node to copy. */ protected IdentifierNode(IdentifierNode other) { super(other); this.name = other.name; } /** * The name of this identifier. */ private String name; // // NodeBase overrides // @Override public ASTNodeID getNodeID() { return ASTNodeID.IdentifierID; } @Override public boolean isTerminal() { return true; } @Override protected void analyze(EnumSet<PostProcessStep> set, ASScope scope, Collection<ICompilerProblem> problems) { if (IASLanguageConstants.arguments.equals(name)) { if (!this.isMemberRef() && !this.isQualifiedRef() && set.contains(PostProcessStep.POPULATE_SCOPE)) { FunctionNode fn = (FunctionNode)getAncestorOfType(FunctionNode.class); if (fn != null) fn.needsArguments = true; } } } @Override public void normalize(boolean fillInOffsets) { // do nothing } @Override protected void fillInOffsets() { //we gain our offsets externally, no reason to calculate this } @Override public IScopedNode getScopeNode() { IASNode parent = getParent(); if (parent instanceof IterationFlowNode) { // we're a label for a break statement return null; } // If our parent is a DefaultPropertTag [DefaultProperty("property")] // then it is referencing a property that is not in the same scope; // so find the MemberedNode it is above and use that scope instead. if (parent instanceof DefaultPropertyTagNode) { // Get containing block. IASNode node = parent.getAncestorOfType(BlockNode.class); if (node instanceof BlockNode) { int childCount = node.getChildCount(); boolean descend = false; for (int i = 0; i < childCount; i++) { IASNode child = node.getChild(i); if (!descend && child instanceof MetaTagsNode) { IMetaTagNode[] allTags = ((MetaTagsNode)child).getAllTags(); for (int t = 0; t < allTags.length; t++) { if (allTags[t] == parent) { descend = true; break; } } } if (descend && child instanceof MemberedNode) { return ((MemberedNode)child).getScopedNode().getScopeNode(); } } } } else if (parent instanceof EventTagNode || parent instanceof EffectTagNode || parent instanceof StyleTagNode) { IASNode parentNode = parent.getParent(); if (parentNode instanceof MetaTagsNode) { IASNode decoratedNode = ((MetaTagsNode)parentNode).getDecoratedDefinition(); if (decoratedNode instanceof MemberedNode) return ((MemberedNode)decoratedNode).getScopedNode().getScopeNode(); } else if (parentNode instanceof IMXMLMetadataNode) { return ((IScopedDefinitionNode)((IMXMLMetadataNode)parentNode).getParent()).getScopedNode(); } // for these case, we don't need to scan locals so skip up to the membered node here } else if ((parent instanceof ParameterNode && ((ParameterNode)parent).getTypeNode() != null && ((ParameterNode)parent).getTypeNode().equals(this)) || (parent instanceof VariableNode && ((VariableNode)parent).getTypeNode() != null && ((VariableNode)parent).getTypeNode().equals(this))) { while (!(parent instanceof MemberedNode) && !(parent instanceof IFileNode)) { parent = parent.getParent(); } return ((NodeBase)parent).getScopeNode(); } else if (parent instanceof ParameterNode && parent.getParent() instanceof CatchNode) { return ((CatchNode)parent.getParent()); } else if (parent instanceof ClassNode && ((ClassNode)parent).getBaseClassNode() != null && ((ClassNode)parent).getBaseClassNode().equals(this)) { while (!(parent instanceof PackageNode) && !(parent instanceof IFileNode)) { parent = parent.getParent(); } return ((NodeBase)parent).getScopeNode(); } return super.getScopeNode(); } /* * For debugging only. Builds a string such as <code>"i"</code> from the * name of the identifier. */ @Override protected boolean buildInnerString(StringBuilder sb) { sb.append('"'); sb.append(getName()); sb.append('"'); return true; } // // ExpressionNodeBase overrides // @Override public IDefinition resolve(ICompilerProject project) { ASScope asScope = getASScope(); if (asScope == null) return null; // attributes are not statically knowable if (this.isAttributeIdentifier()) return null; // If this is a reference to a known package, then we can't resolve to anything if (isPartOfPackageReference()) return null; IDefinition result = null; final String name = getName(); IQualifiers qualifier = null; if (isQualifiedRef()) { qualifier = resolveQualifier(project); // If we can't resolve the qualifier, then we can't resolve the entire expression if (qualifier == null) return null; } boolean isMemberRef = isMemberRef(); boolean wasMemberRef = isMemberRef; if (isMemberRef && baseIsPackage()) { // If our base refers to a package, then we're really a qualified name // and not a regular member acces (a.b.C means find C in package a.b, if a.b is a package) // convert the name to a fully qualified name, and look that up via findProperty ExpressionNodeBase base = getBaseExpression(); String packageName = base.computeSimpleReference(); packageName = project.getActualPackageName(packageName); Workspace workspace = (Workspace)project.getWorkspace(); qualifier = workspace.getPackageNamespaceDefinitionCache().get(packageName, false); // Set this to false, so we'll fall through to the findProperty case // below, instead of trying to call resolveMemberRef isMemberRef = false; } if (isNameNode()) { // If we are the name node for a declaration, just grab the definition // don't have to look anywhere else IDefinitionNode defNode = getParentAsDefinition(); if (defNode != null) result = defNode.getDefinition(); } else if (isMemberRef) { result = resolveMemberRef(project, asScope, name, qualifier); } else { if (qualifier == null) { result = asScope.findProperty(project, name, getDependencyType(), isTypeRef()); // ASVariableTests_localVarSameNameAsPrivateMethod if (isLegacyCodegen(project) && result != null && getParent().getNodeID() == ASTNodeID.FunctionCallID && result instanceof VariableDefinition) { VariableDefinition varDef = (VariableDefinition)result; if (varDef.getVariableClassification() == VariableClassification.LOCAL) { ClassDefinitionBase cdef = asScope.getContainingClass(); IDefinition memberResult = asScope.getPropertyFromDef(project, cdef, name, false); if (memberResult instanceof FunctionDefinition) result = memberResult; } } } else { result = asScope.findPropertyQualified(project, qualifier, name, getDependencyType(), isTypeRef()); if (result == null && wasMemberRef && baseIsPackage()) { // if we get here it was because there is a memberaccessexpression like "a.b.foo" // that did not resolve because a.b is a package but foo isn't a class. There is a chance that // "a" by itself is a package and there is a class "b" with a member called "foo" so // look for that result = resolveMemberRef(project, asScope, name, null); } } } return result; } @Override public ITypeDefinition resolveType(ICompilerProject project) { IDefinition def = resolve(project); if (def != null) { if (isXMLish(def.getParent(), project)) { // XML and XMLList members should be treated as '*' because any access could // resolve to some content inside the XML (i.e. it has a child tag named 'name'). // return '*' since we can't statically know what the type will be. // Compat with ASC behavior. return project.getBuiltinType(IASLanguageConstants.BuiltinType.ANY_TYPE); } return def.resolveType(project); } return null; } @Override protected IdentifierNode copy() { return new IdentifierNode(this); } @Override public boolean isDynamicExpression(ICompilerProject project) { // If this is a package reference, we're not dynamic. if (isPackageReference()) return false; return super.isDynamicExpression(project); } @Override public Name getMName(ICompilerProject project) { // If we can resolve the reference, then just use the QName // of whatever we resolved to IDefinition def = resolve(project); if (canEarlyBind(project, def)) return ((DefinitionBase)def).getMName(project); if (def == null) { // no def? see if this is dynamic // or a missing member on a known and non-dynamic def if (getParent().getNodeID() == ASTNodeID.MemberAccessExpressionID) { // ok it is a member expression, are we on the right? MemberAccessExpressionNode mae = (MemberAccessExpressionNode) getParent(); if (mae.getRightOperandNode() == this) { // we're on the right, what is the left? ITypeDefinition leftDef = mae.getLeftOperandNode().resolveType(project); if (leftDef != null && leftDef.isDynamic() == false) { INamespaceReference nr = leftDef.getNamespaceReference(); INamespaceDefinition nd = nr.resolveNamespaceReference(project); Set<INamespaceDefinition> nsset = ImmutableSet.of((INamespaceDefinition)nd); return makeName(nsset, getName(), isAttributeIdentifier()); } } } } ASScope scope = getASScope(); if (isQualifiedRef()) { // If we're a qualified name, and the qualifier resolves to a compile // time constant, return a QName // otherwise emit a RTQname - the CG will have to take care of generating the code to // evaluate the qualifier and place it on the stack. IQualifiers qual = resolveQualifier(project); Nsset namespaceSet; int nameKind; if (qual != null ) { if( qual.getNamespaceCount() == 1 ) { // Qualifier resolved to 1 namespace, so we can emit a QName NamespaceDefinition ns = (NamespaceDefinition)qual.getFirst(); nameKind = isAttributeIdentifier() ? CONSTANT_QnameA : CONSTANT_Qname; if (isMemberRef()) { ExpressionNodeBase baseExpr = getBaseExpression(); if (baseExpr instanceof LanguageIdentifierNode && ((LanguageIdentifierNode)baseExpr).getKind() == LanguageIdentifierKind.SUPER) { // If we're a super expression, adjust the namespace in case it's the protected namespace IDefinition baseType = baseExpr.resolveType(project); Set<INamespaceDefinition> nsset = ImmutableSet.of((INamespaceDefinition)ns); nsset = scope.adjustNamespaceSetForSuper(baseType, nsset); // We only started with 1 namespace, so we know that's how many we have ns = (NamespaceDefinition)nsset.iterator().next(); } } // If the qualifier is the any namespace, then we want a null nsset // instead of a nsset of length 1, with a null namespace in it. if( ns == NamespaceDefinition.getAnyNamespaceReference() ) namespaceSet = null; else namespaceSet = new Nsset(ns.getAETNamespace()); } else { // qualifier resolve to 1+ namespaces, so emit a multiname Set<INamespaceDefinition> nsset = qual.getNamespaceSet(); nameKind = isAttributeIdentifier() ? CONSTANT_MultinameA : CONSTANT_Multiname; namespaceSet = SemanticUtils.convertSetINamespaceToNsset(nsset); } } else { namespaceSet = null; nameKind = isAttributeIdentifier() ? CONSTANT_RTQnameA : CONSTANT_RTQname; } return new Name(nameKind, namespaceSet, getName()); } Name name = null; if (isMemberRef()) { ExpressionNodeBase baseExpr = getBaseExpression(); if (baseExpr != null) { // Handle the case where we look like a member expression, but the base expression is really // a reference to a package. For example 'a.b.c.Foo' where a.b.c is a known package name. // This needs to generate a QName with a.b.c as the qualifier. if (baseIsPackage()) { String packageName = baseExpr.computeSimpleReference(); Workspace workspace = (Workspace)project.getWorkspace(); INamespaceReference qualifier = workspace.getPackageNamespaceDefinitionCache().get(packageName, false); return new Name(isAttributeIdentifier() ? CONSTANT_QnameA : CONSTANT_Qname, new Nsset(qualifier.resolveAETNamespace(project)), getName()); } Set<INamespaceDefinition> namespaceSet = null; IDefinition baseType = baseExpr.resolveType(project); // If our base type is an interface, then we need to use the special // interface namespace set (the namespace of the interface, plus the namespaces for all the interfaces // it extends) if (baseType instanceof InterfaceDefinition) { namespaceSet = ((InterfaceDefinition)baseType).getInterfaceNamespaceSet(project); } else if (baseExpr instanceof LanguageIdentifierNode && ((LanguageIdentifierNode)baseExpr).getKind() == LanguageIdentifierKind.SUPER) { namespaceSet = scope.getNamespaceSetForSuper(project, baseType); } if (namespaceSet != null) return makeName(namespaceSet, getName(), isAttributeIdentifier()); } } if (isNameNode()) { BaseDefinitionNode defNode = getParentAsDefinition(); name = defNode.getDefinition().getMName(project); } else if (scope != null) { Set<INamespaceDefinition> namespaceSet = null; if (isMemberRef()) { // Member refs just use the open namespace set namespaceSet = scope.getNamespaceSet(project); } else { // lexical refs may be influenced by the imports namespaceSet = scope.getNamespaceSetForName(project, getName()); } name = makeName(namespaceSet, getName(), isAttributeIdentifier()); } return name; } @Override String computeSimpleReference() { return getName(); } @Override IReference computeTypeReference() { // Parser creates IdentifierNodes for "" for some error cases of improperly written code, // like class C extends {} - it gets an IDNode of "" for the extends clause if (name == "") return null; IReference typeRef = null; IWorkspace w = getWorkspace(); switch (getRefType()) { case PACKAGE_QUALIFIED: { ExpressionNodeBase baseExpr = getBaseExpression(); typeRef = ReferenceFactory.packageQualifiedReference(w, baseExpr.computeSimpleReference(), computeSimpleReference(), false); break; } case LEXICAL: { typeRef = ReferenceFactory.lexicalReference(w, computeSimpleReference()); break; } default: { typeRef = ReferenceFactory.notATypeReference(w, computeSimpleReference()); break; } } return typeRef; } @Override public INamespaceReference computeNamespaceReference() { // Parser creates IdentifierNodes for "" for some error cases // of improperly written code, if (name == "") return null; INamespaceReference nsRef = null; Workspace w = (Workspace)getWorkspace(); switch (getRefType()) { case PACKAGE_QUALIFIED: { // Create a reference with the package name as the qualifier // a.b.ns1 // where a.b is a package name ExpressionNodeBase baseExpr = getBaseExpression(); nsRef = NamespaceDefinition.createNamespaceReference( getASScope(), getName(), w.getPackageNamespaceDefinitionCache().get(baseExpr.computeSimpleReference(), false)); break; } case LEXICAL: { // no qualifer, just a lexical ref nsRef = NamespaceDefinition.createNamespaceReference(getASScope(), getName(), null); break; } case NAMESPACE_QUALIFIED: { // Create a reference with the namespace from the qualifier expression as the qualifier // handles code like: // ns1::ns2 ExpressionNodeBase qualExpr = getQualifierExpression(); nsRef = NamespaceDefinition.createNamespaceReference(getASScope(), getName(), qualExpr.computeNamespaceReference()); break; } case MEMBER: { ExpressionNodeBase baseExpr = getBaseExpression(); IReference base = baseExpr.computeTypeReference(); nsRef = NamespaceDefinition.createNamespaceReference(getASScope(), getName(), null, base); break; } default: { break; } } return nsRef; } // IIdentifierNode implementations @Override public String getName() { return name; } @Override public IdentifierType getIdentifierType() { return IdentifierType.NAME; } // Other methods public void setReferenceValue(IDefinition definition) { } /** * Determines if this identifier is actually implicit and does not exist in source. */ public boolean isImplicit() { return getAbsoluteStart() == getAbsoluteEnd(); } private REF_TYPE getRefType() { if (isMemberRef()) { // If this is a member expr, then the stem better be a package; // otherwise we can't possibly be a type ref. if (baseIsPackage()) return REF_TYPE.PACKAGE_QUALIFIED; else return REF_TYPE.MEMBER; } else if (isQualifiedRef()) { return REF_TYPE.NAMESPACE_QUALIFIED; } else { return REF_TYPE.LEXICAL; } } /** * Determine if this identifier is an attribute reference (e.g. @name). * * @return True if this node is an attribute */ protected boolean isAttributeIdentifier() { ExpressionNodeBase p = getParentExpression(); if (p != null) return p.isAttributeExpr(this); return false; } /** * Is this a reference that has an explicit qualifier. e.g. the node for 'b' * in a::b woul return ture. * * @return true if this node should be resolved with an explicit qualifier */ private boolean isQualifiedRef() { ExpressionNodeBase expr = getParentExpression(); if (expr != null) return expr.isQualifiedExpr(this); return false; } /** * Determine if this identifier node is really part of a package reference. * this would be the node for b in 'a.b.c' if a.b.c was a known package name. * @return true if this identifier is used as part of a package reference. */ private boolean isPartOfPackageReference() { ExpressionNodeBase expr = this.getParentExpression(); MemberAccessExpressionNode memExpr = null; while (expr instanceof MemberAccessExpressionNode) { memExpr = (MemberAccessExpressionNode)expr; expr = expr.getParentExpression(); } if (memExpr != null) return memExpr.isPackageReference(); return false; } /** * Is this a reference that should be resolve in another object e.g. the * node for 'b' in a.b would return true * * @return true if this node should be resolved in another object */ public boolean isMemberRef() { ExpressionNodeBase expr = getParentExpression(); boolean memberRef = false; if (expr != null) return expr.isPartOfMemberRef(this); return memberRef; } /** * This method resolves an identifier that is the right-hand-side of a member access expression * (i.e., <code>b</code> in <code>a.b</code> or <code>a.ns::b</code>). */ private IDefinition resolveMemberRef(ICompilerProject project, ASScope asScope, String name, IQualifiers qualifier) { IDefinition result = null; // Determine baseType, the type of 'a' (the left-hand-side of the member access operator). IDefinition baseType = null; ExpressionNodeBase baseExpr = getBaseExpression(); if (baseExpr != null) { baseType = baseExpr.resolveType(project); if (baseType != null) { boolean isSuper = false; if (baseType instanceof IClassDefinition) { // If the base type is XML or XMLList, // don't resolve a member reference to any definition. // As in the old compiler, this avoids possibly bogus // -strict type-coercion errors. // For example, we don't want a declared property like .name // to resolve to the method slot, because it might actually // be referring to a dynamically-defined XML attribute or child tag. // And if we did resolve it to the name() method, which returns Object, // then when doing s = x.name() where s is type String // and x is type XML you would get a can't-convert-Object-to-String // problem, but there is lots of existing source code that expects // this to compile with no cast. if (!((FlexProject)project).useStrictXML() && isXMLish(baseType, project)) return null; if (baseExpr instanceof IdentifierNode) { IdentifierNode idBase = (IdentifierNode)baseExpr; if (idBase instanceof ILanguageIdentifierNode && (((ILanguageIdentifierNode)idBase).getKind() == LanguageIdentifierKind.THIS || ((ILanguageIdentifierNode)idBase).getKind() == LanguageIdentifierKind.SUPER)) { if (((ILanguageIdentifierNode)idBase).getKind() == LanguageIdentifierKind.SUPER) { isSuper = true; } } } } if (qualifier != null) result = asScope.getQualifiedPropertyFromDef(project, baseType, name, qualifier, isSuper); else result = asScope.getPropertyFromDef(project, baseType, name, isSuper); } } return result; } /** * Resolve the qualifier of this IdentifierNode, if the IDentifierNode is * part of a QualifiedExpression * * @param project The {@link ICompilerProject} to use to do lookups. * @return The IQualifiers the qualifier resolved to, or null if it * was unresolved. A single qualifier may resolve to multiple namespaces */ private IQualifiers resolveQualifier(ICompilerProject project) { IQualifiers qual = null; ExpressionNodeBase qualExpr = getQualifierExpression(); if (qualExpr != null) { if( qualExpr instanceof NamespaceIdentifierNode ) { // namespace reference, have the namespace // node resolve as a qualifier qual = ((NamespaceIdentifierNode) qualExpr).resolveQualifier(project); } else { // Some random expression, just try and resolve it IDefinition def = qualExpr.resolve(project); if (def instanceof NamespaceDefinition) qual = (NamespaceDefinition)def; } } return qual; } /** * Get the ExpressionNodeBase of the qualifier if this Node is part of a * qualified expression */ private ExpressionNodeBase getQualifierExpression() { ExpressionNodeBase expr = getParentExpression(); if (expr != null) return expr.getQualifier(this); return null; } /** * For member refs, determine if the base expression is really a reference * to a known package name. * * @return true, if the base expression should be treated as a package name */ private boolean baseIsPackage() { ExpressionNodeBase expr = getParentExpression(); if (expr instanceof MemberAccessExpressionNode) return ((MemberAccessExpressionNode)expr).stemIsPackage(); return false; } /** * Helper method to get the parent node as an IDefinitionNode * * @return the parent IDefinitionNode, or null if the parent is not an * IDefinitionNode */ private BaseDefinitionNode getParentAsDefinition() { IASNode p = getParent(); BaseDefinitionNode def = p instanceof BaseDefinitionNode ? (BaseDefinitionNode)p : null; return def; } /** * Helper method to determine if this node is the name node of a definition. * * @return true if this Node is the Name node of a definition */ private boolean isNameNode() { IDefinitionNode parent = getParentAsDefinition(); if (parent != null) { if (parent.getNameExpressionNode() == this) return true; } return false; } /** * Can this Node be early bound to the Definition it refers to. * We can early bind if we resolve to a definition, the definition is not ambiguous, and * the definition is not something defined in one of the XML classes (XML or XMLList). * @param project project to resolve things in * @param def the definition this node resolved to * @return true if we can early bind, otherwise false */ protected boolean canEarlyBind(ICompilerProject project, IDefinition def) { if (def instanceof DefinitionBase && !AmbiguousDefinition.isAmbiguous(def)) { // Can't early bind to XML/XMLList properties as they may be hidden by the unknown contents // of the XML itself, i.e. a child tag named 'parent' // Matches ASC behavior. if (!isXMLish(def.getParent(), project)) return true; } return false; } /** * Helper method to make an AET name from a Set<INamespaceDefinition> and a * name. * * @param namespaceSet the set of namespaces to use for the name * @param name the name to use for the AET Name. * @return the AET Name */ private static Name makeName(Set<INamespaceDefinition> namespaceSet, String name, boolean isAttr) { int nameKind = isAttr ? CONSTANT_MultinameA : CONSTANT_Multiname; Nsset nsSet = SemanticUtils.convertSetINamespaceToNsset(namespaceSet); Name n = new Name(nameKind, nsSet, name); return n; } /** * Is this a type reference - e.g. a base class ref, type anno, etc * * @return true if this is a type reference */ protected boolean isTypeRef() { DependencyType dt = getDependencyType(); if (dt == DependencyType.SIGNATURE || dt == DependencyType.INHERITANCE) return true; if (dt == DependencyType.EXPRESSION) { final IASNode parent = getParent(); if (parent instanceof BaseTypedDefinitionNode && this == ((BaseTypedDefinitionNode)parent).getTypeNode()) return true; } return false; } public boolean isLegacyCodegen(ICompilerProject project) { if (!(project instanceof FlexProject)) return false; final Integer compatibilityVersion = ((FlexProject)project).getCompatibilityVersion(); if (compatibilityVersion == null) return false; else if (compatibilityVersion <= Configuration.MXML_VERSION_4_6) return true; else return false; } // // Inner types // private static enum REF_TYPE { /** * A Member expression where the stem is a package name */ PACKAGE_QUALIFIED, /** * A lexical ref */ LEXICAL, /** * A name where the base/qualifier is a runtime value */ RUNTIME, /** * A reference qualified by a namespace expression, such as ns1::x */ NAMESPACE_QUALIFIED, /** * A member reference, such as a.b.Foo */ MEMBER } }