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

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

/*
 *
 *  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.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.IScopedDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.internal.common.Counter;
import org.apache.royale.compiler.internal.definitions.ClassDefinition;
import org.apache.royale.compiler.internal.definitions.DefinitionBase;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.scopes.IASScope;
import org.apache.royale.compiler.scopes.IDefinitionSet;
import org.apache.royale.compiler.tree.as.IScopedNode;
import org.apache.royale.compiler.units.ICompilationUnit;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ForwardingCollection;

/**
 * ASScopeBase is the abstract base class for all lexical scopes. It has three
 * concrete subclasses: ASProjectScope for project scopes,
 * ASFileScope for file scopes, and ASScope for class,
 * interface, function, and with scopes.
 * 

* The primary purpose of a lexical scope is to store a set of definitions that * are potentially visible if the scope is in the chain of scopes used to * resolve an identifier. */ public abstract class ASScopeBase implements IASScope { public static final Set allNamespacesSet = null; /** * Used only for debugging, as part of {@link this.toString()}. */ private static void indent(StringBuilder sb, int level) { // Indent six spaces for each scope-nesting level. for (int i = 0; i < level; i++) { sb.append(" "); } } /** * Constructor */ public ASScopeBase() { // Start out with an empty definiton store until a definition is added. definitionStore = EmptyDefinitionStore.SINGLETON; if (Counter.COUNT_SCOPES) countScopes(); } /** * Storage for definitions in this scope, * organized into sets of definitions with the same base name. */ protected IDefinitionStore definitionStore; /** * Minimizes the memory used by this scope. *

* The definition store does not get compacted, * but subclasses override this to compact other data structures. */ public void compact() { } /** * Adds the specified definition to this scope. * * @param definition The {@link IDefinition} to be added. */ public void addDefinition(IDefinition definition) { if (definition == null) return; addDefinitionToStore(definition); if (definition instanceof DefinitionBase) ((DefinitionBase)definition).setContainingScope(this); } /** * Helper method called by {@link #addDefinition}(). *

* It handles actually adding the definition to the store. * It first tries to add it to the current store. * If it won't fit, it creates a bigger store and adds it to that. * * @param definition The {@link IDefinition} to be added. */ protected void addDefinitionToStore(IDefinition definition) { if (!definitionStore.add(definition)) { definitionStore = definitionStore.createLargerStore(); definitionStore.add(definition); } } /** * Removes the specified definition from this scope. * * @param definition The {@link IDefinition} to be removed. */ public void removeDefinition(IDefinition definition) { removeDefinitionFromStore(definition); // TODO It seems like a good idea to null out the containing // scope of a definition after we remove it from that scope. // But this makes various tests fail. // if (definition instanceof DefinitionBase) // ((DefinitionBase)definition).setContainingScope(null); } /** * Helper method called by {@link #removeDefinition}(). *

* It handles actually removing the definition from the store. * It does not bother to check whether the store could be * downgraded with one that has a smaller capacity. * * @param definition The {@link IDefinition} to be added. */ protected void removeDefinitionFromStore(IDefinition definition) { definitionStore.remove(definition); } @Override public IScopedNode getScopeNode() { return null; } @Override public IScopedDefinition getDefinition() { return null; } @Override public IDefinitionSet getLocalDefinitionSetByName(String baseName) { return definitionStore.getDefinitionSetByName(baseName); } @Override public Collection getAllLocalNames() { return definitionStore.getAllNames(); } @Override public Collection getAllLocalDefinitionSets() { return definitionStore.getAllDefinitionSets(); } @Override public Collection getAllLocalDefinitions() { return definitionStore.getAllDefinitions(); } /** * Adds definitions with the specified base name whose namespaces match the * specified namespace set to the specified collection of definitions. *

* If more that one definition is added to the collection by this method, * then the reference is ambiguous * * @param project {@link ICompilerProject} 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 baseName Name of property to find. * @param namespaceSet Namespace set in which the qualifier of any matching * definition must exist to be considered a match. */ public final void getLocalProperty(ICompilerProject project, Collection defs, String baseName, Set namespaceSet) { getLocalProperty(project, defs, baseName, namespaceSet, true); } /** * Adds definitions with the specified base name whose namespaces match the * specified namespace set to the specified collection of definitions. *

* If more that one definition is added to the collection by this method, * then the reference is ambiguous * * @param project {@link ICompilerProject} 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 baseName Name of property to find. * @param namespaceSet Namespace set in which the qualifier of any matching * definition must exist to be considered a match. * @param getContingents If true, non-contingent definitions are ignored. If * false, contingent definitions are ignored. */ // TODO Remove the override in ASProjectScope // and make this final again when we start using Set. protected final void getLocalProperty(ICompilerProject project, Collection defs, String baseName, Set namespaceSet, boolean getContingents) { defs = new FilteredCollection(new NamespaceSetPredicate(project, namespaceSet), defs); getLocalProperty(project, defs, baseName, getContingents); } /** * Adds definitions with the specified base name to the specified collection of definitions. *

* If additional constraints on the definitions are required, then the Collection passed in should * implement those. Most commonly, this will be a {@link FilteredCollection} with a {@link NamespaceSetPredicate} * as the predicate. *

* If more that one definition is added to the collection by this method, * then the reference is ambiguous * * @param project {@link ICompilerProject} 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 baseName Name of property to find. * @param getContingents If true, non-contingent definitions are ignored. If * false, contingent definitions are ignored. */ protected final void getLocalProperty(ICompilerProject project, Collection defs, String baseName, boolean getContingents) { IDefinitionSet defSet = getLocalDefinitionSetByName(baseName); if (defSet != null) { int nDefs = defSet.getSize(); for (int i = 0; i < nDefs; ++i) { IDefinition definition = defSet.getDefinition(i); if ((!definition.isContingent() || (getContingents && isContingentDefinitionNeeded(project, definition)))) { defs.add(definition); } } } } /** * Check whether there is a matching definition in the class hierarchy * already defined in which case the contingent definition is not needed. * * @param project The compiler project. * @param definition A definition. */ public boolean isContingentDefinitionNeeded(ICompilerProject project, IDefinition definition) { assert (definition.isContingent()) : "contingentNeeded() called on non-contingent definition"; IASScope containingScope = definition.getContainingScope(); // for now contingent definitions are only ever class members, so the containing scope def must // be a ClassDefinition. In future this rule can be changed, but code to search the class // hierarchy will become more complex, as the whole definition resolution code needs to be updated assert (containingScope.getDefinition() instanceof ClassDefinition) : "contingent definitions containing scope must be a Class"; ClassDefinition containingType = (ClassDefinition)containingScope.getDefinition(); String contingentName = definition.getBaseName(); for (ITypeDefinition type : definition.isStatic() ? containingType.staticTypeIterable(project, false) : containingType.typeIteratable(project, false)) { ASScope typeScope = (ASScope)type.getContainedScope(); List defs = new LinkedList(); typeScope.getLocalProperty(project, defs, contingentName, null, false); // found a non contingent definition, so this contingent def is // not needed. if (!defs.isEmpty()) return false; } return true; } /** * Adds all local definitions from this scope to the specified collections * of definitions that have a namespace qualifier in the specified * definition set. * * @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. * @param extraNamespace A single extra {@link INamespaceDefinition} that is * considered part of the namespace set by this method. This is used when * resolving protected definitions in a class scope. */ public void getAllLocalProperties(CompilerProject project, Collection defs, Set namespaceSet, INamespaceDefinition extraNamespace) { for (IDefinitionSet definitionSet : getAllLocalDefinitionSets()) { for (int i = 0; i < definitionSet.getSize(); ++i) { // we don't want do add definitions that are promises. // when this function is called, any definitions that are still promises // are never going to be resolved - they represent bad code IDefinition definition = definitionSet.getDefinition(i); if (!(definition instanceof ASProjectScope.DefinitionPromise) && (!definition.isContingent() || isContingentDefinitionNeeded(project, definition))) { if ((extraNamespace != null) && (extraNamespace == definition.getNamespaceReference())) { defs.add(definition); } else if (namespaceSet == null) { defs.add(definition); } else { INamespaceDefinition ns = definition.resolveNamespace(project); if (ns != null && (extraNamespace != null) && ((ns == extraNamespace) || (ns.equals(extraNamespace)))) { defs.add(definition); } else if (defs != null && namespaceSet.contains(ns)) { defs.add(definition); } } } } } } /** * Collection that ignores added items that for which a predicate returns * false. * * @param Type of items in the collection */ public static final class FilteredCollection extends ForwardingCollection { /** * Constructor * * @param predicate Predicate used to filter items as they are added * @param storage Collection to which items are added if the predicate * returns true. */ public FilteredCollection(Predicate predicate, Collection storage) { this.predicate = predicate; this.storage = storage; } private final Predicate predicate; private final Collection storage; @Override protected Collection delegate() { return storage; } @Override public boolean add(T element) { if (predicate.apply(element)) return super.add(element); return false; } @Override public boolean addAll(Collection collection) { return super.addAll(Collections2.filter(collection, predicate)); } } /** * Used only in asserts. */ public boolean verify() { // Verify each definition in this scope. Collection names = getAllLocalNames(); for (String name : names) { // Don't call getDefinitionSetByName() on the scope, because // for a project scope this would actualize every DefinitionPromise. IDefinitionSet definitionSet = definitionStore.getDefinitionSetByName(name); int n = definitionSet.getSize(); for (int i = 0; i < n; i++) { IDefinition definition = definitionSet.getDefinition(i); ((DefinitionBase)definition).verify(); } } return true; } /** * For debugging only. This method displays the definitions contained in * this scope, alphabetically by name. If the definitions have scopes, those * scopes are recursively displayed in an indented fashion. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); buildStringRecursive(sb, 0); return sb.toString(); } /** * Used only for debugging, as part of {@link this.toString()}. */ private void buildStringRecursive(StringBuilder sb, int level) { // Get the names of all the definitions in this scope // and alphabetize them. String[] names = definitionStore.getAllNames().toArray(new String[0]); Arrays.sort(names); // Display a header identifying the scope. indent(sb, level); sb.append(toStringHeader()); sb.append('\n'); for (String name : names) { indent(sb, level); sb.append(' '); sb.append(' '); sb.append(name.length() > 0 ? name : "\"\""); sb.append('\n'); // Get the set of definitions with this name. // Don't call getDefinitionSetByName() on the scope, because // for a project scope this would actualize every DefinitionPromise. IDefinitionSet set = definitionStore.getDefinitionSetByName(name); int n = set.getSize(); for (int i = 0; i < n; i++) { IDefinition d = set.getDefinition(i); indent(sb, level); sb.append(' '); sb.append(' '); sb.append(' '); sb.append(' '); ((DefinitionBase)d).buildString(sb, false); sb.append('\n'); // If the definition has a scope, display that scope recursively. if (d instanceof IScopedDefinition) { ASScopeBase containedScope = (ASScopeBase)((IScopedDefinition)d).getContainedScope(); if (containedScope != null) containedScope.buildStringRecursive(sb, level + 1); } } } } /** * For debugging only. Called by toString() to return the header that is * displayed at the beginning. */ protected String toStringHeader() { return getClass().getSimpleName(); } /** * Counts various types of scopes that are created, * as well as the total number of scopes. */ private void countScopes() { Counter counter = Counter.getInstance(); counter.incrementCount(getClass().getSimpleName()); counter.incrementCount("scopes"); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy