blob: eccc2b12ce258c7c1e879fd6a5b9e0da91cee866 [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 java.util.Collection;
import java.util.Collections;
import java.util.Set;
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.common.DependencyType;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.IEffectDefinition;
import org.apache.royale.compiler.definitions.IEventDefinition;
import org.apache.royale.compiler.definitions.IFunctionDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.IStyleDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.definitions.IAppliedVectorDefinition;
import org.apache.royale.compiler.definitions.metadata.IMetaTag;
import org.apache.royale.compiler.definitions.references.IReference;
import org.apache.royale.compiler.definitions.references.IResolvedQualifiersReference;
import org.apache.royale.compiler.definitions.references.ReferenceFactory;
import org.apache.royale.compiler.exceptions.MissingBuiltinException;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.internal.scopes.FunctionScope;
import org.apache.royale.compiler.internal.scopes.TypeScope;
import org.apache.royale.compiler.internal.tree.as.ExpressionNodeBase;
import org.apache.royale.compiler.internal.tree.as.IdentifierNode;
import org.apache.royale.compiler.internal.tree.as.TypedExpressionNode;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.scopes.IASScope;
import org.apache.royale.compiler.tree.as.IExpressionNode;
import org.apache.royale.compiler.workspaces.IWorkspace;
/**
* Definition representing a Vectorized type. It is built from a specific type,
* and gains the properties and functions of Array.
*/
public final class AppliedVectorDefinition extends ClassDefinitionBase implements IAppliedVectorDefinition
{
/**
* A Name representing the base __AS3__.vec.Vector class.
*/
private static final Name VECTOR_NAME;
static
{
Nsset nsSet = new Nsset(new Namespace(ABCConstants.CONSTANT_PackageNs, IASLanguageConstants.Vector_impl_package));
VECTOR_NAME = new Name(ABCConstants.CONSTANT_Qname, nsSet, IASLanguageConstants.Vector);
}
/*
* Builds a string of the form "Vector.<Sprite>" or "Vector.<flash.display.Sprite>",
* depending on the useFullyQualifiedNotation flag.
*/
private static String getName(ITypeDefinition elementType, boolean useFullyQualifiedNotation)
{
StringBuilder builder = new StringBuilder();
builder.append(IASLanguageConstants.Vector);
builder.append(".<");
if (useFullyQualifiedNotation)
builder.append(elementType.getQualifiedName());
else
builder.append(elementType.getBaseName());
builder.append(">");
return builder.toString();
}
/**
* Helper method to copy a parameter - will use the name from the info
* argument if provided, otherwise the name will be whatever the original
* parameters name was - this lets us give more descriptive names to some of
* the Vector parameters than they have in the .abc file.
*/
private static ParameterDefinition copyParameter(ICompilerProject project, ParameterDefinition orig, VectorInformation.ArgumentInfo info)
{
ParameterDefinition newParam = new ParameterDefinition(info != null ? info.getName() : orig.getBaseName());
newParam.setNamespaceReference(orig.getNamespaceReference());
newParam.setTypeReference(orig.getTypeReference());
if (orig.isRest())
newParam.setRest();
if (orig.hasDefaultValue())
newParam.setDefaultValue(orig.resolveDefaultValue(project));
return newParam;
}
/**
* Constructor.
* <p>
* Don't call this constructor from anywhere other than the ASProjectScope.
*
* @param project The compiler project
* @param baseClass The base class for the vector type, such as {@code Vector$int}.
* @param elementType The definition for the element type.
*/
public AppliedVectorDefinition(ICompilerProject project, IClassDefinition baseClass,
ITypeDefinition elementType)
{
// Set the 'storageName' field to a String like "Vector.<Sprite>".
super(AppliedVectorDefinition.getName(elementType, false));
this.project = project;
this.baseClass = baseClass;
this.elementType = elementType;
setContainedScope(new TypeScope(null, this));
setModifier(ASModifier.FINAL);
setImplicit();
setNamespaceReference(NamespaceDefinition.getPublicNamespaceDefinition());
}
private ICompilerProject project;
/*
* The base class will be Vector$int, Vector$uint, Vector$Number, or Vector$Object.
*/
private IClassDefinition baseClass;
/*
* The element type can be any class or interface, including another vector type
* in a nested case like Vector.<Vector.<int>>.
*/
private ITypeDefinition elementType;
@Override
protected String toStorageName(String name)
{
return name;
}
@Override
public IReference getBaseClassReference()
{
return null;
}
@Override
protected TypeDefinitionBase resolveType(String typeName,
ICompilerProject project, DependencyType dt)
{
return resolveType((DefinitionBase)baseClass, typeName, project, dt);
}
@Override
public final String getQualifiedName()
{
return AppliedVectorDefinition.getName(elementType, true);
}
@Override
public final String getBaseName()
{
return AppliedVectorDefinition.getName(elementType, false);
}
@Override
public final String getPackageName()
{
// Base class version of this method relies
// on this definition having a namespace reference
// which we don't have. So we just overload this
// method to return the empty string which is the package
// name clients of CM want for instances of Vector.<>.
return "";
}
@Override
public boolean isImplicit()
{
return true;
}
@Override
public IClassDefinition[] resolveAncestry(ICompilerProject project)
{
return new IClassDefinition[] {baseClass};
}
public String getBaseClassName()
{
return baseClass.getBaseName();
}
@Override
public ClassClassification getClassClassification()
{
return ClassClassification.PARAMETERIZED_CLASS_INSTANCE;
}
@Override
public String[] getImplementedInterfacesAsDisplayStrings()
{
return new String[0];
}
@Override
public IReference[] getImplementedInterfaceReferences()
{
return new IReference[0];
}
public IExpressionNode[] getImplementedInterfaceNodes()
{
return new IExpressionNode[0];
}
@Override
public IMetaTag[] getMetaTagsByName(String name)
{
return new IMetaTag[0];
}
@Override
public ITypeDefinition resolveElementType(ICompilerProject project)
{
return elementType;
}
/**
* Returns the expression that represents the value of this vector
*
* @return an expression that represents the value of this vector
*/
public ExpressionNodeBase toExpression()
{
if (elementType instanceof AppliedVectorDefinition)
{
return new TypedExpressionNode(new IdentifierNode(IASLanguageConstants.Vector), ((AppliedVectorDefinition)elementType).toExpression());
}
return new IdentifierNode(elementType.getBaseName());
}
/**
* Returns a typed expression that represents the signature of this Vector
*
* @return a {@link TypedExpressionNode}
*/
public TypedExpressionNode toTypedExpression()
{
if (elementType instanceof AppliedVectorDefinition)
{
return new TypedExpressionNode(new IdentifierNode(IASLanguageConstants.Vector), ((AppliedVectorDefinition)elementType).toTypedExpression());
}
return new TypedExpressionNode(new IdentifierNode(IASLanguageConstants.Vector), new IdentifierNode(elementType.getBaseName()));
}
@Override
public String getBaseClassAsDisplayString()
{
return baseClass.getQualifiedName();
}
@Override
public IClassDefinition resolveBaseClass(ICompilerProject project)
{
return baseClass;
}
public String getDefaultPropertyName()
{
return null;
}
@Override
public INamespaceDefinition getProtectedNamespaceReference()
{
return null;
}
@Override
public INamespaceDefinition getStaticProtectedNamespaceReference()
{
return null;
}
@Override
public IEventDefinition getEventDefinition(IWorkspace w, String name)
{
return null;
}
@Override
public IEventDefinition[] getEventDefinitions(IWorkspace w)
{
return new IEventDefinition[0];
}
@Override
public IEventDefinition[] findEventDefinitions(ICompilerProject project)
{
return new IEventDefinition[0];
}
@Override
public IStyleDefinition getStyleDefinition(IWorkspace w, String name)
{
return null;
}
@Override
public IStyleDefinition[] getStyleDefinitions(IWorkspace w)
{
return new IStyleDefinition[0];
}
@Override
public IStyleDefinition[] findStyleDefinitions(ICompilerProject project)
{
return new IStyleDefinition[0];
}
@Override
public IEffectDefinition getEffectDefinition(IWorkspace w, String name)
{
return null;
}
@Override
public IEffectDefinition[] getEffectDefinitions(IWorkspace w)
{
return new IEffectDefinition[0];
}
@Override
public IEffectDefinition[] findEffectDefinitions(ICompilerProject project)
{
return new IEffectDefinition[0];
}
@Override
public String[] getSkinStates(Collection<ICompilerProblem> problems)
{
return new String[0];
}
@Override
public String[] findSkinStates(ICompilerProject project, Collection<ICompilerProblem> problems)
{
return getSkinStates(problems);
}
@Override
public IMetaTag[] getSkinParts(Collection<ICompilerProblem> problems)
{
return new IMetaTag[0];
}
@Override
public IMetaTag[] findSkinParts(ICompilerProject project, Collection<ICompilerProblem> problems)
{
return getSkinParts(problems);
}
@Override
public Set<String> getStateNames()
{
return Collections.emptySet();
}
@Override
public Set<String> findStateNames(ICompilerProject project)
{
return getStateNames();
}
@Override
public Name getMName(ICompilerProject project)
{
// The Name for a type such as Vector.<T> has kind CONSTANT_TypeName.
// It gets constructed by passing two Names, the first representing
//the __AS3__.vec.Vector base type and the second representing the T type.
Name typeName = ((DefinitionBase)elementType).getMName(project);
return new Name(VECTOR_NAME, typeName);
}
/**
* Copy down definitions from Vector$object into the instantiated class as
* we go, modify to the parameter and return types to match what is actually
* expected (Vector$object becomes Vector.<T>, etc).
*
* @param project Project we are in
*/
public void adjustVectorMethods(ICompilerProject project)
{
VectorInformation vecInfo = VectorInformation.getInformation();
ASScope scope = getContainedScope();
// Copy the methods from the base class, so they report themselves as belonging
// to the instantiated Vector class instead of Vector$object, etc.
IASScope baseClassScope = baseClass.getContainedScope();
for (IDefinition defaultDef : baseClassScope.getAllLocalDefinitions())
{
String name = defaultDef.getBaseName();
VectorInformation.FunctionInfo info = vecInfo.getFunctionInfo(name);
if (defaultDef instanceof FunctionDefinition)
{
FunctionDefinition defaultFunc = (FunctionDefinition)defaultDef;
// Override the base class definition with one that has the correct signature
FunctionDefinition newDef;
if (defaultDef instanceof GetterDefinition)
{
newDef = new GetterDefinition(name);
newDef.setReturnTypeReference(defaultFunc.getReturnTypeReference());
newDef.setTypeReference(defaultFunc.getTypeReference());
}
else if (defaultDef instanceof SetterDefinition)
{
newDef = new SetterDefinition(name);
newDef.setReturnTypeReference(defaultFunc.getReturnTypeReference());
newDef.setTypeReference(defaultFunc.getTypeReference());
}
else
{
newDef = new FunctionDefinition(name);
newDef.setReturnTypeReference(defaultFunc.getReturnTypeReference());
}
ASScope newScope = new FunctionScope(scope);
newDef.setContainedScope(newScope);
newDef.setNamespaceReference(defaultDef.getNamespaceReference());
if (info != null)
{
if (info.returnIsTypeOfCollection())
newDef.setReturnTypeReference(ReferenceFactory.resolvedReference(elementType));
else if (info.returnIsVector())
newDef.setReturnTypeReference(ReferenceFactory.resolvedReference(this));
}
ParameterDefinition[] params = defaultFunc.getParameters();
if (params != null)
{
VectorInformation.ArgumentInfo[] args = info != null ? info.getArgumentInfo() : null;
ParameterDefinition[] newParams = new ParameterDefinition[params.length];
for (int i = 0, l = params.length; i < l; ++i)
{
if (args != null && i < args.length)
{
newParams[i] = copyParameter(project, params[i], args[i]);
if (args[i].returnIsVector())
newParams[i].setTypeReference(ReferenceFactory.resolvedReference(this));
else if (args[i].returnIsTypeOfCollection())
newParams[i].setTypeReference(ReferenceFactory.resolvedReference(elementType));
}
else
{
newParams[i] = copyParameter(project, params[i], null);
}
newScope.addDefinition(newParams[i]);
}
newDef.setParameters(newParams);
}
scope.addDefinition(newDef);
}
}
}
/**
* Gets the {@link ICompilerProject} that this
* {@link AppliedVectorDefinition} was created for.
*
* @return The {@link ICompilerProject} that this
* {@link AppliedVectorDefinition} was created for
*/
public ICompilerProject getProject()
{
return project;
}
@Override
public String getDefaultPropertyName(ICompilerProject project)
{
// TODO Eliminate this method stub. AppliedVectorDefinition should not be implementing IAppliedVectorDefinition.
return null;
}
private static AppliedVectorDefinition toVectorNodeOrContainingVectorNode(IDefinition def)
{
assert def != null;
if (def instanceof AppliedVectorDefinition)
return (AppliedVectorDefinition)def;
ASScope containingScope = (ASScope)def.getContainingScope();
if (containingScope == null)
return null;
IDefinition containingDefinition = containingScope.getDefinition();
if (containingDefinition instanceof AppliedVectorDefinition)
return (AppliedVectorDefinition)containingDefinition;
IDefinition containingClassDefiniton = containingScope.getContainingClass();
if (containingClassDefiniton instanceof AppliedVectorDefinition)
return (AppliedVectorDefinition)containingClassDefiniton;
return null;
}
/**
* Determines if the specified definition is a vector instantiation or a
* contained by a vector instantiation.
*
* @param def {@link IDefinition} to check.
* @return true if the specified definition is a vector instantiation or is
* contained by a vector instantiation.
*/
public static ICompilerProject getProjectIfVectorInsantiation(IDefinition def)
{
AppliedVectorDefinition vectorNode = toVectorNodeOrContainingVectorNode(def);
if (vectorNode != null)
return vectorNode.getProject();
return null;
}
public static boolean isVectorScope(ASScope scope)
{
// Check if this scope is a Vector scope, or if it is contained in a Vector scope
// this is possible because we now copy the methods down to the insantiated vector
// instead of finding them in the Vector$object baseclass.
return scope.getDefinition() instanceof AppliedVectorDefinition
|| scope.getContainingClass() instanceof AppliedVectorDefinition;
}
@Override
public IFunctionDefinition getConstructor()
{
assert false : "This should never get called.";
return null;
}
@Override
public IClassDefinition resolveHostComponent(ICompilerProject project)
{
return null;
}
/**
* Helper method that implements special instanceOf checks for Vector types.
* <p>
* This method aliases the Vector$XXXX where XXXX is double, uint, or int to
* Vector.<XXXXX>.
*
* @param base {@link ITypeDefinition} for the base type in the instanceOf
* check.
* @param derived {@link ITypeDefinition} for the dervied type in the
* instanceOf check.
* @return true if the derived type should be considered and instance of the
* base type.
*/
public static boolean vectorInstanceOfCheck(ITypeDefinition base, ITypeDefinition derived)
{
if (!(base instanceof AppliedVectorDefinition))
return false;
final AppliedVectorDefinition vectorInstance = (AppliedVectorDefinition)base;
if (vectorInstance.baseClass != derived)
return false;
assert ((INamespaceDefinition)vectorInstance.baseClass.getNamespaceReference()).getURI().equals(IASLanguageConstants.Vector_impl_package);
return !(IASLanguageConstants.Vector_object.equals(derived.getBaseName()));
}
@Override
public boolean isInProject(ICompilerProject project)
{
return project == this.project;
}
/**
* Returns the class definition for Vector$object, Vector$int, Vector$uint, or Vector$double.
*/
public static IClassDefinition lookupVectorImplClass(ICompilerProject project, String baseName)
{
// Only the base name (e.g., "Vector$object") is passed in.
// But the developer may have also defined something with the same
// base name, so we need to make sure that the definition we find
// also has the correct package, namely "__AS3__.vec".
INamespaceDefinition vectorImplPackage = ((CompilerProject)project).getWorkspace().getPackageNamespaceDefinitionCache().get(IASLanguageConstants.Vector_impl_package, true);
IResolvedQualifiersReference vectorReference = ReferenceFactory.resolvedQualifierQualifiedReference(project.getWorkspace(), vectorImplPackage, baseName);
IDefinition vectorIDefinition = vectorReference.resolve(project);
if(vectorIDefinition == null)
{
throw new MissingBuiltinException(baseName);
}
assert vectorIDefinition instanceof IClassDefinition :
"Unable to find: " + vectorReference.getDisplayString();
return (IClassDefinition) vectorIDefinition;
}
/**
* Updates the base class of this {@link AppliedVectorDefinition} if the
* vector base name of the base class matches the base name of the vector
* implementation class that has changed.
*
* @param changedVectorImplClass base name of the vector implementation
* class that has changed.
*/
public void updateBaseClass(String changedVectorImplClass)
{
final String baseClassBaseName = baseClass.getBaseName();
if (baseClassBaseName.equals(changedVectorImplClass))
baseClass = lookupVectorImplClass(project, baseClassBaseName);
}
}