/* * * 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 static org.apache.flex.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.flex.compiler.constants.IASLanguageConstants.BuiltinType; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IInterfaceDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.metadata.IMetaTag; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.tree.as.ITypeNode; import org.apache.flex.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; stack.push(initialType); ITypeDefinition classType = (ITypeDefinition)project.getBuiltinType(BuiltinType.CLASS); stack.push(classType); if (skipThis) next(); } @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); }