All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.royale.compiler.internal.scopes.ASScope Maven / Gradle / Ivy

The newest version!
/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.royale.compiler.internal.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.royale.compiler.common.ASImportTarget;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.common.IImportTarget;
import org.apache.royale.compiler.common.NodeReference;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.IQualifiers;
import org.apache.royale.compiler.definitions.IScopedDefinition;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.internal.definitions.AmbiguousDefinition;
import org.apache.royale.compiler.internal.definitions.ClassDefinition;
import org.apache.royale.compiler.internal.definitions.ClassDefinitionBase;
import org.apache.royale.compiler.internal.definitions.FunctionDefinition;
import org.apache.royale.compiler.internal.definitions.InterfaceDefinition;
import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
import org.apache.royale.compiler.internal.definitions.ScopedDefinitionBase;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.internal.tree.as.ScopedBlockNode;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.scopes.IDefinitionSet;
import org.apache.royale.compiler.tree.as.IForLoopNode;
import org.apache.royale.compiler.tree.as.IScopedNode;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.compiler.workspaces.IWorkspace;
import org.apache.royale.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];
    
    private static IForLoopNode[] EMPTY_LOOPCHECK_ARRAY = new IForLoopNode[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 aliasToImportQualifiedName = null;

    private Set packageNames = null;

    private Object usedNamespaces = null;

    private NamespaceDefinition.INamespaceDirective firstNamespaceDirective;

    private NamespaceDefinition.INamespaceDirective lastNamespaceDirective;

    private boolean inWith = false;
    
    private Object loopChecks = null;
    public boolean getHasLoopCheck(){
        return loopChecks != null;
    }
    public void addLoopCheck(IForLoopNode value){
        if (loopChecks == null) loopChecks = CheapArray.create(1);
        else {
            int len = CheapArray.size(loopChecks);
            boolean exitEarly = false;
            //sometimes the same for loop exists as more than one instance for the same source locations.
            //this seems to happen somtimes in mxml code blocks.
            //The following makes sure that only one instance representing the same source code location is tracked
            //for ArrayLike inspection
            for (int i=0; i();
        }
        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();
        }

        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>();

                Set s = importedNames.get(defName);
                if (s == null)
                {
                    s = new LinkedHashSet();
                    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 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 getExplicitImportQualifiers(CompilerProject project, String name)
    {
        Set nsSet = new LinkedHashSet();

        Workspace workspace = project.getWorkspace();

        getContainingScopeExplicitImports(project, name, nsSet);

        if (importedNames != null)
        {
            // Was it an import in this scope
            Set packages = importedNames.get(name);
            if (packages != null)
            {
                for (String s : packages)
                {
                    nsSet.add(workspace.getPackageNamespaceDefinitionCache().get(s, false));
                }
            }
        }
        return nsSet.size() > 0 ? nsSet : Collections. 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 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> 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 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 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 getNamespaceSetForNameImpl(ICompilerProject project, String name)
    {
        if (namespaceSetSameAsContainingScopeNamespaceSet())
        {
            ASScope containingScope = getContainingScope();
            if (containingScope != null)
            {
                return containingScope.getNamespaceSetForName(project, name);
            }
        }

        Set 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 additionalNamespaces = getExplicitImportQualifiers((CompilerProject)project, name);
        if (additionalNamespaces != null)
        {
            Set newSet = new LinkedHashSet();
            newSet.addAll(openNamespaces);
            newSet.addAll(additionalNamespaces);
            return newSet;
        }
        else
        {
            return openNamespaces;
        }
    }

    /**
     * Computes and returns the namespace set for this scope.
     * 

* 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 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. *

* The returned set should not be modified. * * @param project * @return The namespace set for this scope. The returned set should not be * modified */ Set getNamespaceSetImpl(ICompilerProject project) { if (namespaceSetSameAsContainingScopeNamespaceSet()) { ASScope containingScope = getContainingScope(); if (containingScope != null) { return containingScope.getNamespaceSetImpl(project); } } CompilerProject compilerProject = (CompilerProject)project; IWorkspace workspace = compilerProject.getWorkspace(); Set result = new LinkedHashSet(); // 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 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 result) { ASScope containingScope = this.getContainingScope(); if (containingScope != null) { result.addAll(containingScope.getNamespaceSet(compilerProject)); } } public void addImplicitOpenNamespaces(CompilerProject compilerProject, Set 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 defs, Set 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 defs, Set 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 getPropertiesByNameForMemberAccess(CompilerProject project, String memberName, Set namespaceSet) { // Get the collection of all properties. List result = new ArrayList(); 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 namespaceSet) { List defs = new ArrayList(); 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. *

* If this scope is not for a class or interface definition then only * definitions in this scope are considered. *

* 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. *

* * @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 defs, String baseName, Set namespaceSet, boolean findAll) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, namespaceSet); Collection filteredDefs = new FilteredCollection(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. *

* 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) * *

* If this scope is not for a class or interface definition then only * definitions in this scope are considered. *

* 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. *

* * @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 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 getNamespaceSetForMemberAccess(ICompilerProject project, IDefinition def, boolean isSuperRef) { Set 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 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 additional, boolean isSuperRef) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, getNamespaceSetForMemberAccess(project, def, isSuperRef)); Predicate 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 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 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 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 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 defs = new ArrayList(1); defScope.getPropertyForMemberAccess(project, new FilteredCollection(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 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 findProperty(CompilerProject project, String baseName, Set 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 findProperty(CompilerProject project, String baseName, Predicate additional, Set namespaceSet, DependencyType dt) { return findProperty(project, baseName, additional, namespaceSet, dt, false); } /** * This is the core findproperty() method. It implements the * equivalent of the findprop AVM instruction in Royale. *

* The algorithm searches up the scope chain, starting with this scope, for * definitions with the specified base name and namespace set. *

* 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. *

* If the findAll parameter is false, the search * stops with the first scope that has one or more matching definition; if * it is true, 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 accumulator, CompilerProject project, String baseName, Set namespaceSet, DependencyType dt, boolean findAll) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, namespaceSet); FilteredCollection filteredCollection = new FilteredCollection(nsPred, accumulator); findProperty(filteredCollection, project, baseName, nsPred, dt, findAll); } /** * This is the implementation of the various findproperty() methods. It implements the * equivalent of the findprop AVM instruction in Royale. *

* The algorithm searches up the scope chain, starting with this scope, for * definitions with the specified base name. *

* 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}. *

* 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. *

* If the findAll parameter is false, the search * stops with the first scope that has one or more matching definition; if * it is true, 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 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 " + baseName; // 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 toRemove = new ArrayList(); 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 toRemove = new ArrayList(); 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 addStaticProtectedNS(Set namespaceSet) { if (namespaceSet == null) return null; // The keys are URI strings. The values are "protected" namespace definitions. final Map staticProtectedNamespaces = new HashMap(); final Set protectedNamespaces = new HashSet(); 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 addedStaticProtectedNamespaces = new HashSet(); for (final INamespaceDefinition.IProtectedNamespaceDefinition protectedNamespace : protectedNamespaces) { if (!staticProtectedNamespaces.containsKey(protectedNamespace.getURI())) { addedStaticProtectedNamespaces.add( NamespaceDefinition.createStaticProtectedNamespaceDefinition( protectedNamespace.getURI())); } } final Set result; if (addedStaticProtectedNamespaces.isEmpty()) { result = namespaceSet; } else { result = new HashSet(); 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 Royale. * * @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 findProperty(CompilerProject project, String baseName, Set namespaceSet, DependencyType dt, boolean findAll) { ArrayList defs = new ArrayList(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 findProperty(CompilerProject project, String baseName, Predicate additional, Set namespaceSet, DependencyType dt, boolean findAll) { ArrayList defs = new ArrayList(1); NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, namespaceSet); Predicate pred = Predicates.and(additional, nsPred); FilteredCollection filteredCollection = new FilteredCollection(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 and 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 and 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 " + baseName; CompilerProject compilerProject = (CompilerProject)project; ASScopeCache scopeCache = compilerProject.getCacheForScope(this); return filterWith(scopeCache.findProperty(baseName, dt, canEscapeWith), 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 additional, DependencyType dt, boolean canEscapeWith) { Set nsSet = getNamespaceSetForName(project, baseName); return findProperty(project, baseName, additional, nsSet, dt, canEscapeWith); } public IDefinition findProperty (ICompilerProject project, String baseName, Predicate additional, Set nsSet, DependencyType dt, boolean canEscapeWith) { NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, nsSet); List storage = new ArrayList(); Predicate pred = Predicates.and(additional, nsPred); FilteredCollection defs = new FilteredCollection(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 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, false); 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 and 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 and when more than one definition is found. */ public IDefinition findPropertyQualified(ICompilerProject project, Predicate additional, INamespaceDefinition qual, String baseName, DependencyType dt) { if( qual == null ) return null; NamespaceSetPredicate nsPred = new NamespaceSetPredicate(project, ImmutableSet.of(qual)); Predicate pred = Predicates.and(additional, nsPred); List defs = new ArrayList(); FilteredCollection filteredCollection = new FilteredCollection(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 and 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 " + baseName; // 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 and 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 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 getNamespaceSetForSuper(ICompilerProject project, IDefinition superDef) { Set 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 adjustNamespaceSetForSuper(IDefinition superDef, Set nsSet) { ClassDefinitionBase containingClass = getContainingClass(); if (superDef instanceof ClassDefinition && nsSet.contains(containingClass.getProtectedNamespaceReference())) { Set adjustedSet = new LinkedHashSet(); 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); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy