/* * * 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 org.apache.flex.abc.semantics.Name; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.definitions.references.IReference; import org.apache.flex.compiler.internal.scopes.ASScope; import org.apache.flex.compiler.internal.scopes.WithScope; 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.IExpressionNode; import org.apache.flex.compiler.tree.as.IScopedNode; /** * ActionScript parse tree node representing an expression */ public abstract class ExpressionNodeBase extends FixedChildrenNode implements IExpressionNode { private static final int FLAG_HAS_PARENS = 0x8000; private static final int FLAG_TREAT_AS_PACKAGE = 0x4000; /** * Constructor. */ public ExpressionNodeBase() { } /** * Copy constructor. * * @param other The node to copy. */ protected ExpressionNodeBase(ExpressionNodeBase other) { this.flags = other.flags; //this.fParent = other.fParent; this.setSourcePath(other.getSourcePath()); this.setLine(other.getLine()); this.setColumn(other.getColumn()); this.setStart(other.getAbsoluteStart()); this.setEnd(other.getAbsoluteEnd()); this.setEndLine(other.getEndLine()); this.setEndColumn(other.getEndColumn()); } protected int flags; // // NodeBase overrides // /** * Normalize the tree. Move custom children into the real child list and * fill in missing offsets so that offset lookup will work. Used during * parsing. */ @Override public void normalize(boolean fillInOffsets) { setChildren(fillInOffsets); // The list of children doesn't change, so the child count should be constant throughout the loop int childrenSize = getChildCount(); // Normalize the regular children for (int i = 0; i < childrenSize; i++) { IASNode child = getChild(i); if (child instanceof NodeBase) ((NodeBase)child).normalize(fillInOffsets); } if (fillInOffsets) fillInOffsets(); } @Override public ASScope getASScope() { ASScope scope = super.getASScope(); if (scope instanceof WithScope) { // If this expression is part of the target expression of a with node // then we want the scope containing the with scope, not the with scope itself. IExpressionNode baseExpr = this.getDecorationNode(); IASNode parent = baseExpr.getParent(); if (parent instanceof WithNode) { WithNode withParent = (WithNode)parent; if (withParent.getTargetNode() == baseExpr) { return withParent.getASScope(); } } } return scope; } // // IExpressionNode implementations // @Override public IDefinition resolve(ICompilerProject project) { // For all expression nodes except for the MemberAccessExpressionNode and the IdentifierNode, // this method returns null. return null; } @Override public ITypeDefinition resolveType(ICompilerProject project) { // TODO: implement for every expression type - currently only works for IdentifierNodes return null; } @Override public boolean isDynamicExpression(ICompilerProject project) { boolean isDynamic = true; ITypeDefinition type = resolveType(project); if (type != null) isDynamic = type.isDynamic(); return isDynamic; } public ExpressionNodeBase copyForInitializer(IScopedNode scopedNode) { ExpressionNodeBase newExpr = copy(); if (newExpr != null) { // If we copied the expression successfully, // set up all the parent pointers within the new subtree. if (scopedNode instanceof NodeBase) { NodeBase node = (NodeBase) scopedNode; newExpr.setParent(node); } newExpr.normalize(false); } return newExpr; } @Override public boolean hasParenthesis() { return (flags & FLAG_HAS_PARENS) != 0; } // // Other methods // /** * Copy the ExpressionNodeBase and its subtree. * * @return a new ExpressionNodeBase that represents the same content as the original node */ protected abstract ExpressionNodeBase copy(); /** * Gets the AET {@link Name} object to be used for an expression. * <p> * For all expression nodes except for the identifier nodes and member * access nodes, this method returns <code>null</code>. * <p> * This is the method that the code generator calls to determine what name * to emit into the ABC file. It will handle resolving the identifier if * necessary and generating the appropriate qname or multiname. * * @param project The {@link ICompilerProject} to use to do lookups. * @return A AET {@link Name} object. */ public Name getMName(ICompilerProject project) { return null; } public void setHasParenthesis(boolean hasParens) { if (hasParens) flags |= FLAG_HAS_PARENS; else flags &= ~FLAG_HAS_PARENS; } /** * Determine if this node is a reference to a package * * @return true if this node is really a package reference. */ public boolean isPackageReference() { // Return true if we're somewhere that MXML doesn't require packages to be imported if (getTreatAsPackage()) return true; boolean isPackage = false; if (this.getBaseExpression() == null) { // Only check for packages if we don't have a base expression. // in a.b.c, 'a', 'a.b', or 'a.b.c' could be package names, but not 'c' or 'b.c' String ref = this.computeSimpleReference(); if (ref != null) { ASScope scope = getASScope(); if (scope.isPackageName(ref)) isPackage = true; } } return isPackage; } /** * Get the base expression, if any. In <code>a.b</code>, or * <code>a.@b</code>, or <code>a.c::b</code>, the base of <code>b</code> is * <code>a</code>. * * @return The base as an ExpressionNodeBase, if it is one, otherwise null */ public ExpressionNodeBase getBaseExpression() { ExpressionNodeBase parent = getParentExpression(); if (parent != null) return parent.getBaseForMemberRef(this); return null; } /** * @return true if this ExpressionNodeBase is in a with scope. */ public boolean inWith() { ASScope scope = getASScope(); return scope != null ? scope.isInWith() : false; } /** * Get the appropriate node at which to start decoration. This returns the * largest containing expression node, which should provide enough context * to figure out what the definitions are. * * @return the node that should be decorated */ public ExpressionNodeBase getDecorationNode() { IASNode current = this; while (current.getParent() != null && current.getParent() instanceof ExpressionNodeBase) current = current.getParent(); return (ExpressionNodeBase)current; } /** * Check if the expression is inside a filter expression. For example: * * <pre> * xmlData.(getName() == "hello") * </pre> * * {@code getName() == "hello"} is in a filter. * <p> * The implementation walks up the parent nodes till the first * {@link ScopedBlockNode}. If a {@link MemberAccessExpressionNode} with * {@link ASTNodeID#E4XFilterID} is found, this node is inside a filter * expression. * * @return True if this node is in a filter expression. */ public final boolean inFilter() { IASNode parent = this.getParent(); while (parent != null && !(parent instanceof ScopedBlockNode)) { if (parent.getNodeID() == ASTNodeID.E4XFilterID) return true; else parent = parent.getParent(); } return false; } /** * Get the parent of this Node as an ExpressionNodeBase. * * @return the parent of this node as an ExpressionNodeBase, or null if it isn't * an ExpressionNodeBase. */ protected ExpressionNodeBase getParentExpression() { IASNode p = getParent(); return p instanceof ExpressionNodeBase ? (ExpressionNodeBase)p : null; } /** * Generate a simple reference - this is a String representing a name that * can be resolved with the set of open namespaces. If such a simple * reference can't be generated for this expression, null is returned. * * @return A String representing the simple name that represents this node, * or null if no simple name can be obtained. */ String computeSimpleReference() { return null; } /** * Generate an IReference that can serve as the type reference for something * like a type annotation, baseclass reference, interface reference, etc. * * @return An IReference that represents this expression node or null. */ IReference computeTypeReference() { return null; } /** * Generate an INamespaceReference that can serve as the namespace reference for something * like a namespace initializer. * * @return An INamespaceReference that represents this expression node or null. */ public INamespaceReference computeNamespaceReference() { return null; } boolean isPartOfMemberRef(ExpressionNodeBase e) { return false; } ExpressionNodeBase getBaseForMemberRef(ExpressionNodeBase e) { return null; } boolean isQualifiedExpr(ExpressionNodeBase e) { return false; } ExpressionNodeBase getQualifier(ExpressionNodeBase e) { return null; } /** * Determine if e is part of an attribute expression */ boolean isAttributeExpr(ExpressionNodeBase e) { return false; } private boolean getTreatAsPackage() { return (flags & FLAG_TREAT_AS_PACKAGE) != 0; } /** * When set to true, this expression will always be treated as a package - * the set of imports will not be checked. This is needed for some MXML * processing that did not require a user to import a package before using * it as a package (instead everything to the left of the last dot was * considered a package) e.g. 'a.b.Foo' would treat 'a.b' as a package name. * * @param b the new Value of treatAsPackage */ void setTreatAsPackage(boolean b) { if (b) flags |= FLAG_TREAT_AS_PACKAGE; else flags &= ~FLAG_TREAT_AS_PACKAGE; } /** * Compute the type of dependency between two compilation unit this * expression node creates. * * @return The type of dependency between two compilation unit this * identifier node creates. */ public DependencyType getDependencyType() { // TODO We'd really like to make this method not dependent on the AST shape. // In an ideal world the parser would use information it has while building the tree to // compute the dependency type or some other value that can be trivially used to compute the // dependency type. final IASNode parent = getParent(); // // If we're part of an expression, then ask our parent expression what type // of dependency we are. // should handle things like the node for 'Bar' in: // var t:Vector.<Vector.<Bar>>; // if( parent instanceof ExpressionNodeBase ) return ((ExpressionNodeBase)parent).getDependencyType(); // If the this node is part of the extends or implements expressions of a class // then this node creates an inheritance dependency. if (parent instanceof ClassNode && this == ((ClassNode)parent).getBaseClassNode()) return DependencyType.INHERITANCE; if (parent instanceof ContainerNode && parent.getParent() instanceof ClassNode && parent == ((ClassNode)parent.getParent()).getInterfacesNode()) return DependencyType.INHERITANCE; if (parent instanceof ContainerNode && parent.getParent() instanceof InterfaceNode && parent == ((InterfaceNode)parent.getParent()).getBaseInterfacesNode()) return DependencyType.INHERITANCE; // Why we are resolving the identifier node that is the name of a class is beyond me, // but might we'll treat that as a signature dependency. if (parent instanceof ClassNode && this == ((ClassNode)getParent()).getNameExpressionNode()) return DependencyType.SIGNATURE; // Identifier nodes referred to directly by a FunctionNode // or ParameterNode create signature dependencies unless // the FunctionNode is itself inside of an other FunctionNode. FunctionNode functionContainingReference = null; if (parent instanceof FunctionNode) { FunctionNode parentFunction = (FunctionNode)parent; if (this == parentFunction.getReturnTypeNode()) functionContainingReference = parentFunction; } else if (parent instanceof ParameterNode) { if (this == ((ParameterNode)parent).getTypeNode()) { IASNode tempNode = parent.getParent().getParent(); if (tempNode instanceof FunctionNode) functionContainingReference = (FunctionNode)tempNode; // We might be in a catch node, in which case we should be ane // expression dep, as theres no way a catch could influence the signature else if (parent.getParent() instanceof CatchNode) return DependencyType.EXPRESSION; } } if (functionContainingReference != null) { IASNode outerFunction = functionContainingReference.getAncestorOfType(FunctionNode.class); if (outerFunction != null) return DependencyType.EXPRESSION; else return DependencyType.SIGNATURE; } // Identifier nodes that are the type annotation of a definition // are signature dependencies unless they are inside of a FunctionNode. if (parent instanceof BaseTypedDefinitionNode && this == ((BaseTypedDefinitionNode)parent).getTypeNode()) { assert !((parent instanceof FunctionNode) || (parent instanceof ParameterNode)); // variable or const type annotation // If we are in a function this is an expression dep, otherwise it is a signature dep. FunctionNode functionContainingIdentifier = (FunctionNode)getAncestorOfType(FunctionNode.class); if (functionContainingIdentifier != null) return DependencyType.EXPRESSION; else return DependencyType.SIGNATURE; } // don't add a dependency because of an import stmt. The parent will always be an ImportNode // regardless of import tree shape, such as: // import foo; // import p.foo; // import p.*; if (parent instanceof ImportNode) return null; // Anything not handled by the above cases is an expression dependency. return DependencyType.EXPRESSION; } }