/* * * 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.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.flex.compiler.common.ASImportTarget; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.common.IImportTarget; import org.apache.flex.compiler.common.NodeReference; 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.IQualifiers; import org.apache.flex.compiler.definitions.IScopedDefinition; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.internal.definitions.AmbiguousDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinitionBase; import org.apache.flex.compiler.internal.definitions.FunctionDefinition; import org.apache.flex.compiler.internal.definitions.InterfaceDefinition; import org.apache.flex.compiler.internal.definitions.NamespaceDefinition; import org.apache.flex.compiler.internal.definitions.ScopedDefinitionBase; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.tree.as.ScopedBlockNode; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.scopes.IDefinitionSet; import org.apache.flex.compiler.tree.as.IScopedNode; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.compiler.workspaces.IWorkspace; import org.apache.flex.utils.CheapArray; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; /** * IASScope implementation for class, interface, function, and 'with' scopes. */ public abstract class ASScope extends ASScopeBase { private static String[] EMPTY_STRING_ARRAY = new String[0]; protected static final NamespaceDefinition.IUseNamespaceDirective[] EMPTY_USE_ARRAY = new NamespaceDefinition.IUseNamespaceDirective[0]; /** * Constructor * * @param block block node to which this scope belongs */ public ASScope(ASScope containingScope, ScopedBlockNode block) { super(); setContainingScope(containingScope); if (block != null) { block.setScope(this); // Node reference constructor only works // if the setContainingScope has already be called // above. scopedNodeRef = new NodeReference(block); } } public ASScope(ASScope containingScope) { this(containingScope, null); } private ASScope containingScope; /** * Weak ref back to the Block node to which this scope belongs TODO: Remove * once code model clients don't depend on this anymore */ protected NodeReference scopedNodeRef = NodeReference.noReference; /** * List of all imports in scope */ private Object importsInScope = null; /** * List of all aliases for imports in scope */ private Map<String, String> aliasToImportQualifiedName = null; private Set<String> packageNames = null; private Object usedNamespaces = null; private NamespaceDefinition.INamespaceDirective firstNamespaceDirective; private NamespaceDefinition.INamespaceDirective lastNamespaceDirective; private boolean inWith = false; /** * Sets the scope which lexically contains this scope. * * @param containingScope The containing scope. */ public void setContainingScope(ASScope containingScope) { this.containingScope = containingScope; // calc this once, as it shouldn't change this.inWith = getContainingWithScope() != null; } /** * Compact the ArrayLists in this scope (so that they don't take up as much * space) */ @Override public void compact() { super.compact(); if (importsInScope != null) CheapArray.optimize(importsInScope, EMPTY_STRING_ARRAY); if (usedNamespaces != null) CheapArray.optimize(usedNamespaces, EMPTY_USE_ARRAY); } public void addNamespaceDirective(NamespaceDefinition.INamespaceDirective directive) { if (lastNamespaceDirective != null) { lastNamespaceDirective.setNext(directive); lastNamespaceDirective = directive; } else { assert firstNamespaceDirective == null; firstNamespaceDirective = directive; lastNamespaceDirective = directive; } } public void addUseDirective(NamespaceDefinition.IUseNamespaceDirective useDirective) { addNamespaceDirective(useDirective); if (usedNamespaces == null) usedNamespaces = CheapArray.create(1); CheapArray.add(useDirective, usedNamespaces); } public boolean hasImportAlias(String alias) { return aliasToImportQualifiedName != null && aliasToImportQualifiedName.containsKey(alias); } public void addImport(String target, String alias) { if (aliasToImportQualifiedName == null) { aliasToImportQualifiedName = new HashMap<String, String>(); } assert !hasImportAlias(alias) : "addImport() should not be called with an existing alias"; addImport(target); aliasToImportQualifiedName.put(alias, target); } public void addImport(String target) { if (importsInScope == null) { importsInScope = CheapArray.create(20); packageNames = new HashSet<String>(); } CheapArray.add(target, importsInScope); int idx = target.lastIndexOf('.'); if (idx != -1) { String packName = target.substring(0, idx); if (!target.endsWith(".*")) { // If this is not a wildcard import, then add the imported name // to the importedNames table so we can construct the right namespace // set for that name when we see a reference to it. String defName = target.substring(idx + 1, target.length()); if (importedNames == null) importedNames = new HashMap<String, Set<String>>(); Set<String> s = importedNames.get(defName); if (s == null) { s = new LinkedHashSet<String>(); importedNames.put(defName, s); } s.add(packName); } // Whether the import was a wildcard or not, the packageName contributes to the // set of known package names. packageNames.add(packName); } } @Override public ASScope getContainingScope() { return containingScope; } /** * Re-connects this scope to the syntax tree node that corresponds to this * scope. * * @param node {@link IScopedNode} that corresponds to this scope. */ public void reconnectScopeNode(IScopedNode node) { scopedNodeRef.reconnectNode(node); } @Override public IScopedNode getScopeNode() { IWorkspace w = getWorkspace(); return (IScopedNode)scopedNodeRef.getNode(w, this); } public String[] getImports() { return (String[])CheapArray.toArray(importsInScope, EMPTY_STRING_ARRAY); } private ScopedDefinitionBase containingDefinition; @Override public ScopedDefinitionBase getDefinition() { return containingDefinition; } public void setContainingDefinition(ScopedDefinitionBase value) { containingDefinition = value; } /** * For debugging only. */ @Override protected String toStringHeader() { StringBuilder sb = new StringBuilder(); sb.append(super.toStringHeader()); IDefinition definition = getDefinition(); if (definition != null) { sb.append(" for "); sb.append(definition.toString()); } return sb.toString(); } /** * Determine whether the string passed in is a known package name The scope * will check if the package was introduced by any of it's imports, and if * not will delegate to its containing scope. * * @param p the string to test * @return true is p is a package name */ public boolean isPackageName(String p) { if (packageNames != null) { if (packageNames.contains(p)) { return true; } } if (containingScope != null) return containingScope.isPackageName(p); return false; } /** * Return the additional namespaces for a reference, if the name has been * explicitly imported. If 'a.b.Foo' has been imported, and we see reference * to Foo, this will return the INamespaceDefinition for 'a.b'. If the name * has not been explicitly imported then this method will return the empty * set. * * @param project CompilerProject to use to resolve the package INamespaces * @param name The name of the reference * @return A Set<INamespaceDefinition> representing the packages from the * imports if the name was explicitly imported. Returns the empty set if the * name was not explicitly imported. */ public Set<INamespaceDefinition> getExplicitImportQualifiers(CompilerProject project, String name) { Set<INamespaceDefinition> nsSet = new LinkedHashSet<INamespaceDefinition>(); Workspace workspace = project.getWorkspace(); getContainingScopeExplicitImports(project, name, nsSet); if (importedNames != null) { // Was it an import in this scope Set<String> packages = importedNames.get(name); if (packages != null) { for (String s : packages) { nsSet.add(workspace.getPackageNamespaceDefinitionCache().get(s, false)); } } } return nsSet.size() > 0 ? nsSet : Collections.<INamespaceDefinition> emptySet(); } /** * Get the additional namespaces for a reference, if the name has been explicitly imported in * a containing scope * @param project the active project * @param name the name of the reference * @param nsSet the namespace set to add the namespaces to */ protected void getContainingScopeExplicitImports (CompilerProject project, String name, Set<INamespaceDefinition> nsSet) { if (getContainingScope() != null) { // check any containing scopes nsSet.addAll(getContainingScope().getExplicitImportQualifiers(project, name)); } } /** * Maps names to the package name used to look them up - this is used to * store explicit imports of definitions (import a.b.Foo) */ private Map<String, Set<String>> importedNames; protected INamespaceReference[] getUsedNamespaces() { return (INamespaceReference[])CheapArray.toArray(usedNamespaces, EMPTY_USE_ARRAY); } /** * Gets the first namespace definition or use namespace directive in the * scope. * * @return The first namespace definition or use namespace directive in the * scope. */ public NamespaceDefinition.INamespaceDirective getFirstNamespaceDirective() { return firstNamespaceDirective; } /** * Adds {@link INamespaceDefinition}'s for each import in this scope to the * specified namespace set. * * @param workspace {@link IWorkspace} used to construct * {@link INamespaceDefinition}'s for imported packages. * @param namespaceSet Namespace set to add namespaces to. */ public void addLocalImportsToNamespaceSet(IWorkspace workspace, Set<INamespaceDefinition> namespaceSet) { String[] imports = getImports(); if (imports != null) { for (String importStr : imports) { IImportTarget importTarget = ASImportTarget.get(workspace, importStr); // Only wildcard imports contribute to the namespace set // e.g. a.b.*, but not a.b.Foo if (importTarget.isWildcard()) namespaceSet.add(importTarget.getNamespace()); } } } /** * Calculate the namespace set to use to resolve name. If name is an * explicitly imported definition, then the namespace set will consist of * the package name from the import(s) plus the open namespace set. If name * was not explitly imported then the open namespace set will be calculated * and returned * * @param project The compiler project * @param name A name. * @return the namespace set to use to lookup name. This set should not be * modified */ public Set<INamespaceDefinition> getNamespaceSetForName(ICompilerProject project, String name) { // if the name is an alias, we want the original name -JT name = resolveBaseNameFromAlias(name); if (namespaceSetSameAsContainingScopeNamespaceSet() && getContainingScope() != null) { // If this scope doesn't contribute anything to the namespace set, then just ask our containing // scope for the namespace set. Doing this before we hit the cache has the benefit that the // namespace set will only get cached in the containing scopes cache, instead of getting cached // in each individual scope cache (e.g. it will be cached in the class scope, instead of in each function // scope in the class). This saves a lot of memory, as many functions will not affect the list // of open namespaces. return getContainingScope().getNamespaceSetForName(project, name); } CompilerProject compilerProject = (CompilerProject)project; ASScopeCache scopeCache = compilerProject.getCacheForScope(this); return scopeCache.getNamespaceSetForName(name); } protected boolean namespaceSetSameAsContainingScopeNamespaceSet() { if ((getImports() != null) || (getUsedNamespaces() != null)) return false; // function with no namespace set modifications, so reuse if (containingDefinition instanceof FunctionDefinition) return true; // TODO: can with scopes also be optimized here? return false; } /** * Implementation of getNamespaceSetForName method, above. The scope cache * will call this method when it does not already have the results cached * * @param project * @param name * @return the namespace set to use to lookup name. This set should not be * modified */ Set<INamespaceDefinition> getNamespaceSetForNameImpl(ICompilerProject project, String name) { if (namespaceSetSameAsContainingScopeNamespaceSet()) { ASScope containingScope = getContainingScope(); if (containingScope != null) { return containingScope.getNamespaceSetForName(project, name); } } Set<INamespaceDefinition> openNamespaces = getNamespaceSet(project); // If the reference has been explicitly imported, then we are a qualified name lookup // e.g. 'import a.b.Foo' means that any reference to Foo must have the package namespace of 'a.b' added // to its set of namespaces Set<INamespaceDefinition> additionalNamespaces = getExplicitImportQualifiers((CompilerProject)project, name); if (additionalNamespaces != null) { Set<INamespaceDefinition> newSet = new LinkedHashSet<INamespaceDefinition>(); newSet.addAll(openNamespaces); newSet.addAll(additionalNamespaces); return newSet; } else { return openNamespaces; } } /** * Computes and returns the namespace set for this scope. * <p> * The returned set should not be modified. * * @param project The compiler project. * @return The namespace set for this scope. The returned set should not be * modified */ public Set<INamespaceDefinition> getNamespaceSet(ICompilerProject project) { CompilerProject compilerProject = (CompilerProject)project; ASScopeCache scopeCache = compilerProject.getCacheForScope(this); return scopeCache.getNamespaceSet(); } /** * Computes and returns the namespace set for this scope. This is the * implementation of getNamespaceSet above. The scope cache will call this * method when it does not have a cached result for the namespace set. * <p> * The returned set should not be modified. * * @param project * @return The namespace set for this scope. The returned set should not be * modified */ Set<INamespaceDefinition> getNamespaceSetImpl(ICompilerProject project) { if (namespaceSetSameAsContainingScopeNamespaceSet()) { ASScope containingScope = getContainingScope(); if (containingScope != null) { return containingScope.getNamespaceSetImpl(project); } } CompilerProject compilerProject = (CompilerProject)project; IWorkspace workspace = compilerProject.getWorkspace(); Set<INamespaceDefinition> result = new LinkedHashSet<INamespaceDefinition>(); // First add the imports, use namespaces, etc from this scope this.addLocalImportsToNamespaceSet(workspace, result); INamespaceReference[] usedNamespaces = this.getUsedNamespaces(); if (usedNamespaces != null) { for (INamespaceReference usedNamespaceReference : usedNamespaces) { INamespaceDefinition usedNamespace = usedNamespaceReference.resolveNamespaceReference(compilerProject); if (usedNamespace != null) result.add(usedNamespace); } } this.addImplicitOpenNamespaces(compilerProject, result); // Next add the open namespaces from the containing scope addNamespacesFromContainingScope(compilerProject, result); Set<INamespaceDefinition> emptyNamespaceSet = Collections.emptySet(); result = result.size() == 0 ? emptyNamespaceSet : result; return result; } /** * Add the open namespaces from the containing scope to the namespace set passed in * @param compilerProject the active project * @param result the Namespace Set to add namespaces to */ protected void addNamespacesFromContainingScope (CompilerProject compilerProject, Set<INamespaceDefinition> result) { ASScope containingScope = this.getContainingScope(); if (containingScope != null) { result.addAll(containingScope.getNamespaceSet(compilerProject)); } } public void addImplicitOpenNamespaces(CompilerProject compilerProject, Set<INamespaceDefinition> result) { // By default there is nothing to do here. // overrides in ASFileScope, PackageScope, and TypeScope. } /** * Adds all definitions ( including definitions from base types ) in the * current scope to the specified collections of definitions that have a * namespace qualifier in the specified definition set, when looking for * definitions in the scope chain. * * @param project {@link CompilerProject} used to resolve reference to * definitions outside of the {@link ICompilationUnit} that contains this * scope. * @param defs Collection that found {@link IDefinition}'s are added to. * @param namespaceSet Namespace set in which the qualifier of any matching * definition must exist to be considered a match. */ public void getAllPropertiesForScopeChain(CompilerProject project, Collection<IDefinition> defs, Set<INamespaceDefinition> namespaceSet) { getAllLocalProperties(project, defs, namespaceSet, null); } /** * Adds all definitions ( including definitions from base types ) in the * current scope to the specified collections of definitions that have a * namespace qualifier in the specified definition set, when looking for * definitions through a member access. * * @param project {@link CompilerProject} used to resolve reference to * definitions outside of the {@link ICompilationUnit} that contains this * scope. * @param defs Collection that found {@link IDefinition}'s are added to. * @param namespaceSet Namespace set in which the qualifier of any matching * definition must exist to be considered a match. */ public void getAllPropertiesForMemberAccess(CompilerProject project, Collection<IDefinition> defs, Set<INamespaceDefinition> namespaceSet) { getAllLocalProperties(project, defs, namespaceSet, null); } /** * Gets all definitions (including definitions from base types) that have * the specified name to the specified collections of definitions that have * a namespace qualifier in the specified definition set, when looking for * definitions through a member access. * * @param project {@link CompilerProject} used to resolve reference to * definitions outside of the {@link ICompilationUnit} that contains this * scope. * @param memberName the name of the desired definition(s). * @param namespaceSet Namespace set in which the qualifier of any matching * definition must exist to be considered a match. * @return the collection of matching definitions. */ public List<IDefinition> getPropertiesByNameForMemberAccess(CompilerProject project, String memberName, Set<INamespaceDefinition> namespaceSet) { // Get the collection of all properties. List<IDefinition> result = new ArrayList<IDefinition>(); getPropertyForMemberAccess(project, result, memberName, namespaceSet, true); return result; } /** * Gets the definition (including definitions from base types) that has * the specified name and that has a namespace qualifier in the specified * namespace set, when looking for definitions through a member access. * * @param project {@link CompilerProject} used to resolve reference to * definitions outside of the {@link ICompilationUnit} that contains this * scope. * @param memberName the name of the desired definition(s). * @param namespaceSet Namespace set in which the qualifier of any matching * definition must exist to be considered a match. * @return The first definition that matches the name and namespaceSet. May return the AmbiguousDefinition * if more than one definition in a scope matches. */ public IDefinition getPropertyByNameForMemberAccess(CompilerProject project, String memberName, Set<INamespaceDefinition> namespaceSet) { List<IDefinition> defs = new ArrayList<IDefinition>(); getPropertyForMemberAccess(project, defs, memberName, namespaceSet, false); return getSingleResult(project, defs); } /** * Finds all the definitions in this scope that match the specified * namespace set and base name. This method is intended to implement the * getproperty operation defined by AS3 and the VM. * <p> * If this scope is not for a class or interface definition then only * definitions in this scope are considered. * <p> * If this scope is for a class or interface definition then definitions in * this scope and the scope for any implemented or extended interfaces and * classes are also considered. Unless findAll is true, then this function * returns as soon as one or more definitions has been found that match the * namespace set and base name. * <p> * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param defs Collection of {@link IDefinition}'s to add found definitions * to. * @param baseName The name of the definition(s) to find. * @param namespaceSet The namespace set in which a found definition's * qualifier must be in. * @param findAll If true find all match definitions that match the baseName * and namespace set not just those in the first scope that had one or more * matches. */ public void getPropertyForMemberAccess(CompilerProject project, Collection<IDefinition> defs, String baseName, Set<INamespaceDefinition> namespaceSet, boolean findAll) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, namespaceSet); Collection<IDefinition> filteredDefs = new FilteredCollection<IDefinition>(nsPred, defs); getPropertyForMemberAccess(project, filteredDefs, baseName, nsPred, findAll); } /** * Finds all the definitions in this scope that match the specified * namespace set and base name. This method is intended to implement the * getproperty operation defined by AS3 and the VM. * <p> * This version of the method expects that the Collection passed in will implement * whatever filtering is necessary, other than filtering based on the base name. * For most cases, this means the Collection will be an {@link ASScopeBase.FilteredCollection} * with a {@link NamespaceSetPredicate}. * The {@link NamespaceSetPredicate} must also be passed down as some name resolution * may need it to apply extra namespaces (i.e. deal with protected namespaces) * * <p> * If this scope is not for a class or interface definition then only * definitions in this scope are considered. * <p> * If this scope is for a class or interface definition then definitions in * this scope and the scope for any implemented or extended interfaces and * classes are also considered. Unless findAll is true, then this function * returns as soon as one or more definitions has been found that match the * namespace set and base name. * <p> * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param defs Collection of {@link IDefinition}'s to add found definitions * to. This collection must perform any necessary filtering of results, other than filtering * based on the baseName. * @param baseName The name of the definition(s) to find. * @param namespaceSet The {@link NamespaceSetPredicate} which the name resolution code * can use to modify the namespace set as necessary. * @param findAll If true find all match definitions that match the baseName * and namespace set not just those in the first scope that had one or more * matches. */ protected void getPropertyForMemberAccess(CompilerProject project, Collection<IDefinition> defs, String baseName, NamespaceSetPredicate namespaceSet, boolean findAll) { getLocalProperty(project, defs, baseName, true); } /** * Helper method to get a namespace set for a member access */ private Set<INamespaceDefinition> getNamespaceSetForMemberAccess(ICompilerProject project, IDefinition def, boolean isSuperRef) { Set<INamespaceDefinition> namespaceSet; if (def instanceof InterfaceDefinition) // If we are getting a property from an interface, use the special interface namespace set namespaceSet = ((InterfaceDefinition)def).getInterfaceNamespaceSet(project); else if (isSuperRef) namespaceSet = getNamespaceSetForSuper(project, def); else { namespaceSet = getNamespaceSet(project); // If the expression a.b occurs inside the class definition for a's type A, // then add A's protected namespace so that we can see a protected b. if (def == getContainingClass()) namespaceSet.add(((IClassDefinition)def).getProtectedNamespaceReference()); } return namespaceSet; } /** * Find a property in an IDefinition, using the open namespaces & packages * of this scope. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param def The definition to resolve the property in * @param name The name of the definition to find * @param isSuperRef whether this lookup is through a 'super' reference - if * it is then the namespace set will be adjusted to use the base classes * protected namespace instead of the containing classes protected namespace * @return The IDefinition for the property, or null if one is not found */ public IDefinition getPropertyFromDef(ICompilerProject project, IDefinition def, String name, boolean isSuperRef) { CompilerProject compilerProject = (CompilerProject)project; Set<INamespaceDefinition> namespaceSet = getNamespaceSetForMemberAccess(project, def, isSuperRef); return getPropertyFromDef(compilerProject, def, name, namespaceSet, false); } /** * Find a property in an IDefinition, using the open namespaces & packages * of this scope, and any additional constraints that are passed in as a {@link Predicate}. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param def The definition to resolve the property in * @param name The name of the definition to find * @param additional A {@link Predicate} that will perform additional filtering of the results. * This {@link Predicate} will run before any namespace set checking. * @param isSuperRef whether this lookup is through a 'super' reference - if * it is then the namespace set will be adjusted to use the base classes * protected namespace instead of the containing classes protected namespace * @return The IDefinition for the property, or null if one is not found */ public IDefinition getPropertyFromDef(ICompilerProject project, IDefinition def, String name, Predicate<IDefinition> additional, boolean isSuperRef) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, getNamespaceSetForMemberAccess(project, def, isSuperRef)); Predicate<IDefinition> combinedPred = Predicates.and(additional, nsPred); return getPropertyFromDef((CompilerProject)project, def, name, combinedPred, nsPred, isSuperRef); } /** * Find a property in an IDefinition, using the namespace passed in as the * qualifier. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve references. * @param def The definition to resolve the property in * @param name The name of the definition to find * @param qualifier The namespace to us to look up name * @param isSuperRef whether this lookup is through a 'super' reference - if * it is then the namespace set will be adjusted to use the base classes * protected namespace instead of the containing classes protected namespace * @return The IDefinition for the property, or null if one is not found */ public IDefinition getQualifiedPropertyFromDef(ICompilerProject project, IDefinition def, String name, INamespaceDefinition qualifier, boolean isSuperRef) { Set<INamespaceDefinition> namespaceSet = ImmutableSet.of(qualifier); if (isSuperRef) namespaceSet = adjustNamespaceSetForSuper(def, namespaceSet); return getPropertyFromDef((CompilerProject)project, def, name, namespaceSet, false); } /** * Find a property in an IDefinition, using the qualifiers passed in to provide the namespace set. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve references. * @param def The definition to resolve the property in * @param name The name of the definition to find * @param qualifiers The namespace(s) to us to look up name * @param isSuperRef whether this lookup is through a 'super' reference - if * it is then the namespace set will be adjusted to use the base classes * protected namespace instead of the containing classes protected namespace * @return The IDefinition for the property, or null if one is not found */ public IDefinition getQualifiedPropertyFromDef(ICompilerProject project, IDefinition def, String name, IQualifiers qualifiers, boolean isSuperRef) { Set<INamespaceDefinition> namespaceSet = qualifiers.getNamespaceSet(); if (isSuperRef) namespaceSet = adjustNamespaceSetForSuper(def, namespaceSet); return getPropertyFromDef((CompilerProject)project, def, name, namespaceSet, false); } /** * Implementation for getPropertyFromDef + getQualifiedPropertyFromDef */ private IDefinition getPropertyFromDef(CompilerProject project, IDefinition def, String name, Set<INamespaceDefinition> namespaceSet, boolean lookForStatics) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, namespaceSet); return getPropertyFromDef(project, def, name, nsPred, nsPred, lookForStatics); } /** * Implementation of getPropertyFromDef + getQualifiedPropertyFromDef * * @param project project to resolve references in * @param def The {@link IDefinition} to get the property from * @param name The name to look for * @param pred The {@link Predicate} to use to perform the lookup * @param nsPred The {@link NamespaceSetPredicate} to use if the namespace set needs to be modified * during lookup * @param lookForStatics whether to find statics or not */ private IDefinition getPropertyFromDef(CompilerProject project, IDefinition def, String name, Predicate<IDefinition> pred, NamespaceSetPredicate nsPred, boolean lookForStatics) { ASScope defScope = (ASScope)(def instanceof IScopedDefinition ? ((IScopedDefinition)def).getContainedScope() : null); // TODO: eliminate lookForStatics flag from getPropertyFromDef methods if (defScope instanceof TypeScope) { // Adjust scope if we are looking in a TypeScope TypeScope ts = (TypeScope)defScope; if (lookForStatics) defScope = ts.getStaticScope(); else defScope = ts.getInstanceScope(); } if (defScope != null) { ArrayList<IDefinition> defs = new ArrayList<IDefinition>(1); defScope.getPropertyForMemberAccess(project, new FilteredCollection<IDefinition>(pred, defs), name, nsPred, false); return getSingleResult(project, defs); } return null; } /** * Implementation of getPropertyForScopeChain. * * This method will filter results based on baseName only - any additional filtering * should be done by the {@link Collection} passed in. * * @param project {@link CompilerProject} to resolve things in * @param defs The {@link Collection} to add the results to * @param baseName The name of the definition to find * @param namespaceSet the {@link NamespaceSetPredicate} to use if the namespace set needs to be adjusted * during lookup */ protected void getPropertyForScopeChain(CompilerProject project, Collection<IDefinition> defs, String baseName, NamespaceSetPredicate namespaceSet, boolean findAll) { getLocalProperty(project, defs, baseName, true); } protected String resolveBaseNameFromAlias(String possibleAlias) { if (aliasToImportQualifiedName != null && aliasToImportQualifiedName.containsKey(possibleAlias)) { String qualifiedName = aliasToImportQualifiedName.get(possibleAlias); int index = qualifiedName.lastIndexOf("."); if (index != -1) { return qualifiedName.substring(index + 1); } return qualifiedName; } ASScope containingScope = getContainingScope(); if (containingScope != null) { return containingScope.resolveBaseNameFromAlias(possibleAlias); } return possibleAlias; } protected String resolveAliasFromQualifiedImport(String qualifiedName) { if (aliasToImportQualifiedName != null && aliasToImportQualifiedName.containsValue(qualifiedName)) { for (String key : aliasToImportQualifiedName.keySet()) { if (aliasToImportQualifiedName.get(key).equals(qualifiedName)) { return key; } } } ASScope containingScope = getContainingScope(); if (containingScope != null) { return containingScope.resolveAliasFromQualifiedImport(qualifiedName); } return null; } protected String resolveQualifiedNameFromAlias(String possibleAlias) { if (aliasToImportQualifiedName != null && aliasToImportQualifiedName.containsKey(possibleAlias)) { return aliasToImportQualifiedName.get(possibleAlias); } ASScope containingScope = getContainingScope(); if (containingScope != null) { return containingScope.resolveQualifiedNameFromAlias(possibleAlias); } return possibleAlias; } /** * This is called by {@link ASScopeCache} when there was a cache miss. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param baseName base name of the property we are looking for. * @param namespaceSet Namespace set in which the qualifier of any found * definition must found. * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @return One or more {@link IDefinition}'s matched by the namespace set * and base name. */ public List<IDefinition> findProperty(CompilerProject project, String baseName, Set<INamespaceDefinition> namespaceSet, DependencyType dt) { return findProperty(project, baseName, namespaceSet, dt, false); } /** * Version of findProperty that determine the results based on the namespace set passed in, * along with any additional constraints passed in via the {@link Predicate}. * * * @param project The {@link CompilerProject} to resolve things in * @param baseName The name to find * @param additional Any additional constraints on the lookup. This predicate will * run before any namespace checking occurs. * @param namespaceSet The Namespace set to use for the lookup * @param dt The dependency type to introduce if this resolves to something from * another compilation unit * @return a List of IDefinition that matched the name, namespace set, and any * additional constraints specified by the predicate. */ public List<IDefinition> findProperty(CompilerProject project, String baseName, Predicate<IDefinition> additional, Set<INamespaceDefinition> namespaceSet, DependencyType dt) { return findProperty(project, baseName, additional, namespaceSet, dt, false); } /** * This is the core <code>findproperty()</code> method. It implements the * equivalent of the <code>findprop</code> AVM instruction in Falcon. * <p> * The algorithm searches up the scope chain, starting with this scope, for * definitions with the specified base name and namespace set. * <p> * After the file scope, the project scope is searched if necessary. If * definitions are found in the project scope, a dependency is created on * the compilation unit that produced them. * <p> * If the <code>findAll</code> parameter is <code>false</code>, the search * stops with the first scope that has one or more matching definition; if * it is <code>true</code>, the search continues to find all matching * definitions in the entire chain, including the project scope. * * @param accumulator Collection to which definitions that match the * namespace set and base name are added. * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param baseName base name of the property we are looking for. * @param namespaceSet Namespace set in which the qualifier of any found * definition must found. * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @param findAll If true, then find all definitions that match the * namespace set and base name, not just those from the first scope with a * single match. */ public void findProperty(Collection<IDefinition> accumulator, CompilerProject project, String baseName, Set<INamespaceDefinition> namespaceSet, DependencyType dt, boolean findAll) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, namespaceSet); FilteredCollection<IDefinition> filteredCollection = new FilteredCollection<IDefinition>(nsPred, accumulator); findProperty(filteredCollection, project, baseName, nsPred, dt, findAll); } /** * This is the implementation of the various <code>findproperty()</code> methods. It implements the * equivalent of the <code>findprop</code> AVM instruction in Falcon. * <p> * The algorithm searches up the scope chain, starting with this scope, for * definitions with the specified base name. * <p> * If any additional constraints are required (e.g. filtering based on the namespace set), then * callers should pass in an {@link ASScopeBase.FilteredCollection} as the accumulator that will implement those * constraints. For the common case, the accumulator will be an {@link ASScopeBase.FilteredCollection} * with a {@link NamespaceSetPredicate}. * <p> * After the file scope, the project scope is searched if necessary. If * definitions are found in the project scope, a dependency is created on * the compilation unit that produced them. * <p> * If the <code>findAll</code> parameter is <code>false</code>, the search * stops with the first scope that has one or more matching definition; if * it is <code>true</code>, the search continues to find all matching * definitions in the entire chain, including the project scope. * * @param accumulator Collection to which definitions that match the * base name are added. * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param baseName base name of the property we are looking for. * @param nsPred The {@link NamespaceSetPredicate}, if one is being used, that the lookup * can modify as it walks up the scope chain (necessary to handle protected correctly). * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @param findAll If true, then find all definitions that match the * namespace set and base name, not just those from the first scope with a * single match. */ protected void findProperty(Collection<IDefinition> accumulator, CompilerProject project, String baseName, NamespaceSetPredicate nsPred, DependencyType dt, boolean findAll) { assert accumulator.isEmpty() : "findProperty() should not be called with a non-empty collection"; assert baseName.indexOf('.') == -1 : "baseName must not be any sort of qname"; // Walk the scope chain starting with this scope. // This loop may go as far as the file scope, whose containing scope is null. // But it may break out early; lastSearchScope will keep track of how far it went. ASScope lastSearchedScope = null; String baseNameForAlias = this.resolveBaseNameFromAlias(baseName); for (ASScope currentScope = this; currentScope != null; currentScope = currentScope.getContainingScope()) { // If we're not looking for all matching definitions, // and we've already got some, stop walking. if (!findAll && accumulator.size() != 0) break; // Search one scope for any definitions matching baseName and naamespaceSet. currentScope.getPropertyForScopeChain(project, accumulator, baseNameForAlias, nsPred, findAll); // Keep track of the last scope that was searched. lastSearchedScope = currentScope; } assert lastSearchedScope != null || accumulator.size() == 0 : "If accumulator is not empty, which searched scope added to it?"; // Determine whether we need to search the project scope. boolean searchProjectScope = false; // If we haven't found any matching definitions yet, // we need to search the project scope. if (accumulator.size() == 0) searchProjectScope = true; // If we're looking for all matching definitions, // we need to search the project scope. else if (findAll) searchProjectScope = true; // If the last scope we searched was a package scope or a file scope, // we need to search the project scope because the project scope // might have other definitions with the same name which should // cause an ambiguity. else if (lastSearchedScope instanceof PackageScope || lastSearchedScope instanceof ASFileScope) { searchProjectScope = true; } // Search the project scope if necessary. if (searchProjectScope) { ASProjectScope projectScope = project.getScope(); projectScope.getPropertyForScopeChain(this, accumulator, baseNameForAlias, nsPred.getNamespaceSet(), dt); } if(!baseName.equals(baseNameForAlias)) // has alias { // remove anything with the same base name that doesn't have an // alias, unless its base name is equal to the alias. that is the // only time where there will be ambiguity. -JT String alias = baseName; //for clarity ArrayList<IDefinition> toRemove = new ArrayList<IDefinition>(); String qualifiedNameForAlias = resolveQualifiedNameFromAlias(alias); for(IDefinition definition : accumulator) { if(!definition.getBaseName().equals(alias) && !definition.getQualifiedName().equals(qualifiedNameForAlias)) { toRemove.add(definition); } } // some collections can't remove while iterating, so do it after // collecting all of the definitions to remove -JT accumulator.removeAll(toRemove); } else // no alias { // remove anything that has an alias, unless its alias is equal to // the original base name. ArrayList<IDefinition> toRemove = new ArrayList<IDefinition>(); for (IDefinition definition : accumulator) { String otherAlias = resolveAliasFromQualifiedImport(definition.getQualifiedName()); if (otherAlias != null && !otherAlias.equals(baseName)) { toRemove.add(definition); } } accumulator.removeAll(toRemove); } } /** * For each {@link IProtectedNamespaceDefinition} in the given * {@code namespaceSet}, if it does not have a corresponding * {@link IStaticProtectedNamespaceDefinition}, create one and add it to the * {@code namespaceSet}. * * @param namespaceSet Namespace definitions. New items might be added. * @return Updated namespace definitions. */ @SuppressWarnings("unused") private static Set<INamespaceDefinition> addStaticProtectedNS(Set<INamespaceDefinition> namespaceSet) { if (namespaceSet == null) return null; // The keys are URI strings. The values are "protected" namespace definitions. final Map<String, INamespaceDefinition.IStaticProtectedNamespaceDefinition> staticProtectedNamespaces = new HashMap<String, INamespaceDefinition.IStaticProtectedNamespaceDefinition>(); final Set<INamespaceDefinition.IProtectedNamespaceDefinition> protectedNamespaces = new HashSet<NamespaceDefinition.IProtectedNamespaceDefinition>(); for (final INamespaceDefinition namespace : namespaceSet) { if (namespace instanceof INamespaceDefinition.IStaticProtectedNamespaceDefinition) { final INamespaceDefinition.IStaticProtectedNamespaceDefinition staticProtectedNamespace = (INamespaceDefinition.IStaticProtectedNamespaceDefinition)namespace; staticProtectedNamespaces.put( staticProtectedNamespace.getURI(), staticProtectedNamespace); } else if (namespace instanceof INamespaceDefinition.IProtectedNamespaceDefinition) { protectedNamespaces.add((INamespaceDefinition.IProtectedNamespaceDefinition)namespace); } } // Find all "protected" namespace definitions that don't have their // corresponding "static protected" namespace definitions. final Set<INamespaceDefinition.IStaticProtectedNamespaceDefinition> addedStaticProtectedNamespaces = new HashSet<NamespaceDefinition.IStaticProtectedNamespaceDefinition>(); for (final INamespaceDefinition.IProtectedNamespaceDefinition protectedNamespace : protectedNamespaces) { if (!staticProtectedNamespaces.containsKey(protectedNamespace.getURI())) { addedStaticProtectedNamespaces.add( NamespaceDefinition.createStaticProtectedNamespaceDefinition( protectedNamespace.getURI())); } } final Set<INamespaceDefinition> result; if (addedStaticProtectedNamespaces.isEmpty()) { result = namespaceSet; } else { result = new HashSet<INamespaceDefinition>(); result.addAll(addedStaticProtectedNamespaces); result.addAll(namespaceSet); } return result; } /** * This is the core findproperty method. This method implements the * equivalent of the findprop AVM instruction in Falcon. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param baseName base name of the property we are looking for. * @param namespaceSet Namespace set in which the qualifier of any found * definition must found. * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @param findAll If true, then find all definitons that match the namespace * set and base name, not just those from the first scope with a single * match. * @return One or more {@link IDefinition}'s matched by the namespace set * and base name. */ private List<IDefinition> findProperty(CompilerProject project, String baseName, Set<INamespaceDefinition> namespaceSet, DependencyType dt, boolean findAll) { ArrayList<IDefinition> defs = new ArrayList<IDefinition>(1); findProperty(defs, project, baseName, namespaceSet, dt, findAll); return defs; } /** * Version of findProperty that determine the results based on the namespace set passed in, * along with any additional constraints passed in via the {@link Predicate}. * * * @param project The {@link CompilerProject} to resolve things in * @param baseName The name to find * @param additional Any additional constraints on the lookup. This predicate will * run before any namespace checking occurs. * @param namespaceSet The Namespace set to use for the lookup * @param dt The dependency type to introduce if this resolves to something from * another compilation unit * @return a List of IDefinition that matched the name, namespace set, and any * additional constraints specified by the predicate. */ private List<IDefinition> findProperty(CompilerProject project, String baseName, Predicate<IDefinition> additional, Set<INamespaceDefinition> namespaceSet, DependencyType dt, boolean findAll) { ArrayList<IDefinition> defs = new ArrayList<IDefinition>(1); NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, namespaceSet); Predicate<IDefinition> pred = Predicates.and(additional, nsPred); FilteredCollection<IDefinition> filteredCollection = new FilteredCollection<IDefinition>(pred, defs); findProperty(filteredCollection, project, baseName, nsPred, dt, findAll); return defs; } /** * Is this scope inside a with scope. * * @return true if this scope is nested in a with scope, or this scope is a * with scope */ public boolean isInWith() { return inWith; } /** * Get any containing with scope * * @return the ASScope that is the containing with scope, or null if there * is no containing with scope */ ASScope getContainingWithScope() { ASScope scope = this; while (scope != null) { if (scope instanceof WithScope) return scope; scope = scope.getContainingScope(); } return scope; } /** * IFilter the result of a findDefinition based on any containing with scopes * and if the lookup is allowed to escape a with scope This method is used * by findProperty, and findPropertyQualified to filter the results. By * default, the lookup will look past the with scopes (this is what code * model expects), so we will filter the results here, but only if we are in * a with scope. With scopes are rare enough that the performance hit * shouldn't be too bad, since we only do the filtering when we know we are * in a with scope. If we are not in a with scope, or the lookup is allowed * to escape the with block, then we immediately return the passed in * definition. * * @param d the definition the lookup resolved to * @param canEscapeWith true if the lookup can esape a with, false if it * can't * @return the definition to use as the result of the lookup */ IDefinition filterWith(IDefinition d, boolean canEscapeWith) { if (!inWith || canEscapeWith || d == null) return d; ASScope withScope = getContainingWithScope(); if (withScope != null) { ASScope scope = this; while (scope != null) { // Didn't find the defns containing scope before we hit // the with, so act as if we couldn't resolve it. if (scope == withScope) return null; // We found the declaring scope, and we haven't seen a with // scope yet, so we can return the definition if (scope == d.getContainingScope()) return d; scope = scope.getContainingScope(); } return null; } return d; } /** * The main public entry point for the findprop operation in the compiler. * This method uses the {@link ASScopeCache} to improve performance. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param baseName base name of the property we are looking for. * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @return A single {@link IDefinition} to which the specified base name * resolves to in this scope, or null. Null is returned when no definition * is found <b>and</b> when more than one definition is found. */ public IDefinition findProperty(ICompilerProject project, String baseName, DependencyType dt) { return findProperty(project, baseName, dt, false); } /** * The main public entry point for the findprop operation in the compiler. * This method uses the {@link ASScopeCache} to improve performance. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param baseName base name of the property we are looking for. * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @param canEscapeWith should this lookup find definitions that occur * outside of a containing with scope * @return A single {@link IDefinition} to which the specified base name * resolves to in this scope, or null. Null is returned when no definition * is found <b>and</b> when more than one definition is found. */ public IDefinition findProperty(ICompilerProject project, String baseName, DependencyType dt, boolean canEscapeWith) { if (canDelegateLookupToContainingScope(baseName)) { // If we know that we can't possibly find the property in this scope, just ask the containing scope // which may have already computed and cached the result. return getContainingScope().findProperty(project, baseName, dt, canEscapeWith); } assert baseName.indexOf('.') == -1 : "baseName must not be any sort of qname"; CompilerProject compilerProject = (CompilerProject)project; ASScopeCache scopeCache = compilerProject.getCacheForScope(this); return filterWith(scopeCache.findProperty(baseName, dt), canEscapeWith); } /** * An alternate entry point for findprop operations. * * This method takes an addition Predicate that allows custom filtering of the results, instead * of just using the namespace set. This method will still use the namespace set, but the predicate passed * in will be called first to filter the results. * * @param project the active project * @param baseName base name of the property we're looking for * @param additional A Predicate that performs custom filtering on the results * @param dt The dependency type that should be added to the dependency graph * when resolving this reference across a compilation boundary * @param canEscapeWith should this lookup find definitions that occur outside of a containing with scope * @return A single {@link IDefinition} to which the specified base name resolves to in this * scope, given the additional constraints supplied by the additional Predicate. */ public IDefinition findProperty(ICompilerProject project, String baseName, Predicate<IDefinition> additional, DependencyType dt, boolean canEscapeWith) { Set<INamespaceDefinition> nsSet = getNamespaceSetForName(project, baseName); return findProperty(project, baseName, additional, nsSet, dt, canEscapeWith); } public IDefinition findProperty (ICompilerProject project, String baseName, Predicate<IDefinition> additional, Set<INamespaceDefinition> nsSet, DependencyType dt, boolean canEscapeWith) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, nsSet); List<IDefinition> storage = new ArrayList<IDefinition>(); Predicate<IDefinition> pred = Predicates.and(additional, nsPred); FilteredCollection<IDefinition> defs = new FilteredCollection<IDefinition>(pred, storage); findProperty(defs, (CompilerProject)project, baseName, nsPred, dt, false); IDefinition def = null; def = getSingleResult(project, storage); return filterWith(def, canEscapeWith); } /** * Helper method to narrow a List of results down to one result for the methods * that return only 1 result. * @return null if there are no results, * the first definition if there is 1 result * one of the definitions if there are multiple results, and the ambiguities can be resolved * an {@link AmbiguousDefinition} if there are multiple results and the ambiguities could * not be resolved */ static IDefinition getSingleResult (ICompilerProject project, List<IDefinition> defs) { IDefinition def; switch (defs.size()) { case 0: // No definition found! def = null; break; case 1: // found single definition! def = defs.get(0); assert def.isInProject(project); break; default: IDefinition d = AmbiguousDefinition.resolveAmbiguities(project, defs); if (d != null) def = d; else def = AmbiguousDefinition.get(); } return def; } /** * Is it ok to skip this scope, and just ask the containing scope to perform * the lookup. This is possible if this scope does not contribute to the set * of open namespaces, and if we have no definitions with the simple name we * are looking for. * * @return true if we can just ask the containing scope to perform the * lookup */ protected boolean canDelegateLookupToContainingScope(String name) { return namespaceSetSameAsContainingScopeNamespaceSet() && getContainingScope() != null && getLocalDefinitionSetByName(name) == null; } /** * The main public entry point for the findprop operation in the compiler * with an explicit qualifier namespace. This method uses the * {@link ASScopeCache} to improve performance. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param qual {@link INamespaceDefinition} which must match the qualifier * namespace of the found {@link IDefinition}. * @param baseName base name of the property we are looking for. * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @return A single {@link IDefinition} to which the specified qualifier and * base name resolves to in this scope, or null. Null is returned when no * definition is found <b>and</b> when more than one definition is found. */ public IDefinition findPropertyQualified(ICompilerProject project, INamespaceDefinition qual, String baseName, DependencyType dt) { return findPropertyQualified(project, qual, baseName, dt, false); } /** * The main public entry point for the findprop operation in the compiler * with an explicit qualifier namespace. This method uses the * {@link ASScopeCache} to improve performance. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param qual {@link INamespaceDefinition} which must match the qualifier * namespace of the found {@link IDefinition}. * @param baseName base name of the property we are looking for. * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @return A single {@link IDefinition} to which the specified qualifier and * base name resolves to in this scope, or null. Null is returned when no * definition is found <b>and</b> when more than one definition is found. */ public IDefinition findPropertyQualified(ICompilerProject project, Predicate<IDefinition> additional, INamespaceDefinition qual, String baseName, DependencyType dt) { if( qual == null ) return null; NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, ImmutableSet.of(qual)); Predicate<IDefinition> pred = Predicates.and(additional, nsPred); List<IDefinition> defs = new ArrayList<IDefinition>(); FilteredCollection<IDefinition> filteredCollection = new FilteredCollection<IDefinition>(pred, defs); findProperty(filteredCollection, (CompilerProject)project, baseName, nsPred, dt, false); return getSingleResult(project, defs); } /** * The main public entry point for the findprop operation in the compiler * with an explicit qualifier namespace. This method uses the * {@link ASScopeCache} to improve performance. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param qual The qualifier(s) to use to lookup the property. * @param baseName base name of the property we are looking for. * @param canEscapeWith should this lookup find definitions that occur * outside of a containing with scope * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @return A single {@link IDefinition} to which the specified qualifier and * base name resolves to in this scope, or null. Null is returned when no * definition is found <b>and</b> when more than one definition is found. */ public IDefinition findPropertyQualified(ICompilerProject project, INamespaceDefinition qual, String baseName, DependencyType dt, boolean canEscapeWith) { assert baseName.indexOf('.') == -1 : "baseName must not be any sort of qname"; // Can't find a property if we don't know what its qualifier is if( qual == null ) return null; CompilerProject compilerProject = (CompilerProject)project; ASScopeCache scopeCache = compilerProject.getCacheForScope(this); IDefinition definition = scopeCache.findPropertyQualified(qual, baseName, dt); return filterWith(definition, canEscapeWith); } /** * The main public entry point for the findprop operation in the compiler * with an explicit set of qualifier namespaces. This method uses the * {@link ASScopeCache} to improve performance. * * @param project {@link ICompilerProject} whose symbol table is used to * resolve namespace references in the "use namespace" set this scope. * @param baseName base name of the property we are looking for. * @param qual The qualifier(s) to use to lookup the property. * @param canEscapeWith should this lookup find definitions that occur * outside of a containing with scope * @param dt The type of dependency that should be added to the dependency * graph when resolving this reference across a compilation unit boundary. * @return A single {@link IDefinition} to which the specified qualifier and * base name resolves to in this scope, or null. Null is returned when no * definition is found <b>and</b> when more than one definition is found. */ public IDefinition findPropertyQualified(ICompilerProject project, IQualifiers qual, String baseName, DependencyType dt, boolean canEscapeWith) { if( qual == null || qual.getNamespaceCount() == 0 ) return null ; if( qual.getNamespaceCount() == 1 ) { return findPropertyQualified(project, qual.getFirst(), baseName, dt, canEscapeWith); } else { List<IDefinition> defs = findProperty((CompilerProject)project, baseName, qual.getNamespaceSet(), dt); return filterWith(getSingleResult(project, defs), canEscapeWith); } } /** * Helper method to get the namespace set to use for a super reference. This * will replace the protected namespace for this class with the protected * namespace for the super class in the returned namespace set. * * @param project project used to resolve namespaces * @param superDef the IDefinition representing the base class * @return The correct namespace set to use for a super reference */ public Set<INamespaceDefinition> getNamespaceSetForSuper(ICompilerProject project, IDefinition superDef) { Set<INamespaceDefinition> nsSet = getNamespaceSet(project); return adjustNamespaceSetForSuper(superDef, nsSet); } /** * Adjust the namespace set passed in so it's the right set for a super * access. This will replace the protected namespace for this class with the * protected namespace for the super class in the returned namespace set. * * @param superDef the IDefinition representing the base class * @param nsSet the namespace set to adjust * @return The correct namespace set to use for a super reference */ public Set<INamespaceDefinition> adjustNamespaceSetForSuper(IDefinition superDef, Set<INamespaceDefinition> nsSet) { ClassDefinitionBase containingClass = getContainingClass(); if (superDef instanceof ClassDefinition && nsSet.contains(containingClass.getProtectedNamespaceReference())) { Set<INamespaceDefinition> adjustedSet = new LinkedHashSet<INamespaceDefinition>(); adjustedSet.addAll(nsSet); adjustedSet.remove(containingClass.getProtectedNamespaceReference()); adjustedSet.add(((ClassDefinition)superDef).getProtectedNamespaceReference()); return adjustedSet; } return nsSet; } /** * Helper method to return the ClassDefinition this scope is inside of, if * there is one. * * @return the ClassDefinition that contains this scope, or null if this * scope is not in a ClassDefinition */ public ClassDefinitionBase getContainingClass() { ASScope scope = this; ScopedDefinitionBase sdb = null; while (scope != null && sdb == null) { // Walk up the scope chain until we find the first scope with a definition. // stuff like catch, or with scopes will have no definition associated with them sdb = scope.getDefinition(); scope = scope.getContainingScope(); } if (sdb instanceof ClassDefinitionBase) return (ClassDefinitionBase)sdb; else if (sdb != null) return (ClassDefinitionBase)sdb.getAncestorOfType(ClassDefinitionBase.class); return null; } public ASFileScope getFileScope() { ASScope scope = this; while (!(scope instanceof ASFileScope)) { scope = scope.getContainingScope(); // instantiated Vectors may not have containing scopes if (scope == null) break; } return (ASFileScope)scope; } /** * Get's the {@link IWorkspace} in which this {@link ASScope} lives. * * @return The {@link IWorkspace} in which this {@link ASScope} lives. */ public IWorkspace getWorkspace() { return getFileScope().getWorkspace(); } public String getContainingSourcePath(String qName, ICompilerProject project) { ASScope containingScope = getContainingScope(); if (containingScope != null) return containingScope.getContainingSourcePath(qName, project); return null; } /** * Determine if any of the definitions in this scope are Bindable * * @return true, if any non-static definitions in this scope are explicitly marked * bindable, false if there are none. */ public boolean hasAnyBindableDefinitions() { for (IDefinitionSet set : getAllLocalDefinitionSets()) { int n = set.getSize(); for (int i = 0; i < n; i++) { IDefinition d = set.getDefinition(i); if (d.isBindable() && !d.isStatic()) return true; } } return false; } /** * Get's the {@link ScopedDefinitionBase} that contains this scope. This * method differs from {@link #getDefinition()} in that this method will * walk up the scope chain. * * @return The {@link ScopedDefinitionBase} that contains this scope */ public IScopedDefinition getContainingDefinition() { // sub-classes override this method. // This class just returns the definition attached this scope. return containingDefinition; } /** * Makes this scope be the containing scope of the specified anonymous * function. Anonymous functions do not get added to scopes, but they do * need to know which scope they are inside of. */ public void setAsContainingScopeOfAnonymousFunction(FunctionDefinition anonymousFunction) { anonymousFunction.setContainingScope(this); } /** * Add a dependency to the given builtintype, from the compilation unit which contains this scope * @param project the active project * @param builtinType the builtin type to depend on * @param dependencyType the type of dependency to add */ public void addDependencyOnBuiltinType(ICompilerProject project, IASLanguageConstants.BuiltinType builtinType, DependencyType dependencyType) { // Just proxy up to the file scope, since dependencies are from CompilationUnit to CompilationUnit if( containingScope != null ) containingScope.addDependencyOnBuiltinType(project, builtinType, dependencyType); } /** * Implementation of addDependencyOnBuiltinType that will actually add the dependency. * This will only be called if there is a cache miss. * @param project the active project * @param builtinType the builtin type to depend on * @param dependencyType type of dependency to add */ void addDependencyOnBuiltinTypeImpl(CompilerProject project, IASLanguageConstants.BuiltinType builtinType, DependencyType dependencyType) { IDefinition definition = project.getBuiltinType(builtinType); if( definition != null && builtinType != IASLanguageConstants.BuiltinType.ANY_TYPE && builtinType != IASLanguageConstants.BuiltinType.VOID) { ASProjectScope projectScope = project.getScope(); ICompilationUnit from = projectScope.getCompilationUnitForScope(this); ICompilationUnit to = projectScope.getCompilationUnitForDefinition(definition); String qname = definition.getQualifiedName(); project.addDependency(from, to, dependencyType, qname); } } }