blob: dacad9d31152bce88f4693208bc8e9a742ef8e0f [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 static org.apache.royale.compiler.common.ISourceLocation.UNKNOWN;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IInterfaceDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.definitions.metadata.IMetaTag;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.tree.as.ITypeNode;
import org.apache.royale.utils.FastStack;
import com.google.common.base.Strings;
/**
* Instances of this class represent definitions of ActionScript types (i.e.,
* classes and interfaces) in the symbol table.
* <p>
* After a type definition is in the symbol table, it should always be accessed
* through the read-only <code>ITypeDefinition</code>,
* <code>IClassDefinition</code>, or <code>IInterfaceDefinition</code>
* interfaces.
*/
public abstract class TypeDefinitionBase extends MemberedDefinition implements ITypeDefinition
{
/**
* The ctraits for this type.
*/
private ClassTraitsDefinition classTraits;
public TypeDefinitionBase(String name)
{
super(name);
this.classTraits = new ClassTraitsDefinition(this);
}
/**
* Ctor that ClassTraitsDefinition can call to avoid infinite recursion
*/
protected TypeDefinitionBase(String name, ClassTraitsDefinition ctraits)
{
super(name);
this.classTraits = ctraits;
}
@Override
public boolean isInstanceOf(String qualifiedName, ICompilerProject project)
{
ITypeDefinition t = resolveType(qualifiedName, project, null);
if (t == null)
return false;
return isInstanceOf(t, project);
}
@Override
public ITypeNode getNode()
{
return (ITypeNode)super.getNode();
}
/**
* If the definition has metadata "__go_to_definition_help", use the
* annotated location. Otherwise, use the location information stored on the
* AST node.
* <p>
* {@inheritDoc}
*/
@Override
public int getNameStart()
{
final IMetaTag goToDefinitionHelp =
getMetaTagByName(IMetaTag.GO_TO_DEFINITION_HELP);
if (goToDefinitionHelp != null)
{
final String value = goToDefinitionHelp.getAttributeValue(
IMetaTag.GO_TO_DEFINITION_HELP_POS);
if (Strings.isNullOrEmpty(value))
{
return UNKNOWN;
}
else
{
return Integer.valueOf(value);
}
}
return super.getNameStart();
}
@Override
public String getPackageName()
{
// We know that all interface or class definitions are top level
// definitions so we overload this method to
// avoid the base class method trying to run up the scope
// chain to find the toplevel definition.
INamespaceReference namespaceRef = getNamespaceReference();
if (namespaceRef instanceof NamespaceDefinition.INamespaceWithPackageName)
return ((NamespaceDefinition.INamespaceWithPackageName)namespaceRef).getNamespacePackageName();
return "";
}
protected IInterfaceDefinition[] filterNullInterfaces(IInterfaceDefinition[] in)
{
ArrayList<IInterfaceDefinition> result = new ArrayList<IInterfaceDefinition>(in.length);
for (IInterfaceDefinition i : in)
{
if (i != null)
result.add(i);
}
return result.toArray(new IInterfaceDefinition[result.size()]);
}
@Override
public Iterable<ITypeDefinition> typeIteratable(final ICompilerProject project, final boolean skipThis)
{
final TypeDefinitionBase myThis = this;
return new Iterable<ITypeDefinition>()
{
@Override
public Iterator<ITypeDefinition> iterator()
{
return new TypeIterator(project, myThis, skipThis);
}
};
}
/**
* Get an iterator that will iterate over the static inheritance chain. This
* starts with the class object, and then proceeds to Class, as class
* objects extend Class, not whatever is specified in the extends clause.
*/
public Iterable<ITypeDefinition> staticTypeIterable(final ICompilerProject project, final boolean skipThis)
{
final TypeDefinitionBase myThis = this;
return new Iterable<ITypeDefinition>()
{
@Override
public Iterator<ITypeDefinition> iterator()
{
return new StaticTypeIterator(project, myThis, skipThis);
}
};
}
protected static class TypeIterator implements Iterator<ITypeDefinition>
{
private static final int INITIAL_STACK_SIZE = 10;
public TypeIterator(ICompilerProject project, ITypeDefinition initialType, boolean skipInitial)
{
this.project = project;
stack = new FastStack<ITypeDefinition>(INITIAL_STACK_SIZE);
visited = new HashSet<ITypeDefinition>();
classesWithInterfaces = new FastStack<IClassDefinition>();
init(initialType, skipInitial);
}
protected final ICompilerProject project;
protected final FastStack<ITypeDefinition> stack;
protected final Set<ITypeDefinition> visited;
/**
* Keep track of classes that implement interfaces, as we may need to iterate the interfaces
*/
protected final FastStack<IClassDefinition> classesWithInterfaces;
@Override
public boolean hasNext()
{
// First walk the classes in the inheritance chain
if( !stack.isEmpty() )
return true;
// after walking the inheritance chain walk the implemented interfaces
pushInterfaces();
return !stack.isEmpty();
}
@Override
public ITypeDefinition next()
{
if (!hasNext())
throw new NoSuchElementException();
ITypeDefinition next = stack.pop();
visited.add(next);
pushChildren(next);
return next;
}
/**
* Push the implemented interfaces of the last class with implemented interfaces
*/
protected void pushInterfaces()
{
boolean pushedInterface = false;
while(!pushedInterface && !classesWithInterfaces.isEmpty())
{
IClassDefinition clazz = classesWithInterfaces.pop();
IInterfaceDefinition[] interfaces = clazz.resolveImplementedInterfaces(project);
for (int i = interfaces.length - 1; i >= 0; i--)
{
if (interfaces[i] != null)
{
pushedInterface = true;
push(interfaces[i]);
}
}
}
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
protected void init(ITypeDefinition initialType, boolean skipThis)
{
stack.push(initialType);
if (skipThis)
next();
}
protected void push(ITypeDefinition type)
{
if (!(visited.contains(type)))
stack.push(type);
}
protected void pushChildren(ITypeDefinition type)
{
// Note that the children need to be pushed onto the stack reverse
// order of interfaces first, then the base class last, so the
// correct order is preserved while iterating through class type classes.
// The order preservation is required for functions such as TypeScope.getPropertyForMemberAccess()
// where it is iterating through the extended class, then interfaces looking
// for members, and it will return once it finds the member it is looking for
// to do this we push the base class, and add the type to a stack of types with
// interfaces. After we have walked all the base classes, we go back to the types
// with interfaces and then push the interfaces. This gives us the correct iteration
// order, while also making sure that we don't resolve the interfaces until we have
// to - many iterations will stop once they find the property they are looking for,
// and will never need to look in the interfaces.
if (type instanceof IClassDefinition)
{
IClassDefinition classDef = (IClassDefinition)type;
if( classDef.getImplementedInterfaceReferences().length > 0 )
classesWithInterfaces.push(classDef);
IClassDefinition baseClass = classDef.resolveBaseClass(project);
if (baseClass != null)
push(baseClass);
}
else if (type instanceof IInterfaceDefinition)
{
IInterfaceDefinition interfaceDef = (IInterfaceDefinition)type;
IInterfaceDefinition[] baseInterfaces = interfaceDef.resolveExtendedInterfaces(project);
for (int i = baseInterfaces.length - 1; i >= 0; i--)
{
if (baseInterfaces[i] != null)
push(baseInterfaces[i]);
}
}
}
}
/**
* Iterator to iterate over the ctraits. Class objects extend Class, not
* whatever is specified in the extends clause (that only applies to the
* instance objects) class C{} C$ (the class object) extends Class C
* (instance object) extends Object
*/
protected static class StaticTypeIterator extends TypeIterator
{
public StaticTypeIterator(ICompilerProject project, ITypeDefinition initialType, boolean skipInitial)
{
super(project, initialType, skipInitial);
}
private ITypeDefinition initialType;
@Override
protected void init(ITypeDefinition initialType, boolean skipThis)
{
this.initialType = initialType;
// bottom of the stack should be the Class type
ITypeDefinition classType = (ITypeDefinition)project.getBuiltinType(BuiltinType.CLASS);
stack.push(classType);
// add our initial type onto the stack, unless we're skipping it where we
// just add it straight to the visited list (see definitions of 'next()' && 'pushChildren()')
if (skipThis)
{
visited.add(initialType);
}
else
{
stack.push(initialType);
}
}
@Override
protected void pushChildren(ITypeDefinition type)
{
if (type == initialType)
{
return;
}
super.pushChildren(type);
}
}
/**
* Resolve the type of this type - will return a Definition representing the
* ctraits
*/
@Override
public TypeDefinitionBase resolveType(ICompilerProject project)
{
return classTraits;
}
/**
* Resolve the interfaces that this definition extends (if an interface), or implements (if a class).
* This is the implementation method that the ASScopeCache can call when there is a cache miss. Other
* clients should use the resolveExtendedInterfaces/resolveImplementedInterfaces
* @param project the active project
* @return an Array of IInterfaceDefinition that this type extends, or implements.
*/
public abstract IInterfaceDefinition[] resolveInterfacesImpl (ICompilerProject project);
}