blob: d0bd2cd61d41787737a40947086cf7f7001a25cc [file] [log] [blame]
/*
*
* 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.definitions;
import org.apache.royale.abc.ABCConstants;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.constants.IASKeywordConstants;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.IPackageDefinition;
import org.apache.royale.compiler.definitions.IVariableDefinition;
import org.apache.royale.compiler.definitions.metadata.IMetaTag;
import org.apache.royale.compiler.internal.as.codegen.CodeGeneratorManager;
import org.apache.royale.compiler.internal.as.codegen.ICodeGenerator;
import org.apache.royale.compiler.internal.as.codegen.ICodeGenerator.IConstantValue;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.internal.scopes.CatchScope;
import org.apache.royale.compiler.internal.scopes.FunctionScope;
import org.apache.royale.compiler.internal.semantics.SemanticUtils;
import org.apache.royale.compiler.internal.tree.as.LiteralNode;
import org.apache.royale.compiler.internal.tree.as.NodeBase;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.scopes.IASScope;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IExpressionNode;
import org.apache.royale.compiler.tree.as.IImportNode;
import org.apache.royale.compiler.tree.as.IScopedNode;
import org.apache.royale.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 Royale 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 IExpressionNode getInitializer() {
return initializer;
}
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;
}
}