blob: 397bb6bf3230f515348a6db2a1dc203b11f2a525 [file] [log] [blame]
/*
*
* 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.projects;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.common.DependencyTypeSet;
import org.apache.royale.compiler.common.Multiname;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.IFunctionDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.internal.definitions.ClassDefinition;
import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
import org.apache.royale.compiler.internal.embedding.EmbedData;
import org.apache.royale.compiler.internal.parsing.as.IProjectConfigVariables;
import org.apache.royale.compiler.internal.scopes.ASProjectScope;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.internal.scopes.ASScopeCache;
import org.apache.royale.compiler.internal.targets.AppSWFTarget;
import org.apache.royale.compiler.internal.targets.Target;
import org.apache.royale.compiler.internal.units.EmbedCompilationUnit;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.MissingBuiltinProblem;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.scopes.IASScope;
import org.apache.royale.compiler.targets.ISWFTarget;
import org.apache.royale.compiler.targets.ITargetProgressMonitor;
import org.apache.royale.compiler.targets.ITargetSettings;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.compiler.units.requests.IFileScopeRequestResult;
import org.apache.royale.compiler.units.requests.IRequest;
import org.apache.royale.utils.FilenameNormalization;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/**
* Abstract class used to share implementation of some ICompilerProject methods
* across multiple concrete implementation classes.
*/
public abstract class CompilerProject implements ICompilerProject
{
private final ASProjectScope projectScope;
private final Workspace workspace;
protected Set<Target> targets;
private final ReadWriteLock unfoundDependenciesLock;
private final Map<String, Map<ICompilationUnit, Object>> unfoundDefinitionDependencies;
protected Collection<ICompilerProblem> problems;
/**
* These are files that compilation units have tried to find and failed. At the moment these are
* Embedded files and included files. Should these files be added to the Builder workspace, the project
* will be notified and can in turn re-compiler the units that are missing these files
*/
private final Map<String, Map<ICompilationUnit, Object>> unfoundReferencedSourceFileDependencies;
private final Map<EmbedData, EmbedCompilationUnit> embedCompilationUnits;
/**
* Helper class to create new Scope Caches on the fly, when the scope cache map misses
*/
static class ScopeCacheLoader extends CacheLoader<ASScope, ASScopeCache>
{
CompilerProject project;
public ScopeCacheLoader(CompilerProject project)
{
this.project = project;
}
@Override
public ASScopeCache load(ASScope scope)
{
ASScopeCache cache = new ASScopeCache(project, scope);
project.projectScope.addScopeToCompilationUnitScopeList(scope);
return cache;
}
}
/**
* Map that holds caches for each scope in the project - uses a Concurrent Map, with weak keys so that the caches
* will go away once the corresponding scope has been gc'ed. Uses soft values so the caches may be collected
* if the VM is running out of memory
*/
private LoadingCache<ASScope, ASScopeCache> scopeCaches = CacheBuilder.newBuilder()
.weakKeys()
.softValues()
.build(new ScopeCacheLoader(this));
/** This thread local is to avoid every thread contending for access to the scopeCaches map, which is shared
* across the entire Project.
* When a scope cache is requested, we first check the thread local cache. If the entry is not present in
* the thread local, then we hit the shared scopeCaches map. The result from the scopeCaches map is then also
* cached in the thread local. This way, most of the time, we'll find the scope cache in the thread local, and
* won't have to do any locking.
*/
private ThreadLocal<Map<ASScope, WeakReference<ASScopeCache>> > threadLocalScopeCache;
/**
* Dependency graph is used to keep track of which {@link ICompilationUnit}
* 's are in this project. The dependency graph is in this shared base class
* instead of being in {@link ASProject} because {@link ICompilationUnit}
* implementations will want to update the dependency graph and
* {@link ICompilationUnit} implementations should be able to behave the
* same irrespective of what type of {@link CompilerProject} they are in.
*/
protected final DependencyGraph dependencyGraph;
/**
* If true, use parallel code generation of method bodies.
*/
private boolean useParallelCodeGen;
/**
* If true, use function inlining in code generation.
*/
private boolean enableInlining;
private final boolean useAS3;
/**
* used to track config variables and return config information back to clients, such as the parser
*/
private final ConfigManager configManager;
public CompilerProject(Workspace workspace, boolean useAS3)
{
this.workspace = workspace;
targets = new HashSet<Target>();
unfoundDependenciesLock = new ReentrantReadWriteLock();
unfoundDefinitionDependencies = new HashMap<String, Map<ICompilationUnit, Object>>();
unfoundReferencedSourceFileDependencies = new HashMap<String, Map<ICompilationUnit, Object>>();
embedCompilationUnits = new HashMap<EmbedData, EmbedCompilationUnit>();
dependencyGraph = new DependencyGraph();
// ** TODO Instantiate real scope object.
projectScope = initProjectScope(this);
this.useAS3 = useAS3;
initThreadLocalCaches();
configManager = new ConfigManager();
useParallelCodeGen = false;
enableInlining = false;
workspace.addProject(this);
}
/**
* Init the project scope. Allow subclasses to contribute their own if needed. Default will create a new {@link ASProjectScope}
* @param project the project to build the scope from
* @return a new {@link ASProjectScope}
*/
protected ASProjectScope initProjectScope(CompilerProject project) {
return new ASProjectScope(project);
}
/**
* Init the thread local cache - will reset the thread local cache to it's initial, empty state.
*/
private void initThreadLocalCaches()
{
threadLocalScopeCache = new ThreadLocal<Map<ASScope, WeakReference<ASScopeCache>>>()
{
@Override
protected Map<ASScope, WeakReference<ASScopeCache>> initialValue()
{
// Use a WeakHashMap with WeakRefs as values - the ASScopes and ASScopeCaches
// will be kept alive or not by the Project
return new WeakHashMap<ASScope, WeakReference<ASScopeCache>>();
}
};
}
@Override
public ASProjectScope getScope()
{
return this.projectScope;
}
@Override
public Workspace getWorkspace()
{
return workspace;
}
@Override
public List<ICompilationUnit> getReachableCompilationUnitsInSWFOrder(Collection<ICompilationUnit> roots)
{
return dependencyGraph.topologicalSort(roots);
}
/**
* Updates the {@link ASProjectScope} to contain reference to public and
* internal definitions in those compilation units.
*
* @param units {@link ICompilationUnit}'s to add to the project.
*/
public void updatePublicAndInternalDefinitions(Collection<ICompilationUnit> units) throws InterruptedException
{
if (units.isEmpty())
return;
ArrayList<IRequest<IFileScopeRequestResult, ICompilationUnit>> scopeRequests = new ArrayList<IRequest<IFileScopeRequestResult, ICompilationUnit>>(units.size());
for (ICompilationUnit unit : units)
{
ICompilerProject unitProject = unit.getProject();
assert (unitProject == this) || (unitProject == null) : "ICompilationUnit should be in this project or not in any project";
if (unitProject == this)
scopeRequests.add(unit.getFileScopeRequest());
}
scopeCaches.invalidateAll();
initThreadLocalCaches();
projectScope.addAllExternallyVisibleDefinitions(scopeRequests);
}
/**
* Adds compilation units to the project and updates the public and private
* definitions. Eventually this method should be removed, but it is
* convenient for testing because it allows custom created
* {@link ICompilationUnit}'s to be injected into a project.
*
* @param compilationUnits A collection of compilation units.
*/
public void addCompilationUnitsAndUpdateDefinitions(Collection<ICompilationUnit> compilationUnits) throws InterruptedException
{
addCompilationUnits(compilationUnits);
updatePublicAndInternalDefinitions(compilationUnits);
}
public void addEmbedCompilationUnit(EmbedCompilationUnit unit) throws InterruptedException
{
dependencyGraph.addEmbedCompilationUnit(unit);
workspace.addCompilationUnit(unit);
embedCompilationUnits.put(unit.getEmbedData(), unit);
updatePublicAndInternalDefinitions(Collections.<ICompilationUnit>singletonList(unit));
}
/**
* Add a {@link ICompilationUnit} to the project. Calling this method does not
* trigger the {@link ICompilationUnit} to be compiled.
*
* @param unit {@link ICompilationUnit} to add
*/
public void addCompilationUnit(ICompilationUnit unit)
{
assert !(unit instanceof EmbedCompilationUnit) : "Embed compilation unit should be added with addEmbedCompilationUnit!";
dependencyGraph.addCompilationUnit(unit);
workspace.addCompilationUnit(unit);
}
/**
* Add compilation units to the project. Calling this method does not
* trigger the compilation units to be compiled.
*
* @param units compilation units
*/
public void addCompilationUnits(Collection<ICompilationUnit> units)
{
for (ICompilationUnit compilationUnit : units)
{
addCompilationUnit(compilationUnit);
}
}
/**
* Removes a {@link ICompilationUnit} from the project.
* @param unit {@link ICompilationUnit} to remove.
*/
public void removeCompilationUnit(ICompilationUnit unit)
{
dependencyGraph.removeCompilationUnit(unit);
workspace.removeCompilationUnit(unit);
unit.clearProject();
if (unit instanceof EmbedCompilationUnit)
{
EmbedData embedData = ((EmbedCompilationUnit)unit).getEmbedData();
embedCompilationUnits.remove(embedData);
}
}
/**
* Don't call this from production code. Normally library managers take care of this.
* This function is only for unit tests.
*/
public void unitTestingEntryPointForRemovingCompilationUnit(ICompilationUnit unit)
{
removeCompilationUnits( Collections.singleton(unit) );
}
/**
* Removes a set of {@link ICompilationUnit}'s from the project.
* @param unitsToRemove {@link ICompilationUnit}'s to remove.
*/
public void removeCompilationUnits(Collection<ICompilationUnit> unitsToRemove)
{
if ((unitsToRemove == null) || (unitsToRemove.size() == 0))
return;
// TODO pre-remove invalidation
// Factor this into a method and un-comment at some point!
// Set<ICompilationUnit> workSet = new HashSet<ICompilationUnit>(unitsToRemove);
// Set<ICompilationUnit> visited = new HashSet<ICompilationUnit>(unitsToRemove.size());
// EnumSet<DependencyGraph.DependencyType> inheritanceTypeSet =
// EnumSet.of(DependencyGraph.DependencyType.INHERITANCE);
// while (workSet.size() >= 0)
// {
// ICompilationUnit unit = workSet.iterator().next();
// workSet.remove(unit);
// if (!visited.contains(unit))
// {
// visited.add(unit);
// unit.invalidate(DependencyGraph.DependencyType.INHERITANCE);
// workSet.addAll(dependencyGraph.getDirectReverseDependencies(unit, inheritanceTypeSet));
// }
// }
// TODO add code behind the invalidate all above or here to
// invalidation name resolution caches.
// unhook all the compilation units to remove from the project scope.
projectScope.removeCompilationUnits(unitsToRemove);
for (ICompilationUnit unit : unitsToRemove)
{
removeCompilationUnit(unit);
}
}
@Override
public Collection<ICompilationUnit> getCompilationUnits()
{
return dependencyGraph.getCompilationUnits();
}
@Override
public Collection<ICompilationUnit> getCompilationUnits(String filename)
{
return workspace.getCompilationUnits(FilenameNormalization.normalize(filename), this);
}
@Override
public Collection<ICompilationUnit> getIncludingCompilationUnits(String filename)
{
return workspace.getIncludingCompilationUnits(FilenameNormalization.normalize(filename), this);
}
public void addUnfoundDefinitionDependency(String definitionBaseName, ICompilationUnit compilationUnit)
{
unfoundDependenciesLock.writeLock().lock();
try
{
Map<ICompilationUnit, Object> dependentUnits = unfoundDefinitionDependencies.get(definitionBaseName);
if (dependentUnits == null)
{
dependentUnits = new WeakHashMap<ICompilationUnit, Object>();
unfoundDefinitionDependencies.put(definitionBaseName, dependentUnits);
}
dependentUnits.put(compilationUnit, null);
}
finally
{
unfoundDependenciesLock.writeLock().unlock();
}
}
public Set<ICompilationUnit> getDependenciesOnUnfoundDefinition(String definitionBaseName)
{
unfoundDependenciesLock.readLock().lock();
try
{
Map<ICompilationUnit, Object> dependentUnits = unfoundDefinitionDependencies.get(definitionBaseName);
if (dependentUnits == null)
{
dependentUnits = Collections.emptyMap();
}
return dependentUnits.keySet();
}
finally
{
unfoundDependenciesLock.readLock().unlock();
}
}
private void removeAnyUnfoundDefinitionDependency(ICompilationUnit compilationUnit)
{
for (Map<ICompilationUnit, Object> dependentUnits : unfoundDefinitionDependencies.values())
{
dependentUnits.remove(compilationUnit);
}
}
/**
* Notify project that a compilation unit could not find a referenced file.
*
* "Referenced" here means that the compilation unit references the file in some way other than the normal
* language level manner. For example, embedded and included files are "referenced", whereas files that
* defined referenced types are not.
*
* Calling this function will allow incremental re-compiation to be kicked off when/if the required
* file comes into existence.
*
* @param sourceFilename The full path to the required file
* @param compilationUnit The compilation unit that requires the referenced file
*/
public void addUnfoundReferencedSourceFileDependency(String sourceFilename, ICompilationUnit compilationUnit)
{
unfoundDependenciesLock.writeLock().lock();
try
{
String filenameNoPath = new File(sourceFilename).getName();
Map<ICompilationUnit, Object> dependentUnits = unfoundReferencedSourceFileDependencies.get(filenameNoPath);
if (dependentUnits == null)
{
dependentUnits = new WeakHashMap<ICompilationUnit, Object>();
unfoundReferencedSourceFileDependencies.put(filenameNoPath, dependentUnits);
}
dependentUnits.put(compilationUnit, null);
}
finally
{
unfoundDependenciesLock.writeLock().unlock();
}
}
/**
* gets the set of all compilation units that have an unsatisfied reference to a given file.
* @param sourceFilename The path to the file.
*/
public Set<ICompilationUnit> getDependenciesOnUnfoundReferencedSourceFile(String sourceFilename)
{
unfoundDependenciesLock.readLock().lock();
try
{
String filenameNoPath = new File(sourceFilename).getName();
Map<ICompilationUnit, Object> dependentUnits = unfoundReferencedSourceFileDependencies.get(filenameNoPath);
if (dependentUnits == null)
{
dependentUnits = Collections.emptyMap();
}
return dependentUnits.keySet();
}
finally
{
unfoundDependenciesLock.readLock().unlock();
}
}
private void removeAnyUnfoundReferencedSourceFileDependency(ICompilationUnit compilationUnit)
{
for (Map<ICompilationUnit, Object> dependentUnits : unfoundReferencedSourceFileDependencies.values())
{
dependentUnits.remove(compilationUnit);
}
}
public void removeAnyUnfoundDependencies(ICompilationUnit compilationUnit)
{
// we could probably get away without locking when a remove is done, as
// this is only called from a call to clean() on a compilation unit, and
// that should only happen in a single thread. but let's just play it safe
// and lock anyway in case someone in future calls this method from somewhere else.
unfoundDependenciesLock.writeLock().lock();
try
{
removeAnyUnfoundDefinitionDependency(compilationUnit);
removeAnyUnfoundReferencedSourceFileDependency(compilationUnit);
}
finally
{
unfoundDependenciesLock.writeLock().unlock();
}
}
public Set<ICompilationUnit> getDependenciesOnDefinition(String definitionBaseName)
{
Set<ICompilationUnit> dependentUnits = new HashSet<ICompilationUnit>();
Set<ICompilationUnit> definingUnits = projectScope.getCompilationUnitsByDefinitionName(definitionBaseName);
DependencyTypeSet dependencyTypes = DependencyTypeSet.allOf();
for (ICompilationUnit definingUnit : definingUnits)
{
dependentUnits.addAll(dependencyGraph.getDirectReverseDependencies(definingUnit, dependencyTypes));
}
return dependentUnits;
}
@Override
public void clean()
{
Map<ICompilerProject, Set<ICompilationUnit>> cusToUpdate = new HashMap<ICompilerProject, Set<ICompilationUnit>>();
workspace.startIdleState();
try
{
Collection<ICompilationUnit> compilationUnits = getCompilationUnits();
for (ICompilationUnit compilationUnit : compilationUnits)
{
compilationUnit.clean(null, cusToUpdate, true);
}
scopeCaches.invalidateAll();
initThreadLocalCaches();
}
finally
{
workspace.endIdleState(cusToUpdate);
}
}
@Override
public void delete()
{
Collection<ICompilationUnit> compilationUnits = getCompilationUnits();
for (ICompilationUnit compilationUnit : compilationUnits)
{
workspace.removeCompilationUnit(compilationUnit);
}
workspace.deleteProject(this);
}
@Override
public ISWFTarget createSWFTarget(ITargetSettings targetSettings, ITargetProgressMonitor progressMonitor) throws InterruptedException
{
return new AppSWFTarget(this, targetSettings, progressMonitor);
}
/**
* Adds a dependency from one {@link ICompilationUnit} to another
* {@link ICompilationUnit}.
*
* @param from {@link ICompilationUnit} that depends on "to".
* @param to {@link ICompilationUnit} that is depended on by "from".
* @param dt The types of dependencies to add.
* @param qname The qname of the target dependency added
*/
public void addDependency(ICompilationUnit from, ICompilationUnit to, DependencyTypeSet dt, String qname)
{
dependencyGraph.addDependency(from, to, dt, qname);
}
/**
* Adds a dependency from one {@link ICompilationUnit} to another
* {@link ICompilationUnit}.
*
* @param from {@link ICompilationUnit} that depends on "to".
* @param to {@link ICompilationUnit} that is depended on by "from".
* @param dt The type of dependency to add.
* @param qname The qname of the target dependency added
*/
public void addDependency(ICompilationUnit from, ICompilationUnit to, DependencyType dt, String qname)
{
dependencyGraph.addDependency(from, to, dt, qname);
}
/**
* Adds an anonymous dependency from one {@link ICompilationUnit} to another
* {@link ICompilationUnit}. This dependency will not be used in the link report.
*
* @param from {@link ICompilationUnit} that depends on "to".
* @param to {@link ICompilationUnit} that is depended on by "from".
* @param dt The type of dependency to add.
*/
public void addDependency(ICompilationUnit from, ICompilationUnit to, DependencyType dt)
{
dependencyGraph.addDependency(from, to, dt);
}
/**
* Removes the outgoing dependencies of a collection of {@link ICompilationUnit}.
*
* @param units {@link ICompilationUnit} to remove dependencies from.
*/
public void removeDependencies(Collection<ICompilationUnit> units)
{
for (ICompilationUnit unit : units)
dependencyGraph.removeDependencies(unit);
}
/**
* Gets the set of dependencies referenced by the specified
* {@link ICompilationUnit}. in the absence of wierd things like
* defaults/theme css, this is just the set of {@link ICompilationUnit}'s
* that define definitions directly referenced by the specified
* {@link ICompilationUnit}.
* <p>
* This method can be overridden by subclasses to add additional
* dependencies that are not in the dependency graph.
*
* @param cu {@link ICompilationUnit} to retrieve dependencies for.
* @return A new Set of {@link ICompilationUnit}'s that the specified
* {@link ICompilationUnit} directly depends on.
*/
public Set<ICompilationUnit> getDependencies(ICompilationUnit cu) throws InterruptedException
{
// The process of finding semantic problems updates the dependency graph
// so wait for semantic analysis pass to finish.
// TODO possible revisit how we wait for the dependency graph to be updated
// or at least better document it in ICompilationUnit.
cu.getOutgoingDependenciesRequest().get();
return dependencyGraph.getDirectDependencies(cu);
}
/**
* Gets project level {@link ICompilerProblem}'s target are not specific to any
* one target created by the project.
*/
public abstract void collectProblems(Collection<ICompilerProblem> problems);
/**
* Gets the project level {@link ICompilerProblem}'s that come from parsing the
* config options, from the command line, or a configuration file
* @param problems the Collection to add the config problems to.
*/
protected void collectConfigProblems(Collection<ICompilerProblem> problems)
{
problems.addAll(configManager.getProjectConfig(this).getProblems());
}
/**
* Get the cache for a particular scope
* @param scope the scope you want the cache for.
* @return the ASScopeCache for the scope
*/
public ASScopeCache getCacheForScope(ASScope scope)
{
ASScopeCache scopeCache = null;
// First check and see if we have the result cached in the thread local cache
Map<ASScope, WeakReference<ASScopeCache>> cache = threadLocalScopeCache.get();
WeakReference<ASScopeCache> ref = cache.get(scope);
scopeCache = ref != null ? ref.get() : null;
if( scopeCache == null )
{
// Didn't find it in the thread local, hit the shared cache, and cache those results
// in the thread local so that we won't have to hit the shared cache next time.
scopeCache = scopeCaches.getUnchecked(scope);
cache.put(scope, new WeakReference<ASScopeCache>(scopeCache));
}
return scopeCache;
}
/**
* Clears all the {@link ASScopeCache}s associated with the specified
* {@link ICompilationUnit} and removes all the {@link IASScope}s build by
* the {@link ICompilationUnit} from the list of scopes associated with the
* {@link ICompilationUnit}. This method should only be called when a
* {@link ICompilationUnit} is being removed from a project.
*
* @param compilationUnit {@link ICompilationUnit} whose scope caches should
* be cleared and whose scope list should be emptied.
*/
public void clearScopeCacheForCompilationUnit(ICompilationUnit compilationUnit)
{
Collection<IASScope> relatedScopes = projectScope.clearCompilationUnitScopeList(compilationUnit);
if (relatedScopes == null)
return;
resetScopeCaches(relatedScopes);
}
/**
* Resets all the {@link ASScopeCache}s associated with the specified
* {@link ICompilationUnit}.
*
* @param compilationUnit {@link ICompilationUnit} whose scope caches should
* be cleared
*/
public void resetScopeCacheForCompilationUnit(ICompilationUnit compilationUnit)
{
Collection<IASScope> relatedScopes = projectScope.getCompilationUnitScopeList(compilationUnit);
assert relatedScopes != null;
if (relatedScopes.isEmpty())
return;
resetScopeCaches(relatedScopes);
}
private void resetScopeCaches(Iterable<IASScope> scopes)
{
assert scopes != null;
for (IASScope scope : scopes)
{
scopeCaches.invalidate(scope);
}
initThreadLocalCaches();
}
public void addGlobalUsedNamespacesToNamespaceSet(Set<INamespaceDefinition> nsSet)
{
nsSet.add(NamespaceDefinition.getPublicNamespaceDefinition());
if (this.useAS3)
nsSet.add(NamespaceDefinition.getAS3NamespaceDefinition());
}
/**
* Called by
* {@link Workspace#fileAdded(org.apache.royale.compiler.filespecs.IFileSpecification)}
* for each project in the workspace. Each subclass of this class must
* decide when an added file is interesting or should be ignored.
*
* @param addedFile File that was added.
* @return true if any new compilation units were created as a result of
* adding the file, false otherwise.
*/
public abstract boolean handleAddedFile(File addedFile);
/**
* Return the compilation unit related this EmbedData, or it's equivalent. If
* there is no equiv., null will be returned
* @param data The embedding data.
* @return related EmbedCompilationUnit. Can be null
*/
public EmbedCompilationUnit getCompilationUnit(EmbedData data)
{
return embedCompilationUnits.get(data);
}
public final DependencyGraph getDependencyGraph()
{
return dependencyGraph;
}
/**
* Sets project config variables for use on this project
* @see org.apache.royale.compiler.internal.projects.ConfigManager#addConfigVariables(Map)
* @param map the mapping of config names to their expressions
*/
public void addConfigVariables(Map<String, String> map) {
configManager.addConfigVariables(map);
// TODO: in future we could be smarter about incrementally invalidating
// the project based on what the config variables affect, but for now
// just blow away the whole project.
clean();
}
/**
* Sets project config variable for use on this project
* @see org.apache.royale.compiler.internal.projects.ConfigManager#addConfigVariables(Map)
* @param namespace the config namespace
* @param expression the config expression for the namespace
*/
public void addConfigVariable(String namespace, String expression) {
configManager.addConfigVariable(namespace, expression);
// TODO: in future we could be smarter about incrementally invalidating
// the project based on what the config variables affect, but for now
// just blow away the whole project.
clean();
}
/**
* Returns an {@link IProjectConfigVariables} struct that contains all the config data for this project
* @return an {@link IProjectConfigVariables} object, never null
*/
public IProjectConfigVariables getProjectConfigVariables() {
return configManager.getProjectConfig(this);
}
@Override
public IDefinition resolveQNameToDefinition(final String qName)
{
assert qName != null : "qName can't be null.";
final Multiname multiname = Multiname.crackDottedQName(this, qName);
return getScope().findDefinitionByName(multiname, false);
}
@Override
public ICompilationUnit resolveQNameToCompilationUnit(final String qName)
{
IDefinition definition = resolveQNameToDefinition(qName);
if (definition == null)
return null;
return projectScope.getCompilationUnitForDefinition(definition);
}
// List of Builtin types that must be present for compilation
// to succeed.
private static final BuiltinType[] REQUIRED_BUILTINS = {
BuiltinType.OBJECT,
BuiltinType.ARRAY,
BuiltinType.STRING,
BuiltinType.ANY_TYPE,
BuiltinType.VOID,
BuiltinType.NUMBER,
BuiltinType.INT,
BuiltinType.UINT
};
/**
* Check for fatal problems, that would prevent us from successfully compiling.
* Currently this just checks that all of the builtin types are present.
*/
public Collection<ICompilerProblem> getFatalProblems()
{
Collection<ICompilerProblem> fatalProblems = null;
for(IASLanguageConstants.BuiltinType builtinType : REQUIRED_BUILTINS )
{
if (getBuiltinType(builtinType) == null)
{
if (fatalProblems == null)
fatalProblems = new ArrayList<ICompilerProblem>();
fatalProblems.add(new MissingBuiltinProblem(builtinType.getName()));
break;
}
}
return fatalProblems == null ? Collections.<ICompilerProblem>emptyList() : fatalProblems;
}
@SuppressWarnings("incomplete-switch")
@Override
public ITypeDefinition getBuiltinType(IASLanguageConstants.BuiltinType type)
{
switch( type )
{
case OBJECT:
return projectScope.getObjectDefinition();
case STRING:
return projectScope.getStringDefinition();
case ARRAY:
return projectScope.getArrayDefinition();
case XML:
return projectScope.getXMLDefinition();
case XMLLIST:
return projectScope.getXMLListDefinition();
case BOOLEAN:
return projectScope.getBooleanDefinition();
case INT:
return projectScope.getIntDefinition();
case UINT:
return projectScope.getUIntDefinition();
case NUMBER:
return projectScope.getNumberDefinition();
case CLASS:
return projectScope.getClassDefinition();
case FUNCTION:
return projectScope.getFunctionDefinition();
case NAMESPACE:
return projectScope.getNamespaceDefinition();
case VECTOR:
return projectScope.getVectorDefinition();
case NULL:
return ClassDefinition.getNullClassDefinition();
case VOID:
return ClassDefinition.getVoidClassDefinition();
case Undefined:
return ClassDefinition.getUndefinedClassDefinition();
case ANY_TYPE:
return ClassDefinition.getAnyTypeClassDefinition();
}
String name = type.getName();
ITypeDefinition definition = (ITypeDefinition)getScope().findDefinitionByName(name);
// Don't have playerglobal.abc/swc with float yet.
assert definition != null
: "Error, builtin type '"+ type.getName()+ "' can't be found!";
return definition;
}
@Override
public IDefinition getUndefinedValue()
{
return projectScope.getUndefinedValueDefinition();
}
/**
* Gets a boolean that indicates whether or not parallel code generation of method bodies is enabled.
* @return true if method bodies should be generated in parallel, false otherwise.
*/
public boolean getUseParallelCodeGeneration()
{
return useParallelCodeGen;
}
@Override
public void setUseParallelCodeGeneration(boolean useParallelCodeGeneration)
{
this.useParallelCodeGen = useParallelCodeGeneration;
}
@Override
public Set<ICompilationUnit> getDirectDependencies(ICompilationUnit cu)
{
return dependencyGraph.getDirectDependencies(cu);
}
@Override
public Set<ICompilationUnit> getDirectReverseDependencies(ICompilationUnit cu, DependencyTypeSet types)
{
return dependencyGraph.getDirectReverseDependencies(cu, types);
}
@Override
public boolean isInliningEnabled()
{
return enableInlining;
}
/**
* Set whether or not function inlining is enabled.
*
* @param enableInlining true to enable inlining.
*/
public void setEnableInlining(boolean enableInlining)
{
this.enableInlining = enableInlining;
clean();
}
/**
* Add AST to cache. By default, not added to any cache.
*
* @param ast The AST.
*/
public void addToASTCache(IASNode ast)
{
}
/**
* Override this to permit package aliasing on imports and elsewhere
*
* @param packageName The package name
*/
public String getActualPackageName(String packageName)
{
return packageName;
}
/**
* Override this to disambiguate between two ambiguous definitinos
*
* @param scope The current scope.
* @param name Definition name.
* @param def1 One possibility.
* @param def2 The other possibility.
* @return null if still ambiguous or else def1 or def2.
*/
public IDefinition doubleCheckAmbiguousDefinition(IASScope scope, String name, IDefinition def1, IDefinition def2)
{
return null;
}
/**
* @return collection of compiler problems.
*/
public Collection<ICompilerProblem> getProblems()
{
return problems;
}
/**
* collection of compiler problems.
*/
public void setProblems(Collection<ICompilerProblem> problems)
{
this.problems = problems;
}
public boolean isCompatibleOverrideReturnType(IFunctionDefinition func, ITypeDefinition overrideDefinition, ITypeDefinition baseDefinition)
{
return (baseDefinition == overrideDefinition);
}
public boolean isValidTypeConversion(IASNode node, IDefinition actualDefinition, IDefinition expectedDefinition, IFunctionDefinition func)
{
return false;
}
public boolean isCompatibleOverrideParameterType(IFunctionDefinition func,
ITypeDefinition overrideDefinition, ITypeDefinition baseDefinition,
int i) {
return (baseDefinition == overrideDefinition);
}
public boolean isParameterCountMismatchAllowed(IFunctionDefinition func,
int formalCount, int actualCount) {
return false;
}
}