/* * * 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.ABCConstants; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.semantics.Namespace; import org.apache.flex.abc.semantics.Nsset; import org.apache.flex.compiler.common.ASModifier; import org.apache.flex.compiler.constants.IASKeywordConstants; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.IVariableDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinitionBase; import org.apache.flex.compiler.internal.definitions.FunctionDefinition; import org.apache.flex.compiler.internal.scopes.ASScope; import org.apache.flex.compiler.parsing.IASToken; 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.ILanguageIdentifierNode; import org.apache.flex.compiler.tree.mxml.IMXMLDocumentNode; import org.apache.flex.compiler.tree.mxml.IMXMLScriptNode; /** * Represents identifiers that are built into the AS3 language. */ public class LanguageIdentifierNode extends IdentifierNode implements ILanguageIdentifierNode { /** * Builds a {@link LanguageIdentifierNode} that represents * <code>super</code>. * * @param superToken the token to build from * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildSuper(IASToken superToken) { return new LanguageIdentifierNode(IASKeywordConstants.SUPER, superToken, LanguageIdentifierKind.SUPER); } /** * Builds a {@link LanguageIdentifierNode} that represents * <code>super</code>. * * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildSuper() { return new LanguageIdentifierNode(IASKeywordConstants.SUPER, null, LanguageIdentifierKind.SUPER); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>this</code>. * * @param thisToken the token to build from * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildThis(IASToken thisToken) { return new LanguageIdentifierNode(IASKeywordConstants.THIS, thisToken, LanguageIdentifierKind.THIS); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>this</code>. * * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildThis() { return new LanguageIdentifierNode(IASKeywordConstants.THIS, null, LanguageIdentifierKind.THIS); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>this</code>. * * @param voidToken the token to build from * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildVoid(IASToken voidToken) { return new LanguageIdentifierNode(IASKeywordConstants.VOID, voidToken, LanguageIdentifierKind.VOID); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>this</code>. * * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildVoid() { return new LanguageIdentifierNode(IASKeywordConstants.VOID, null, LanguageIdentifierKind.VOID); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>*</code>. * * @param anyTypeToken the token to build from * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildAnyType(IASToken anyTypeToken) { return new LanguageIdentifierNode(IASLanguageConstants.ANY_TYPE, anyTypeToken, LanguageIdentifierKind.ANY_TYPE); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>*</code>. * * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildAnyType() { return new LanguageIdentifierNode(IASLanguageConstants.ANY_TYPE, null, LanguageIdentifierKind.ANY_TYPE); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>...</code>. * * @param restToken the token to build from * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildRest(IASToken restToken) { return new LanguageIdentifierNode(IASLanguageConstants.REST, restToken, LanguageIdentifierKind.REST); } /** * Builds a {@link LanguageIdentifierNode} that represents <code>...</code>. * * @return a {@link LanguageIdentifierNode} */ public static LanguageIdentifierNode buildRest() { return new LanguageIdentifierNode(IASLanguageConstants.REST, null, LanguageIdentifierKind.REST); } /** * Constructor. */ private LanguageIdentifierNode(String text, IASToken token, LanguageIdentifierKind kind) { super(text, token); this.kind = kind; } /** * Copy constructor. * * @param other the node to copy */ protected LanguageIdentifierNode (LanguageIdentifierNode other) { super(other); this.kind = other.kind; } private LanguageIdentifierKind kind; // // NodeBase overrides // @Override public ASTNodeID getNodeID() { switch (kind) { case SUPER: return ASTNodeID.SuperID; case VOID: return ASTNodeID.VoidID; default: return super.getNodeID(); } } // // ExpressionNodeBase overrides // @Override public IDefinition resolve(ICompilerProject project) { IDefinition def = null; switch (kind) { case THIS: case SUPER: { // There is no definition that introduced these def = null; break; } case VOID: { def = project.getBuiltinType(IASLanguageConstants.BuiltinType.VOID); break; } case REST: { def = project.getBuiltinType(IASLanguageConstants.BuiltinType.ARRAY); break; } case ANY_TYPE: { // a.*, or ns::* is not a reference to the ANY_TYPE if (isMemberRef() || isQualifiedExpr(this)) def = null; else def = project.getBuiltinType(IASLanguageConstants.BuiltinType.ANY_TYPE); break; } default: { def = super.resolve(project); break; } } return def; } @Override public ITypeDefinition resolveType(ICompilerProject project) { ITypeDefinition type = null; switch (kind) { case THIS: { type = resolveThisType(project); break; } case SUPER: { type = resolveSuperType(project); break; } case ANY_TYPE: { IDefinition def = resolve(project); // Didn't resolve to anything, just return '*' if (def == null) type = project.getBuiltinType(IASLanguageConstants.BuiltinType.ANY_TYPE); else type = def.resolveType(project); break; } case REST: { type = project.getBuiltinType(IASLanguageConstants.BuiltinType.ARRAY); break; } case VOID: { type = project.getBuiltinType(IASLanguageConstants.BuiltinType.Undefined); break; } } return type; } @Override protected LanguageIdentifierNode copy() { return new LanguageIdentifierNode(this); } @Override public Name getMName(ICompilerProject project) { switch (kind) { case ANY_TYPE: { if( isQualifiedExpr(this) || isMemberRef() ) { Name superName = super.getMName(project); return new Name(superName.getKind(), superName.getQualifiers(), null); } else if (isAttributeIdentifier()) { return new Name(ABCConstants.CONSTANT_QnameA, new Nsset(new Namespace(ABCConstants.CONSTANT_PackageNs)), "*"); } else if (isTypeRef()) { // null means ANY_TYPE return null; } else { // E4X wildcard operator. return new Name("*"); } } case REST: { // This is a bit hokey, but this ILanguageIdentifierNode is saying // that the type of the parameter definition is type "rest" or "...". // Since those aren't spelled "Array", we translate to "Array" here. return new Name(IASLanguageConstants.Array); } default: { return super.getMName(project); } } } // // IdentifierNode overrrides // /** * @return a string description of this pseudo-identifier. */ @Override public String getName() { switch (kind) { case THIS: return "this"; case SUPER: return "super"; case ANY_TYPE: return "*"; case REST: return "..."; case VOID: return "void"; default: return super.getName(); } } // // ILanguageIdentifierNode implementations // @Override public LanguageIdentifierKind getKind() { return kind; } // // Other methods // /** * calculate the type of a this expression * * @param cache * @return */ private ITypeDefinition resolveThisType(ICompilerProject project) { ITypeDefinition thisType = null; Context ctx = getContext(); switch (ctx) { case INSTANCE_CONTEXT: { thisType = getContainingClassDef(); break; } case STATIC_CONTEXT: case PACKAGE_CONTEXT: { // Semantics should validate where this is used, and report an error for these // cases. thisType = null; break; } case GLOBAL_CONTEXT: { thisType = project.getBuiltinType(IASLanguageConstants.BuiltinType.OBJECT); break; } } return thisType; } /** * Calculate the type of a super expression */ private ITypeDefinition resolveSuperType(ICompilerProject project) { ITypeDefinition superType = null; Context ctx = getContext(); // 'super' is invalid in any other context if (ctx == Context.INSTANCE_CONTEXT) { ClassDefinitionBase clazz = getContainingClassDef(); if (clazz != null) superType = clazz.resolveBaseClass(project); } return superType; } /** * Helper function to get the class definition that contains this Node */ private ClassDefinitionBase getContainingClassDef() { ASScope scope = getASScope(); if (scope != null) return scope.getContainingClass(); return null; } /** * Helper method to determine in what context this expression is being used * in. * * @return The Context that this expression occurs in */ public Context getContext() { IASNode p = getParent(); Context c = null; while (p != null && c == null) { if (p instanceof VariableNode) { VariableNode vn = (VariableNode)p; IVariableDefinition varDef = (IVariableDefinition)vn.getDefinition(); switch (varDef.getVariableClassification()) { case CLASS_MEMBER: { if (varDef.hasModifier(ASModifier.STATIC)) c = Context.STATIC_CONTEXT; else c = Context.INSTANCE_CONTEXT; break; } case PACKAGE_MEMBER: { c = Context.PACKAGE_CONTEXT; break; } case FILE_MEMBER: { c = Context.GLOBAL_CONTEXT; break; } // Otherwise, Keep looking, context will be based on the enclosing definition of the // Variable Decl //case LOCAL: //case PARAMETER: //break; } } else if (p instanceof FunctionNode) { FunctionNode fn = (FunctionNode)p; FunctionDefinition funcDef = fn.getDefinition(); switch (funcDef.getFunctionClassification()) { case CLASS_MEMBER: case INTERFACE_MEMBER: { if (funcDef.hasModifier(ASModifier.STATIC)) c = Context.STATIC_CONTEXT; else c = Context.INSTANCE_CONTEXT; break; } case PACKAGE_MEMBER: case FILE_MEMBER: { c = Context.GLOBAL_CONTEXT; break; } } } if (p instanceof FunctionObjectNode) { // 'this' could be anything in a function expression - callers can use whatever 'this' they like c = Context.GLOBAL_CONTEXT; } else if (p instanceof ClassNode) { // If we get here then this expression must be loose code in a class def, // so we are running in a static context c = Context.STATIC_CONTEXT; } else if (p instanceof PackageNode) { // If we get here, then this expression must be loose code in a package def, // so we're running in a package context c = Context.PACKAGE_CONTEXT; } else if (p instanceof IMXMLScriptNode) { // If we get here then this expression must be loose code in a <fx:Script> tag, // which is compiled as if it was loose code in a class, so we are a static context c = Context.STATIC_CONTEXT; } else if (p instanceof IMXMLDocumentNode) { // If we get here, the this expression must be loose code in a MXML document, such // as in a databinding expression, so we're in Instance context c = Context.INSTANCE_CONTEXT; } p = p.getParent(); } // If we run out of parents, then we must be in a global context if (c == null) c = Context.GLOBAL_CONTEXT; return c; } // // Inner types // public enum Context { // Code running in an instance of a class - prop initializers, member methods, etc INSTANCE_CONTEXT, // Code running in a static initialier - static initializers, static members, loose code in a class def STATIC_CONTEXT, // Code running at the package level - code inside a package def, package property initializers PACKAGE_CONTEXT, // Code running at the global level outside of a package def GLOBAL_CONTEXT } }