/* * * 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.definitions; import org.apache.flex.abc.ABCConstants; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.constants.IASKeywordConstants; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.IPackageDefinition; import org.apache.flex.compiler.definitions.IVariableDefinition; import org.apache.flex.compiler.definitions.metadata.IMetaTag; import org.apache.flex.compiler.internal.as.codegen.CodeGeneratorManager; import org.apache.flex.compiler.internal.as.codegen.ICodeGenerator; import org.apache.flex.compiler.internal.as.codegen.ICodeGenerator.IConstantValue; import org.apache.flex.compiler.internal.scopes.ASScope; import org.apache.flex.compiler.internal.scopes.CatchScope; import org.apache.flex.compiler.internal.scopes.FunctionScope; import org.apache.flex.compiler.internal.semantics.SemanticUtils; import org.apache.flex.compiler.internal.tree.as.LiteralNode; import org.apache.flex.compiler.internal.tree.as.NodeBase; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.scopes.IASScope; import org.apache.flex.compiler.tree.ASTNodeID; import org.apache.flex.compiler.tree.as.IExpressionNode; import org.apache.flex.compiler.tree.as.IImportNode; import org.apache.flex.compiler.tree.as.IScopedNode; import org.apache.flex.compiler.tree.as.IVariableNode; import java.util.Collection; /** * Instances of this class represent definitions of ActionScript variables and * constants in the symbol table. * <p> * After a variable definition is in the symbol table, it should always be * accessed through the read-only <code>IVariableDefinition</code> interface. */ public class VariableDefinition extends DefinitionBase implements IVariableDefinition { /** * Constructor. * * @param name The name of the variable. */ public VariableDefinition(String name) { super(name); } /** * Constructor. * * @param name The name of the variable. * @param initialValue The initial value of the variable. */ public VariableDefinition(String name, Object initialValue) { this(name); this.initValue = initialValue; } /** * The initial value of this VariableDefinition, if it is known this is used * for variables that come from an ABC with a value specified in the traits * entry. */ protected Object initValue; @Override public VariableClassification getVariableClassification() { // Determine whether or not a variable is local // by looking at its containing scope. // // Local variables don't always have a containing // function definition ( MXML event specifiers is one // example ). ASScope containingScope = getContainingASScope(); if (containingScope instanceof FunctionScope) return VariableClassification.LOCAL; if (containingScope instanceof CatchScope) return VariableClassification.LOCAL; IDefinition parent = getParent(); if (parent instanceof IClassDefinition) return VariableClassification.CLASS_MEMBER; if (parent instanceof IPackageDefinition) return VariableClassification.PACKAGE_MEMBER; if (parent == null) { if (inPackageNamespace()) return VariableClassification.PACKAGE_MEMBER; return VariableClassification.FILE_MEMBER; } assert false; return null; } // TODO Remove everything below here when Falcon has been integrated into Fb and Fc. /** * Gets the {@link DependencyType} that should be used when resolving the * type of this variable 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() { DependencyType dt = DependencyType.EXPRESSION; if (getVariableClassification() != VariableClassification.LOCAL) dt = DependencyType.SIGNATURE; return dt; } @Override public IVariableNode getNode() { return (IVariableNode)super.getNode(); } /** * Get the node that produced this definition if it has not yet been collected. * This will return null if the node has been collected, and won't do any work to get the * node back (like reparsing the file). * @return The Variable Node that produced this definition, or null if it does not exist */ private IVariableNode getNodeIfExists() { return (IVariableNode)nodeRef.getNodeIfExists(); } @Override public IVariableNode getVariableNode() { return getNode(); } @Override public boolean matches(DefinitionBase node) { boolean matches = super.matches(node); if (!matches) return false; if (node == this) return true; VariableDefinition vNode = (VariableDefinition)node; VariableClassification classification = vNode.getVariableClassification(); if (classification != getVariableClassification()) return false; // Along with local, file member and parameter, name offsets needs to be compared for class/interface members also. // This is required to differentiate members having same name belonging to different class/interface // within the same AS file - See FBG-3494 for an example. if (classification == VariableClassification.LOCAL || classification == VariableClassification.FILE_MEMBER || classification == VariableClassification.PARAMETER || classification == VariableClassification.CLASS_MEMBER || classification == VariableClassification.INTERFACE_MEMBER) { if (vNode.getNameStart() != getNameStart() || vNode.getNameEnd() != getNameEnd()) { return false; } } return true; } /** * For debugging only. Produces a string such as * <code>public var i:int</code>. */ @Override protected void buildInnerString(StringBuilder sb) { sb.append(getNamespaceReferenceAsString()); sb.append(' '); if (isStatic()) { sb.append(IASKeywordConstants.STATIC); sb.append(' '); } sb.append(IASKeywordConstants.VAR); sb.append(' '); sb.append(getBaseName()); String type = getTypeAsDisplayString(); if (!type.isEmpty()) { sb.append(':'); sb.append(type); } } public void setInitializer(IExpressionNode initExpr) { if( initExpr != null ) { if( initExpr instanceof LiteralNode ) { // Do some quick an dirty evaluation if the initializer is just a literal // no point in copying the expression and running it through the burm later switch(initExpr.getNodeID() ) { case LiteralBooleanID: initValue = SemanticUtils.getBooleanContent(initExpr); return; case LiteralDoubleID: initValue = SemanticUtils.getDoubleContent(initExpr); return; case LiteralStringID: initValue = SemanticUtils.getStringLiteralContent(initExpr); return; case LiteralIntegerID: initValue = SemanticUtils.getIntegerContent(initExpr); return; case LiteralNullID: initValue = ABCConstants.NULL_VALUE; return; case LiteralUintID: initValue = SemanticUtils.getUintContent(initExpr); return; } } setHasInit(); // If we're a local then we can depend on the weak reference back to the original // AST - if that AST is ever collected then we shouldn't be able to resolve back // to the definition of a local. if( getVariableClassification() != VariableClassification.LOCAL ) this.initializer = initExpr.copyForInitializer( new DefinitionScopedNode() ); } } private void setHasInit() { flags |= FLAG_HAS_INIT; } boolean hasInit() { return (flags & FLAG_HAS_INIT) != 0; } /** * IScopedNode impmlementation to make the containing scope available to the * copied initializer expression */ private class DefinitionScopedNode extends NodeBase implements IScopedNode { public IASScope getScope () { return getContainingASScope(); } public void getAllImports (Collection<String> imports) { } public void getAllImportNodes (Collection<IImportNode> imports) { } public ASTNodeID getNodeID () { return ASTNodeID.UnknownID; } } /** * Try to calculate the initial value for this VariableDefinition. * * @param project the project to use to resolve the intializer * @return the initial value of this definition, or null if one can't be * determined. */ @Override public Object resolveInitialValue(ICompilerProject project) { if (initValue != null) return initValue; IExpressionNode initExpr = getInitExpression(); if (initExpr != null) { ICodeGenerator codeGenerator = CodeGeneratorManager.getCodeGenerator(); IConstantValue constantValue = codeGenerator.generateConstantValue(initExpr, project); if (constantValue != null) return constantValue.getValue(); } return null; } private IExpressionNode initializer; protected IExpressionNode getInitExpression() { IExpressionNode init = null; if( hasInit() ) { if( this.getVariableClassification() == VariableClassification.LOCAL ) { IVariableNode n = getNodeIfExists(); assert n != null : "The AST for a local var should still be in memory!"; init = n.getAssignedValueNode(); } else { init = initializer; } } return init; } @Override public boolean isSkinPart() { return getSkinPart() != null; } @Override public boolean isRequiredSkinPart() { IMetaTag skinPart = getSkinPart(); if (skinPart == null) return false; return isRequiredSkinPart(skinPart); } /** * Tell this definition whether it was declared in a control flow block * or not. Default setting is false. * * @param b true if this definition was declared in control flow, otherwise false. */ public void setDeclaredInControlFlow(boolean b) { if (b) flags |= FLAG_DECLARED_IN_CONTROL_FLOW; else flags &= ~FLAG_DECLARED_IN_CONTROL_FLOW; } /** * Was this variable declared inside a control flow construct? * <p> * This is used in constant-evaluating the value of the variable. * * @return true if the Definition was declared inside a control-flow block */ public boolean declaredInControlFlow() { return (flags & FLAG_DECLARED_IN_CONTROL_FLOW) != 0; } }