| /* |
| * |
| * 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 org.apache.royale.compiler.common.DependencyType; |
| import org.apache.royale.compiler.config.CompilerDiagnosticsConstants; |
| import org.apache.royale.compiler.constants.IASLanguageConstants; |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.definitions.IFunctionDefinition; |
| import org.apache.royale.compiler.definitions.IInterfaceDefinition; |
| import org.apache.royale.compiler.definitions.INamespaceDefinition; |
| import org.apache.royale.compiler.definitions.ITypeDefinition; |
| import org.apache.royale.compiler.definitions.references.IResolvedQualifiersReference; |
| import org.apache.royale.compiler.internal.definitions.AmbiguousDefinition; |
| import org.apache.royale.compiler.internal.definitions.ClassDefinitionBase; |
| import org.apache.royale.compiler.internal.definitions.ConstantDefinition; |
| import org.apache.royale.compiler.internal.definitions.TypeDefinitionBase; |
| import org.apache.royale.compiler.internal.projects.CompilerProject; |
| import org.apache.royale.compiler.projects.ICompilerProject; |
| import org.apache.royale.compiler.units.ICompilationUnit; |
| |
| import com.google.common.collect.MapMaker; |
| |
| import java.lang.ref.SoftReference; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentMap; |
| |
| /** |
| * Class to manage cached lookups in a given project. Each scope object will |
| * have one of these per project the scope object is used in. This class is |
| * intended to provide thread safe access to the various caches it maintains. |
| * The caches will be maintained via SoftReferences - this is meant to make the |
| * caches stick around unless the VM really needs the memory back. The caches |
| * can be rebuilt on the fly, so in a worst case scenario where the VM is |
| * constantly low on memory, the results should be correct, but performance will |
| * be slower. |
| */ |
| public class ASScopeCache |
| { |
| /** |
| * The concurrency level to use for the various ConcurrentMaps in use by |
| * this cache. If the concurrency level is too high, you waste time and |
| * space, if it is too low, then you have contention on updates of the map. |
| * 4 seems to be a good tradeoff performance wise, and it is unlikely that |
| * any individual scope cache is being updated from more than 4 threads |
| * simultaneously. If our threading strategy changes (like if we make the CG |
| * multithreaded within one file), we may want to revisit this setting. |
| */ |
| private static final int CONCURRENCY_LEVEL = 4; |
| private static final MapMaker mapMaker = new MapMaker() |
| .concurrencyLevel(CONCURRENCY_LEVEL); |
| |
| public ASScopeCache(CompilerProject project, ASScope scope) |
| { |
| this.scope = scope; |
| this.project = project; |
| } |
| |
| private final ASScope scope; |
| private final CompilerProject project; |
| |
| /** |
| * Cache results of unqualified lookups over the scope chain |
| * (ASScopeBase.findProperty). This is for caching the results of |
| * ASScope.findProperty(). |
| */ |
| private SoftReference<ConcurrentMap<String, IDefinition>> findPropCache; |
| |
| /** |
| * Cache results of lookups of qualified names over the scope chain |
| * (ASScopeBase.findPropertyQualified). |
| */ |
| private SoftReference<ConcurrentMap<QName, IDefinition>> findPropQualifiedCache; |
| |
| /** |
| * Cache the set of open namespaces |
| */ |
| private SoftReference<Set<INamespaceDefinition>> openNamespaceCache = null; |
| |
| /** |
| * Cache the open namespace set per name |
| */ |
| private SoftReference<ConcurrentMap<String, Set<INamespaceDefinition>>> namespacesForNameCache; |
| |
| private SoftReference<ConcurrentMap<IResolvedQualifiersReference, IDefinition>> multinameLookupCache; |
| |
| /** |
| * Cache the compile time values of constants |
| */ |
| private SoftReference<ConcurrentMap<IDefinition, Object>> constValueLookupCache; |
| |
| /** |
| * Cache the needs Event dispatch flag |
| */ |
| private Boolean needsEventDispatcherCache; |
| |
| /** |
| * Cache the extended or implemented interfaces of an interface or class. |
| */ |
| private SoftReference<IInterfaceDefinition[]> interfacesCache; |
| |
| /** |
| * Cache the builtin types we've already added dependencies on |
| */ |
| private SoftReference<Set<IASLanguageConstants.BuiltinType>> builtinTypeDependencyCache; |
| |
| /** |
| * Version of findProperty that uses a cache. Checks the cache first, and |
| * only queries the scope if the we don't have a cached result. |
| * |
| * @param scope The scope to perform the lookup in |
| * @param name Name of the property to find |
| * @param cache ASDefinitionCache to use for the lookup - this is only used |
| * to get at the ICompilerProject |
| * @param dt Which type of dependency to introduce when we do the lookup |
| * @return The IDefinition for the property, or null if it wasn't found |
| */ |
| IDefinition findProperty(String name, DependencyType dt, boolean favorTypes) |
| { |
| ConcurrentMap<String, IDefinition> map = getScopeChainMap(); |
| |
| IDefinition result = map.get(name); |
| if (result != null) |
| { |
| // We found a cached result - we're done |
| // after making sure it has a dependency |
| if (result instanceof ITypeDefinition) |
| { |
| ICompilationUnit from = scope.getFileScope().getCompilationUnit(); |
| assert result.isInProject(project); |
| |
| String qname = result.getQualifiedName(); |
| ICompilationUnit to = ((ASProjectScope)project.getScope()).getCompilationUnitForDefinition(result); |
| if (to == null && !(qname.contentEquals("void") || qname.contentEquals("*"))) |
| System.out.println("No compilation unit for " + qname); |
| if (to != null) |
| project.addDependency(from, to, dt, qname); |
| } |
| return result; |
| } |
| |
| // It is possible for 2+ threads to get in here for the same name. |
| // This is intentional - the worst that happens is that we duplicate the resolution work |
| // the benefit is that we avoid any sort of locking, which was proving expensive (time wise, |
| // and memory wise). |
| |
| boolean wasAmbiguous = false; |
| IDefinition def = null; |
| Set<INamespaceDefinition> namespaceSet = scope.getNamespaceSetForName(project, name); |
| // Look for the definition in the scope |
| List<IDefinition> defs = scope.findProperty(project, name, namespaceSet, dt); |
| 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: |
| wasAmbiguous = true; |
| IDefinition d = AmbiguousDefinition.resolveAmbiguities(project, defs, favorTypes); |
| if (d != null) |
| def = d; |
| else { |
| if (defs.size() == 2) |
| { |
| def = project.doubleCheckAmbiguousDefinition(scope, name, defs.get(0), defs.get(1)); |
| if (def != null) |
| return def; |
| } |
| def = AmbiguousDefinition.get(); |
| } |
| } |
| if (def != null) |
| { |
| assert def.isInProject(project); |
| assert result == null; |
| // If we have a non-null dependency type |
| // then we can cache the result of the name resolution. |
| // If the dependency type is null we can't cache the name |
| // resolution result, because the name resolution cache will not |
| // be properly invalidated when the file containing the definition changes. |
| if (dt != null && !wasAmbiguous) |
| { |
| result = map.putIfAbsent(name, def); |
| if (result == null) |
| result = def; |
| } |
| else |
| { |
| result = def; |
| } |
| } |
| return result; |
| |
| } |
| |
| private ConcurrentMap<String, IDefinition> getScopeChainMap() |
| { |
| ConcurrentMap<String, IDefinition> map = findPropCache != null ? findPropCache.get() : null; |
| if (map == null) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in getScopeChainMap"); |
| synchronized (this) |
| { |
| // Check again, in case another thread updated the map first |
| map = findPropCache != null ? findPropCache.get() : null; |
| if (map == null) |
| { |
| map = mapMaker.<String, IDefinition> makeMap(); |
| findPropCache = new SoftReference<ConcurrentMap<String, IDefinition>>(map); |
| } |
| } |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in getScopeChainMap"); |
| } |
| return map; |
| } |
| |
| private ConcurrentMap<QName, IDefinition> getQualifiedScopeChainMap() |
| { |
| ConcurrentMap<QName, IDefinition> map = findPropQualifiedCache != null ? findPropQualifiedCache.get() : null; |
| if (map == null) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in getQualifiedScopeChainMap"); |
| synchronized (this) |
| { |
| // Check again, in case another thread updated the map first |
| map = findPropQualifiedCache != null ? findPropQualifiedCache.get() : null; |
| if (map == null) |
| { |
| map = mapMaker.<QName, IDefinition> makeMap(); |
| findPropQualifiedCache = new SoftReference<ConcurrentMap<QName, IDefinition>>(map); |
| } |
| } |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in getQualifiedScopeChainMap"); |
| } |
| return map; |
| } |
| |
| /** |
| * Version of findPropertyQualified that uses a cache. Checks the cache |
| * first, and only queries the scope if the we don't have a cached result. |
| * |
| * @param scope The scope to perform the lookup in |
| * @param name Name of the property to find |
| * @param cache ASDefinitionCache to use for the lookup - this is only used |
| * to get at the ICompilerProject |
| * @param dt Which type of dependency to introduce when we do the lookup |
| * @return The IDefinition for the property, or null if it wasn't found |
| */ |
| IDefinition findPropertyQualified(INamespaceDefinition qualifier, String name, |
| DependencyType dt) |
| { |
| QName qname = new QName(name, qualifier); |
| ConcurrentMap<QName, IDefinition> map = getQualifiedScopeChainMap(); |
| |
| IDefinition result = map.get(qname); |
| if (result != null) |
| { |
| assert result.isInProject(project); |
| // We found a cached result - we're done |
| return result; |
| } |
| |
| // If we get this far, then we did not find a cached entry |
| // It is possible for 2+ threads to get in here for the same name. |
| // This is intentional - the worst that happens is that we duplicate the resolution work |
| // the benefit is that we avoid any sort of locking, which was proving expensive (time wise, |
| // and memory wise). |
| |
| IDefinition def; |
| // Look for the definition in the scope |
| Set<INamespaceDefinition> namespaceSet = Collections.singleton(qualifier); |
| List<IDefinition> defs = scope.findProperty(project, name, namespaceSet, dt); |
| switch (defs.size()) |
| { |
| case 0: |
| def = null; |
| break; |
| |
| case 1: |
| 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(); |
| break; |
| } |
| if (def != null) |
| { |
| assert def.isInProject(project); |
| assert result == null; |
| // If we have a non-null dependency type |
| // then we can cache the result of the name resolution. |
| // If the dependency type is null we can't cache the name |
| // resolution result, because the name resolution cache will not |
| // be properly invalidated when the file containing the definition changes. |
| if (dt != null) |
| { |
| result = map.putIfAbsent(qname, def); |
| if (result == null) |
| result = def; |
| } |
| else |
| { |
| result = def; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Resolves the specified reference to a definition and adds a dependency to |
| * the dependency graph if needed. |
| * <p> |
| * This method is only public so that the implementation of |
| * IResolveQualifiersReference.resolve can call it. This method should only |
| * be called from the implementation of {@link IResolvedQualifiersReference}. |
| * |
| * @param ref The reference to resolve. |
| * @param dt The type of dependency to add if a new edge needs to be added |
| * to the dependency graph. |
| * @return The definition the reference resolves to, null, or the ambiguous |
| * definition. |
| */ |
| public IDefinition findPropertyMultiname(IResolvedQualifiersReference ref, DependencyType dt) |
| { |
| ConcurrentMap<IResolvedQualifiersReference, IDefinition> cache = getMultinameLookupMap(); |
| IDefinition result = cache.get(ref); |
| if (result != null) |
| return result; |
| |
| IDefinition def; |
| |
| // Look for the definition in the scope |
| List<IDefinition> defs = scope.findProperty(project, ref.getName(), ref.getQualifiers(), dt); |
| 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(); |
| } |
| if (def != null) |
| { |
| assert def.isInProject(project); |
| assert result == null; |
| // If we have a non-null dependency type |
| // then we can cache the result of the name resolution. |
| // If the dependency type is null we can't cache the name |
| // resolution result, because the name resolution cache will not |
| // be properly invalidated when the file containing the definition changes. |
| if (dt != null) |
| { |
| result = cache.putIfAbsent(ref, def); |
| if (result == null) |
| result = def; |
| } |
| else |
| { |
| result = def; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Version of getNamespaceSet that caches the results. |
| * |
| * @return the namespace set to use for unqualified lookups in this scope |
| */ |
| Set<INamespaceDefinition> getNamespaceSet() |
| { |
| Set<INamespaceDefinition> nsSet = openNamespaceCache != null ? openNamespaceCache.get() : null; |
| if (nsSet != null) |
| return nsSet; |
| |
| nsSet = scope.getNamespaceSetImpl(project); |
| openNamespaceCache = new SoftReference<Set<INamespaceDefinition>>(nsSet); |
| return nsSet; |
| } |
| |
| private ConcurrentMap<String, Set<INamespaceDefinition>> getNamespacesForNameMap() |
| { |
| ConcurrentMap<String, Set<INamespaceDefinition>> map = namespacesForNameCache != null ? namespacesForNameCache.get() : null; |
| if (map == null) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in getNamespacesForNameMap"); |
| synchronized (this) |
| { |
| // Check again, in case another thread updated the map first |
| map = namespacesForNameCache != null ? namespacesForNameCache.get() : null; |
| if (map == null) |
| { |
| map = mapMaker.<String, Set<INamespaceDefinition>> makeMap(); |
| namespacesForNameCache = new SoftReference<ConcurrentMap<String, Set<INamespaceDefinition>>>(map); |
| } |
| } |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in getNamespacesForNameMap"); |
| } |
| return map; |
| } |
| |
| private ConcurrentMap<IResolvedQualifiersReference, IDefinition> getMultinameLookupMap() |
| { |
| ConcurrentMap<IResolvedQualifiersReference, IDefinition> map = multinameLookupCache != null ? multinameLookupCache.get() : null; |
| if (map == null) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in getMultinameLookupMap"); |
| synchronized (this) |
| { |
| // Check again, in case another thread updated the map first |
| map = multinameLookupCache != null ? multinameLookupCache.get() : null; |
| if (map == null) |
| { |
| map = mapMaker.<IResolvedQualifiersReference, IDefinition> makeMap(); |
| multinameLookupCache = new SoftReference<ConcurrentMap<IResolvedQualifiersReference, IDefinition>>(map); |
| } |
| } |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in getMultinameLookupMap"); |
| } |
| return map; |
| } |
| |
| private ConcurrentMap<IDefinition, Object> getConstantValueLookupMap() |
| { |
| ConcurrentMap<IDefinition, Object> map = constValueLookupCache != null ? constValueLookupCache.get() : null; |
| if (map == null) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in getConstantValueLookupMap"); |
| synchronized (this) |
| { |
| // Check again, in case another thread updated the map first |
| map = constValueLookupCache != null ? constValueLookupCache.get() : null; |
| if (map == null) |
| { |
| map = mapMaker.<IDefinition, Object> makeMap(); |
| constValueLookupCache = new SoftReference<ConcurrentMap<IDefinition, Object>>(map); |
| } |
| } |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in getConstantValueLookupMap"); |
| } |
| return map; |
| } |
| |
| private Set<IASLanguageConstants.BuiltinType> getBuiltinTypeMap() |
| { |
| Set<IASLanguageConstants.BuiltinType> set = builtinTypeDependencyCache != null ? builtinTypeDependencyCache.get() : null; |
| if (set == null) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in getBuiltinTypeMap"); |
| synchronized (this) |
| { |
| // Check again, in case another thread updated the set first |
| set = builtinTypeDependencyCache != null ? builtinTypeDependencyCache.get() : null; |
| if (set == null) |
| { |
| set = Collections.newSetFromMap(mapMaker.<IASLanguageConstants.BuiltinType, Boolean> makeMap()); |
| builtinTypeDependencyCache = new SoftReference<Set<IASLanguageConstants.BuiltinType>>(set); |
| } |
| } |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in getBuiltinTypeMap"); |
| } |
| return set; |
| } |
| |
| /** |
| * Determines if the {@link TypeScope} this cache is associated with needs |
| * an implicit 'implements flash.events.IEventDispatcher' due to the class, |
| * or some of its members being marked bindable. The result of this method |
| * is cached. |
| * <p> |
| * Only |
| * {@link ClassDefinitionBase#needsEventDispatcher(ICompilerProject)} |
| * should call this method. All other code should call |
| * {@link ClassDefinitionBase#needsEventDispatcher(ICompilerProject)}. |
| * |
| * @return true if this class needs to add IEventDispatcher to its interface |
| * list, and should implement the IEventDispatcher methods. |
| */ |
| public boolean needsEventDispatcher() |
| { |
| assert scope instanceof TypeScope : "needsEventDispatcher should only be called on scope cache's for TypeScopes!"; |
| assert scope.getDefinition() instanceof ClassDefinitionBase : "needsEventDispatcher should only be called on scope cache's for the scopes contained by classes!"; |
| Boolean valueObject = needsEventDispatcherCache; |
| if (valueObject != null) |
| return valueObject.booleanValue(); |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in needsEventDispatcher"); |
| synchronized (this) |
| { |
| // Check again, in case another thread updated the value first |
| valueObject = needsEventDispatcherCache; |
| if (valueObject != null) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in needsEventDispatcher"); |
| return valueObject.booleanValue(); |
| } |
| boolean computedValue = ((ClassDefinitionBase)scope.getDefinition()).computeNeedsEventDispatcher(project); |
| needsEventDispatcherCache = computedValue; |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in needsEventDispatcher"); |
| return computedValue; |
| } |
| } |
| |
| public IInterfaceDefinition[] resolveInterfaces() |
| { |
| assert scope instanceof TypeScope : "resolveInterfacesImpl should only be called on scope cache's for TypeScopes!"; |
| assert scope.getDefinition() instanceof TypeDefinitionBase : "resolveInterfacesImpl should only be called on scope cache's for the scopes contained by types!"; |
| |
| IInterfaceDefinition[] interfs = interfacesCache != null ? interfacesCache.get() : null; |
| |
| if( interfs != null ) |
| return interfs; |
| |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache waiting for lock in resolveInterfaces"); |
| synchronized (this) |
| { |
| // check again in case another thread updated the value first |
| interfs = interfacesCache != null ? interfacesCache.get() : null; |
| if( interfs != null ) |
| { |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in resolveInterfaces"); |
| return interfs; |
| } |
| |
| interfs = ((TypeDefinitionBase)scope.getDefinition()).resolveInterfacesImpl(project); |
| interfacesCache = new SoftReference<IInterfaceDefinition[]>(interfs); |
| if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.ASSCOPECACHE) == CompilerDiagnosticsConstants.ASSCOPECACHE) |
| System.out.println("ASScopeCache done with lock in resolveInterfaces"); |
| return interfs; |
| } |
| } |
| /** |
| * Version of getNamespaceSetForName that caches the results - this is used |
| * to get the namespace set to use to lookup a 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 returned |
| * |
| * @param name the name to lookup |
| * @return the namespace set to use to lookup name |
| */ |
| Set<INamespaceDefinition> getNamespaceSetForName(String name) |
| { |
| ConcurrentMap<String, Set<INamespaceDefinition>> map = getNamespacesForNameMap(); |
| |
| Set<INamespaceDefinition> result = map.get(name); |
| if (result != null) |
| { |
| // We found a cached result - we're done |
| return result; |
| } |
| |
| // It is possible for 2+ threads to get in here for the same name. |
| // This is intentional - the worst that happens is that we duplicate the resolution work |
| // the benefit is that we avoid any sort of locking, which was proving expensive (time wise, |
| // and memory wise). |
| |
| Set<INamespaceDefinition> newResult = scope.getNamespaceSetForNameImpl(project, name); |
| result = map.putIfAbsent(name, newResult); |
| if (result == null) |
| result = newResult; |
| return result; |
| |
| } |
| |
| /** |
| * Version of addDependencyOnBuiltinType that uses a cache to determine if the dependency actually |
| * needs to be added. The act of adding a dependency is somewhat expensive, so using a cache |
| * is much faster |
| * |
| * @param builtinType the type to depend on |
| * @param dt the type of Dependency to add |
| */ |
| void addDependencyOnBuiltinType(IASLanguageConstants.BuiltinType builtinType, DependencyType dt) |
| { |
| Set<IASLanguageConstants.BuiltinType> set = getBuiltinTypeMap(); |
| |
| if( set.contains(builtinType) ) |
| { |
| // We found a cached result - we're done |
| return; |
| } |
| |
| // It is possible for 2+ threads to get in here for the same name. |
| // This is intentional - the worst that happens is that we duplicate the dependency work |
| // the benefit is that we avoid any sort of locking, which was proving expensive (time wise, |
| // and memory wise). |
| scope.addDependencyOnBuiltinTypeImpl(project, builtinType, dt); |
| set.add(builtinType); |
| |
| return; |
| } |
| |
| /** |
| * Used to cache no constant value results for the const value cache. |
| * Computing the value is expensive whether there is a value or not, and |
| * many constants will not have compile time constant values, so we want to |
| * cache those as well. |
| */ |
| private static final Object NO_CONST_VALUE = new Object(); |
| |
| /** |
| * get the constant value for the given const definition. If a compile time |
| * constant can not be computed for the definition, this will return null. |
| * |
| * @param constDef The constant definition you want the constant value of |
| * @return The constant value, or null if a compie time constant could not |
| * be computed |
| */ |
| public Object getConstantValue(ConstantDefinition constDef) |
| { |
| ConcurrentMap<IDefinition, Object> map = getConstantValueLookupMap(); |
| |
| Object result = map.get(constDef); |
| if (result != null) |
| { |
| // We found a cached result - we're done |
| if (result == NO_CONST_VALUE) |
| return null; |
| return result; |
| } |
| |
| // It is possible for 2+ threads to get in here for the same name. |
| // This is intentional - the worst that happens is that we duplicate the resolution work |
| // the benefit is that we avoid any sort of locking, which was proving expensive (time wise, |
| // and memory wise). |
| |
| Object newResult = constDef.resolveValueImpl(project); |
| if (newResult == null) |
| newResult = NO_CONST_VALUE; |
| result = map.putIfAbsent(constDef, newResult); |
| if (result == null) |
| result = newResult; |
| |
| if (result == NO_CONST_VALUE) |
| return null; |
| else |
| return result; |
| } |
| |
| /** |
| * Helper class - to be used as a key for caching qualified name lookups. |
| */ |
| private static class QName |
| { |
| String name; |
| INamespaceDefinition ns; |
| |
| QName(String name, INamespaceDefinition ns) |
| { |
| assert ns != null; |
| this.name = name; |
| this.ns = ns; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| return name.hashCode() ^ ns.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(Object o) |
| { |
| if (o == this) |
| return true; |
| if (o instanceof QName) |
| { |
| QName other = (QName)o; |
| return name.equals(other.name) && ns.equals(other.ns); |
| } |
| return false; |
| } |
| } |
| } |