/* * * 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.scopes; import java.util.ArrayList; import java.util.Collection; import java.util.Set; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.INamespaceDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.internal.definitions.AppliedVectorDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinitionBase; import org.apache.flex.compiler.internal.definitions.DefinitionBase; import org.apache.flex.compiler.internal.definitions.InterfaceDefinition; import org.apache.flex.compiler.internal.definitions.TypeDefinitionBase; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.tree.as.ScopedBlockNode; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.scopes.IASScope; import com.google.common.base.Predicate; /** * IASScope implementation for class & interface Necessary to implement AS3 name * resolution semantics correctly because we are using 1 scope to represent * instance and static scopes. Does not override findAllDefinition methods, as * they return everything that matches in the entire scope chain. */ public class TypeScope extends ASScope { private static final StaticPredicate STATIC_ONLY_PREDICATE = new StaticPredicate(true); private static final StaticPredicate INSTANCE_ONLY_PREDICATE = new StaticPredicate(false); public TypeScope(ASScope containingScope, ScopedBlockNode block, TypeDefinitionBase owningType) { super(containingScope, block); this.owningType = owningType; initScopes(containingScope); } public TypeScope(ASScope containingScope, TypeDefinitionBase owningType) { super(containingScope); this.owningType = owningType; initScopes(containingScope); } private void initScopes(ASScope containingScope) { staticScope = createStaticScope(containingScope); instanceScope = createInstanceScope(); } private StaticScope createStaticScope(ASScope containingScope) { // Static scope is contained by whatever scope contains the TypeScope return new StaticScope(containingScope, this); } private InstanceScope createInstanceScope() { // Instance scope always has the static scope as it's containing scope. return new InstanceScope(staticScope, this); } private final TypeDefinitionBase owningType; private StaticScope staticScope; private InstanceScope instanceScope; private boolean needsProtected; /** * Get the scope of the super class - this method will do the right thing depending on * if this scope is the instance of static scope. * @param project the Project to resolve things in * @return the scope to use as the scope of the super class. This may be the instance * scope of 'Class' (for static scopes), or the instance scope of the base class (for instance scopes), * or the Type Scope of the baseclass if we are looking for static+instance scopes */ public ASScope resolveSuperScope(ICompilerProject project, ScopeKind kind) { ASScope result = null; if ( owningType instanceof IClassDefinition) { if( kind == ScopeKind.STATIC ) { // For a static scope, the base scope will be the instance scope of 'Class' ITypeDefinition typeDef = project.getBuiltinType(IASLanguageConstants.BuiltinType.CLASS); if( typeDef != null ) { IASScope s = typeDef.getContainedScope(); if( s instanceof TypeScope ) result = ((TypeScope) s).getInstanceScope(); } } else { // Not static, so resolve the super class and get it's scope IClassDefinition superClass = ((IClassDefinition) owningType).resolveBaseClass(project); if( superClass instanceof TypeDefinitionBase) { IASScope superScope = superClass.getContainedScope(); if( superScope instanceof TypeScope ) { switch (kind) { case INSTANCE_AND_STATIC: // CM only - if we started with a plain old TypeScope, then just return the super // TypeScope result = (ASScope) superScope; break; case INSTANCE: // Want the instance scope, so grab the instance scope from the base classes contained // scope result = ((TypeScope) superScope).getInstanceScope(); break; default: // nothing to do - STATIC was handled above in the if check break; } } } } } return result; } /** * Adds the specified definition to this scope. * * @param definition The IDefinition to be added. */ @Override public void addDefinition(IDefinition definition) { if (definition != null) { addDefinitionToStore(definition); // Set up the containing scope to correctly point to the static or instance scopes if (definition instanceof DefinitionBase) { boolean isStatic = definition.isStatic(); final ASScope containingScope; if (isStatic) { containingScope = getStaticScope(); } else { containingScope = getInstanceScope(); if (!needsProtected) { INamespaceDefinition protectedNameSpace = owningType.getProtectedNamespaceReference(); needsProtected = (protectedNameSpace != null) && (protectedNameSpace.equals(definition.getNamespaceReference())); } } ((DefinitionBase)definition).setContainingScope(containingScope); } } } @Override protected void getPropertyForScopeChain(CompilerProject project, Collection<IDefinition> defs, String baseName, NamespaceSetPredicate namespaceSet, boolean findAll) { getPropertyForScopeChain(project, defs, baseName, namespaceSet, findAll, ScopeKind.INSTANCE_AND_STATIC); } /** * Enum to determine what kind of scope we are emulating. */ static enum ScopeKind { /** * The classic CodeModel view of a class scope as a single scope * that contains both instance and static members. */ INSTANCE_AND_STATIC, /** * A filtered view of a class scope that contains only instance members, * for use by the compiler. */ INSTANCE, /** * A filtered view of a class scope that contains only static members, * for use by the compiler. */ STATIC; /** * Should this kind of scope find instance members? */ boolean findInstance() { return this == INSTANCE || this == INSTANCE_AND_STATIC; } /** * Should this kind of scope find static members? */ boolean findStatics() { return this == STATIC || this == INSTANCE_AND_STATIC; } /** * Should this kind of scope find the specified definition? */ boolean findDefinition(IDefinition definition) { if (this == INSTANCE) { // An INSTANCE scope sees only non-static members. return !definition.isStatic(); } else if (this == STATIC) { // A STATIC scope sees only static members. return definition.isStatic(); } else if (this == INSTANCE_AND_STATIC) { // An INSTANCE_AND_STATIC scope sees all members. return true; } else { assert false : "Unknown ScopeKind"; return false; } } } void getPropertyForScopeChain(CompilerProject project, Collection<IDefinition> defs, String baseName, NamespaceSetPredicate nsPred, boolean findAll, ScopeKind lookupKind) { ArrayList<ITypeDefinition> types = new ArrayList<ITypeDefinition>(); for (ITypeDefinition type : owningType.typeIteratable(project, false)) { types.add(type); } int originalDefCount = defs.size(); // Have to do instances first, in case we're looking for instance and statics if (lookupKind.findInstance()) { // We will need to propagate protected namespaces up the chain, but only // if we were passed a namespace set that implies "protected" is open boolean needProtectedNS = nsPred.containsNamespace(owningType.getProtectedNamespaceReference()); Collection<IDefinition> iDefs = new FilteredCollection<IDefinition>(INSTANCE_ONLY_PREDICATE, defs); for (ITypeDefinition type : types) { ASScope typeScope = (ASScope)type.getContainedScope(); if( needProtectedNS ) { nsPred.setExtraNamespace(type.getProtectedNamespaceReference()); } typeScope.getLocalProperty(project, iDefs, baseName, true); if ((!findAll) && (defs.size() > originalDefCount)) { return; } } } if (lookupKind.findStatics()) { Collection<IDefinition> sDefs = new FilteredCollection<IDefinition>(STATIC_ONLY_PREDICATE, defs); for (ITypeDefinition type : types) { ASScope typeScope = (ASScope)type.getContainedScope(); typeScope.getLocalProperty(project, sDefs, baseName, true); if ((!findAll) && (defs.size() > originalDefCount)) { return; } } } } @Override protected void getPropertyForMemberAccess(CompilerProject project, Collection<IDefinition> defs, String baseName, NamespaceSetPredicate namespaceSet, boolean findAll) { getPropertyForMemberAccess(project, defs, baseName, namespaceSet, findAll, ScopeKind.INSTANCE_AND_STATIC); } protected void getPropertyForMemberAccess(CompilerProject project, Collection<IDefinition> defs, String baseName, NamespaceSetPredicate namespaceSet, boolean findAll, ScopeKind lookupKind) { int originalDefCount = defs.size(); boolean needProtectedNamespaces = namespaceSet == ASScopeBase.allNamespacesSet || namespaceSet.containsNamespace(owningType.getProtectedNamespaceReference()); NamespaceSetPredicate nsPred = namespaceSet; Collection<IDefinition> iDefs = new FilteredCollection<IDefinition>(INSTANCE_ONLY_PREDICATE, defs); if (lookupKind.findInstance()) { for (ITypeDefinition type : owningType.typeIteratable(project, false)) { ASScope typeScope = (ASScope)type.getContainedScope(); if (needProtectedNamespaces) { nsPred.setExtraNamespace(type.getProtectedNamespaceReference()); } typeScope.getLocalProperty(project, iDefs, baseName, true); if ((defs.size() > originalDefCount) && (!findAll)) return; } } if (lookupKind.findStatics()) { Collection<IDefinition> sDefs = new FilteredCollection<IDefinition>(STATIC_ONLY_PREDICATE, defs); for (ITypeDefinition type : owningType.staticTypeIterable(project, false)) { if (type == null) { continue; } ASScope typeScope = (ASScope)type.getContainedScope(); typeScope.getLocalProperty(project, // Only lookup static properties in this scope - for any inherited scopes, we should lookup instance properties // because C$ (class) extends Class (instance), not Class$ (class) (typeScope == this) ? sDefs : iDefs, baseName, true); if ((defs.size() > originalDefCount) && (!findAll)) return; } } } /** * Helper to calculate filtered collections for this scope and the base * scopes. This varies because for static lookups, we want to check this * scope for "static" properties, but we want to check the base scopes for * "instance" properties * * @param defs The collection to wrap with the filter * @param lookupKind The kind of lookup we're performing * @return A MemberAccessCollections with 2 Collections: one to use for this * scope, and another to use for base scopes. When we are looking up * instance properties, or instance + static properties, then the 2 * collections will be the same When we are looking up static properties, * then the 2 collections will differ (the first one will find only statics, * but the second will find only instance properties). */ private MemberAccessCollections getFilteredCollectionsForMemberAccess(Collection<IDefinition> defs, ScopeKind lookupKind) { MemberAccessCollections p = null; if (lookupKind.findInstance() && lookupKind.findStatics()) { // Do nothing, we want both statics and instances p = new MemberAccessCollections(defs, defs); } else if (lookupKind.findStatics()) { // Only lookup static properties in this scope - for any inherited scopes, we should lookup instance properties // because C$ (class) extends Class (instance), not Class$ (class) p = new MemberAccessCollections( new FilteredCollection<IDefinition>(STATIC_ONLY_PREDICATE, defs), new FilteredCollection<IDefinition>(INSTANCE_ONLY_PREDICATE, defs)); } else if (lookupKind.findInstance()) { Collection<IDefinition> c = new FilteredCollection<IDefinition>(INSTANCE_ONLY_PREDICATE, defs); p = new MemberAccessCollections(c, c); } return p; } /** * Helper class to return 2 collections - my kingdom for a struct, or * generic Pair class */ private static class MemberAccessCollections { public MemberAccessCollections(Collection<IDefinition> thisDefs, Collection<IDefinition> baseDefs) { this.thisScopeCollection = thisDefs; this.baseScopeCollection = baseDefs; } public final Collection<IDefinition> thisScopeCollection; public final Collection<IDefinition> baseScopeCollection; } @Override public void getAllPropertiesForScopeChain(CompilerProject project, Collection<IDefinition> defs, Set<INamespaceDefinition> namespaceSet) { getAllPropertiesForScopeChain(project, defs, namespaceSet, ScopeKind.INSTANCE_AND_STATIC); } @Override public void getAllPropertiesForMemberAccess(CompilerProject project, Collection<IDefinition> defs, Set<INamespaceDefinition> namespaceSet) { getAllPropertiesForMemberAccess(project, defs, namespaceSet, ScopeKind.INSTANCE_AND_STATIC); } protected void getAllPropertiesForMemberAccess(CompilerProject project, Collection<IDefinition> defs, Set<INamespaceDefinition> namespaceSet, ScopeKind lookupKind) { ArrayList<ITypeDefinition> types = new ArrayList<ITypeDefinition>(); // For static property lookup we have a different inheritance chain. // 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 // for (ITypeDefinition type : lookupKind == ScopeKind.STATIC ? owningType.staticTypeIterable(project, false) : owningType.typeIteratable(project, false)) { types.add(type); } MemberAccessCollections p = getFilteredCollectionsForMemberAccess(defs, lookupKind); Collection<IDefinition> thisScopeDefs = p.thisScopeCollection; Collection<IDefinition> baseScopeDefs = p.baseScopeCollection; boolean needsProtected = false; if (namespaceSet != null) needsProtected = namespaceSet.contains(owningType.getProtectedNamespaceReference()); for (ITypeDefinition type : types) { ASScope typeScope = (ASScope)type.getContainedScope(); INamespaceDefinition protectedNamespace = null; if (needsProtected) { protectedNamespace = type.getProtectedNamespaceReference(); } // Only lookup static properties in this scope - for any inherited scopes, we should lookup instance properties // because C$ (class) extends Class (instance), not Class$ (class) typeScope.getAllLocalProperties(project, typeScope == this ? thisScopeDefs : baseScopeDefs, namespaceSet, protectedNamespace); } } protected void getAllPropertiesForScopeChain(CompilerProject project, Collection<IDefinition> defs, Set<INamespaceDefinition> namespaceSet, ScopeKind lookupKind) { ArrayList<ITypeDefinition> types = new ArrayList<ITypeDefinition>(); for (ITypeDefinition type : owningType.typeIteratable(project, false)) { types.add(type); } boolean needsProtected = false; if (namespaceSet != null) needsProtected = namespaceSet.contains(owningType.getProtectedNamespaceReference()); defs = getFilteredCollectionForScopeChainLookup(defs, lookupKind); for (ITypeDefinition type : types) { ASScope typeScope = (ASScope)type.getContainedScope(); INamespaceDefinition protectedNamespace = null; if (needsProtected) { protectedNamespace = type.getProtectedNamespaceReference(); } typeScope.getAllLocalProperties(project, defs, namespaceSet, protectedNamespace); } } /** * Helper method to apply the appropriate predicate to the Collection passed * in. * * @param defs the target collection * @param lookupKind what kind of lookup we're performing * @return A Collection with the appropriate filters to implement the lookup * 1. If looking for statics and instances, do nothing, just return the * original collection 2. If looking only for statics, then return a * filtered collection, which wraps the collection passed in, and will * filter out non-static definitions 3. If looking only for instance * properties, then return a filtered collection, which wraps the collection * passed in, and will filter out any static definitions */ private Collection<IDefinition> getFilteredCollectionForScopeChainLookup(Collection<IDefinition> defs, ScopeKind lookupKind) { if (lookupKind.findInstance() && lookupKind.findStatics()) { // do nothing } else if (lookupKind.findStatics()) { defs = new FilteredCollection<IDefinition>(STATIC_ONLY_PREDICATE, defs); } else if (lookupKind.findInstance()) { defs = new FilteredCollection<IDefinition>(INSTANCE_ONLY_PREDICATE, defs); } return defs; } @Override protected boolean namespaceSetSameAsContainingScopeNamespaceSet() { return false; } @Override public void addImplicitOpenNamespaces(CompilerProject compilerProject, Set<INamespaceDefinition> result) { addImplicitOpenNamespaces(compilerProject, result, ScopeKind.INSTANCE_AND_STATIC); } protected void addImplicitOpenNamespaces(CompilerProject compilerProject, Set<INamespaceDefinition> result, ScopeKind lookupKind) { IDefinition currentDefinition = this.getDefinition(); if (currentDefinition instanceof ClassDefinition) { ClassDefinition initialClassDef = (ClassDefinition)currentDefinition; // Private namespace always added - same private namespace can be used for static and instance properties result.add(initialClassDef.getPrivateNamespaceReference()); // Protected namespaces differ for static & instance scopes if (lookupKind.findInstance()) { result.add(initialClassDef.getProtectedNamespaceReference()); } // Add all the static protected namespaces to the namespace set. // Instance protected namespaces are added to the namespace set // in TypeScope as we walk up the class hierarchy. The instance // protected namespace needs to change as we walk up the class hierarchy. if (lookupKind.findStatics()) { for (IClassDefinition classDef : initialClassDef.classIterable(compilerProject, true)) result.add(((ClassDefinition)classDef).getStaticProtectedNamespaceReference()); } } else if (currentDefinition instanceof InterfaceDefinition) { InterfaceDefinition interfaceDefinition = (InterfaceDefinition)currentDefinition; result.add(interfaceDefinition.getInterfaceNamespaceReference()); } else if (currentDefinition instanceof AppliedVectorDefinition) { compilerProject.addGlobalUsedNamespacesToNamespaceSet(result); for (IClassDefinition classDef : ((AppliedVectorDefinition)currentDefinition).classIterable(compilerProject, true)) result.add(((ClassDefinitionBase)classDef).getStaticProtectedNamespaceReference()); } } @Override public String getContainingSourcePath(String baseName, ICompilerProject project) { return super.getContainingSourcePath(owningType.getQualifiedName(), project); } /** * Get the ASScope representing the itraits - this scope will only appear to * contain instance properties */ public ASScope getInstanceScope() { return instanceScope; } /** * Get the ASScope representing the ctraits - this scope will only appear to * contain static properties */ public ASScope getStaticScope() { return staticScope; } @Override public void setContainingScope(ASScope containingScope) { super.setContainingScope(containingScope); if (staticScope != null) staticScope.setContainingScope(containingScope); } /** * Wrapper class that makes a TypeScope appear to be an instance scope - it * will filter out all static properties, and appear to only contain * instance properties */ private static class InstanceScope extends ScopeView { InstanceScope(ASScope containingScope, TypeScope typeScope) { super(containingScope, typeScope); } @Override protected ScopeKind getScopeKind() { return ScopeKind.INSTANCE; } } /** * Wrapper class that makes a TypeScope appear to be an static scope - it * will filter out all instance properties, and appear to only contain * static properties */ private static class StaticScope extends ScopeView { StaticScope(ASScope containingScope, TypeScope typeScope) { super(containingScope, typeScope); } @Override protected ScopeKind getScopeKind() { return ScopeKind.STATIC; } } public boolean getNeedsProtected() { return needsProtected; } private static class StaticPredicate implements Predicate<IDefinition> { private boolean findStatics; public StaticPredicate(boolean b) { this.findStatics = b; } @Override public boolean apply(IDefinition definition) { return findStatics == definition.isStatic(); } } }