| /* |
| * |
| * 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.tree.as; |
| |
| 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.common.ASModifier; |
| import org.apache.royale.compiler.constants.IASKeywordConstants; |
| import org.apache.royale.compiler.constants.IASLanguageConstants; |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.definitions.ITypeDefinition; |
| import org.apache.royale.compiler.definitions.IVariableDefinition; |
| import org.apache.royale.compiler.internal.definitions.ClassDefinitionBase; |
| import org.apache.royale.compiler.internal.definitions.FunctionDefinition; |
| import org.apache.royale.compiler.internal.scopes.ASScope; |
| import org.apache.royale.compiler.parsing.IASToken; |
| import org.apache.royale.compiler.projects.ICompilerProject; |
| import org.apache.royale.compiler.tree.ASTNodeID; |
| import org.apache.royale.compiler.tree.as.IASNode; |
| import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode; |
| import org.apache.royale.compiler.tree.mxml.IMXMLDocumentNode; |
| import org.apache.royale.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 |
| } |
| } |