/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.internal.units; import static com.google.common.collect.Collections2.transform; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; 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 java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.io.FilenameUtils; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.common.IDefinitionPriority; import org.apache.flex.compiler.common.IEmbedResolver; import org.apache.flex.compiler.common.IFileSpecificationGetter; import org.apache.flex.compiler.css.ICSSDocument; import org.apache.flex.compiler.css.ICSSRule; import org.apache.flex.compiler.css.ICSSSelector; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.filespecs.IFileSpecification; import org.apache.flex.compiler.internal.css.CSSFunctionCallPropertyValue; import org.apache.flex.compiler.internal.css.codegen.CSSCompilationSession; import org.apache.flex.compiler.internal.css.semantics.CSSSemanticAnalyzer; import org.apache.flex.compiler.internal.embedding.EmbedAttribute; import org.apache.flex.compiler.internal.embedding.EmbedData; import org.apache.flex.compiler.internal.graph.LinkReportWriter; import org.apache.flex.compiler.internal.projects.ASProject; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.projects.DefinitionPriority; import org.apache.flex.compiler.internal.projects.DependencyGraph; import org.apache.flex.compiler.internal.scopes.ASFileScope; import org.apache.flex.compiler.internal.scopes.ASProjectScope; import org.apache.flex.compiler.internal.scopes.ASProjectScope.DefinitionPromise; import org.apache.flex.compiler.internal.tree.as.NodeBase; import org.apache.flex.compiler.internal.units.requests.ABCBytesRequestResult; import org.apache.flex.compiler.internal.units.requests.FileScopeRequestResultBase; import org.apache.flex.compiler.internal.units.requests.RequestMaker; import org.apache.flex.compiler.internal.units.requests.SyntaxTreeRequestResult; import org.apache.flex.compiler.mxml.IXMLNameResolver; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.InternalCompilerProblem2; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.projects.IFlexProject; import org.apache.flex.compiler.targets.ITarget.TargetType; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IFileNodeAccumulator; import org.apache.flex.compiler.tree.as.IImportNode; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.compiler.units.IInvisibleCompilationUnit; import org.apache.flex.compiler.units.requests.IABCBytesRequestResult; import org.apache.flex.compiler.units.requests.IFileScopeRequestResult; import org.apache.flex.compiler.units.requests.IOutgoingDependenciesRequestResult; import org.apache.flex.compiler.units.requests.IRequest; import org.apache.flex.compiler.units.requests.ISWFTagsRequestResult; import org.apache.flex.compiler.units.requests.ISyntaxTreeRequestResult; import org.apache.flex.compiler.workspaces.IWorkspaceProfilingDelegate; import org.apache.flex.swf.SWFFrame; import org.apache.flex.swf.tags.DoABCTag; import org.apache.flex.utils.FilenameNormalization; import org.apache.flex.utils.StringEncoder; import com.google.common.base.FinalizableReferenceQueue; import com.google.common.base.FinalizableWeakReference; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; /** * Abstract class used to share implementation of some ICompilationUnit methods * across multiple concrete implementation classes. * <p> * This class converts calls to the getXXXRequest methods to calls into * handleXXXRequest methods. Subclasses can just implement the handleXXXRequest * methods without having to worry about creating an IRequest implementation. */ public abstract class CompilationUnitBase implements ICompilationUnit { protected static final String DEFAULT_DO_ABC_TAG_NAME = "script"; private static class InvisibleCompilationUnitRef extends FinalizableWeakReference<InvisibleCompilationUnit> { private final CompilationUnitBase delegate; /** * @param referent * @param queue */ protected InvisibleCompilationUnitRef(CompilationUnitBase delegate, InvisibleCompilationUnit referent, FinalizableReferenceQueue queue) { super(referent, queue); this.delegate = delegate; } @Override public void finalizeReferent() { CompilerProject project = delegate.getProject(); // if the project is null, the delegate CU has // already been manually removed by a client, so // nothing more to do. if (project == null) return; // when the InvisibleCompilationUnit get's gc'd, make sure // we also remove the CU which isn't pointing too. project.removeCompilationUnit(delegate); } } /** * Semantic analyze a collection of CSS model objects, and return a * {@link CSSCompilationSession} that contains the resolved symbols. * <ol> * <li>Resolve type selectors to {@link IClassDefinition} definitions.</li> * <li>Find all the dependencies introduced by {@code ClassReference()} and * {@code Embed()} property values.</li> * </ol> * * @param cssCompilationSession A {@code CSSCompilationSession} object that * contains resolved symbols from all the CSS models in {@code cssDocuments} * collection. * @param xmlNameResolver Resolve type selectors to definitions. * @param cssDocuments A list of CSS model objects. * @param problems Compiler problem collection. */ protected void updateStyleCompilationUnitDependencies( final CSSCompilationSession cssCompilationSession, final IXMLNameResolver xmlNameResolver, final Iterable<ICSSDocument> cssDocuments, final Collection<ICompilerProblem> problems) { final Set<IClassDefinition> classReferences = new LinkedHashSet<IClassDefinition>(); final Set<EmbedCompilationUnit> dependentEmbedCompilationUnits = new LinkedHashSet<EmbedCompilationUnit>(); for (final ICSSDocument cssDocument : cssDocuments) { final boolean isFlex3CSS = ((IFlexProject)project).getCSSManager().isFlex3CSS(); final ImmutableMap<ICSSSelector, String> resolvedSelectors = CSSSemanticAnalyzer.resolveSelectors(xmlNameResolver, cssDocument, problems, isFlex3CSS); // Store resolved type selectors required by CSS code generation. cssCompilationSession.resolvedSelectors.putAll(resolvedSelectors); // Store resolved embed compilation units required by CSS code generation. for (final ICSSRule cssRule : cssDocument.getRules()) { final Map<CSSFunctionCallPropertyValue, EmbedCompilationUnit> resolvedEmbedProperties = new HashMap<CSSFunctionCallPropertyValue, EmbedCompilationUnit>(); CSSSemanticAnalyzer.resolveDependencies( resolvedEmbedProperties, cssRule, project, classReferences, dependentEmbedCompilationUnits, problems); cssCompilationSession.resolvedEmbedProperties.putAll(resolvedEmbedProperties); } } // Convert from "IClassDefinition" to "ICompilationUnit". final Function<IClassDefinition, ICompilationUnit> findCompilationUnitForClass = new Function<IClassDefinition, ICompilationUnit>() { @Override public ICompilationUnit apply(final IClassDefinition classDefinition) { return project.getScope().getCompilationUnitForDefinition(classDefinition); } }; final Collection<ICompilationUnit> classReferenceCompilationUnits = transform(classReferences, findCompilationUnitForClass); // "The 'IClassDefinition' was resolved from the project, so there should be a compilation // unit for each IClassDefinition definition. If there's an exception for a null compilation // unit being added to the ImmutableSet, there's probably a bug in // CSSSemanticAnalyzer.resolveDependencies(). // Merge dependencies from ClassReference() and Embed(). final ImmutableSet<ICompilationUnit> dependencies = new ImmutableSet.Builder<ICompilationUnit>() .addAll(classReferenceCompilationUnits) .addAll(dependentEmbedCompilationUnits) .build(); for (final ICompilationUnit dependee : dependencies) { project.getDependencyGraph().addDependency(this, dependee, DependencyType.EXPRESSION); } } private CompilerProject project; private final String absoluteFilename; private List<IDefinition> definitionPromises; private final DefinitionPriority definitionPriority; protected final AtomicReference<IRequest<ISyntaxTreeRequestResult, ICompilationUnit>> syntaxTreeRequest; protected final AtomicReference<IRequest<IFileScopeRequestResult, ICompilationUnit>> fileScopeRequest; protected final AtomicReference<IRequest<IABCBytesRequestResult, ICompilationUnit>> abc; private final AtomicReference<IRequest<ISWFTagsRequestResult, ICompilationUnit>> tags; protected final AtomicReference<IRequest<IOutgoingDependenciesRequestResult, ICompilationUnit>> problems; private final Set<ICompilationUnit> embedCompilationUnits; private IFileSpecificationGetter fileSpecificationGetter; private InvisibleCompilationUnitRef invisibleCURef; private final AtomicInteger state; /** * Cached name of this compilation unit. Must be set in constructor * * It would be nice if this could be final, but it can't because of the multiplicity of constructors in this class */ private String name; /** * Tracks whether or not the syntax tree must be built before the file scope * for this {@link ICompilationUnit} can be built. This flag is needed to * make incremental building of projects containing files on the source list * work. */ private final boolean scopeBuiltFromSyntaxTree; private static final RequestMaker<ISyntaxTreeRequestResult, ICompilationUnit, CompilationUnitBase> syntaxTreeRequestMaker = new RequestMaker<ISyntaxTreeRequestResult, ICompilationUnit, CompilationUnitBase>() { @Override protected Callable<ISyntaxTreeRequestResult> getCallable(final CompilationUnitBase u) { return new Callable<ISyntaxTreeRequestResult>() { @Override public ISyntaxTreeRequestResult call() throws InterruptedException { return u.processSyntaxTreeRequest(); } }; } @Override protected ISyntaxTreeRequestResult getResultForThrowable(CompilationUnitBase u, Throwable throwable) { final ICompilerProblem prob = new InternalCompilerProblem2( u.getRootFileSpecification().getPath(), throwable, "syntax tree request"); return new SyntaxTreeRequestResult(u.getRootFileSpecification().getLastModified(), Collections.singleton(prob)); } }; private static final RequestMaker<IFileScopeRequestResult, ICompilationUnit, CompilationUnitBase> fileScopeRequestMaker = new RequestMaker<IFileScopeRequestResult, ICompilationUnit, CompilationUnitBase>() { @Override protected Callable<IFileScopeRequestResult> getCallable(final CompilationUnitBase u) { return new Callable<IFileScopeRequestResult>() { @Override public IFileScopeRequestResult call() throws InterruptedException { return u.processFileScopeRequest(); } }; } @Override protected IFileScopeRequestResult getResultForThrowable(CompilationUnitBase u, Throwable throwable) { final ICompilerProblem prob = new InternalCompilerProblem2( u.getRootFileSpecification().getPath(), throwable, "file scope builder"); return new FileScopeRequestResultBase(Collections.singleton(prob), null); } }; private static final RequestMaker<IABCBytesRequestResult, ICompilationUnit, CompilationUnitBase> abcBytesRequestMaker = new RequestMaker<IABCBytesRequestResult, ICompilationUnit, CompilationUnitBase>() { @Override protected Callable<IABCBytesRequestResult> getCallable(final CompilationUnitBase u) { return new Callable<IABCBytesRequestResult>() { @Override public IABCBytesRequestResult call() throws Exception { return u.processABCBytesRequest(); } }; } @Override protected IABCBytesRequestResult getResultForThrowable(CompilationUnitBase u, Throwable throwable) { return new ABCBytesRequestResult(new ICompilerProblem[] { new InternalCompilerProblem2(u.getAbsoluteFilename(), throwable, "ABC generator")}); } }; private static final RequestMaker<ISWFTagsRequestResult, ICompilationUnit, CompilationUnitBase> swfTagsRequestMaker = new RequestMaker<ISWFTagsRequestResult, ICompilationUnit, CompilationUnitBase>() { @Override protected Callable<ISWFTagsRequestResult> getCallable(final CompilationUnitBase u) { return new Callable<ISWFTagsRequestResult>() { @Override public ISWFTagsRequestResult call() throws Exception { return u.processSWFTTagsRequest(); } }; } @Override protected ISWFTagsRequestResult getResultForThrowable(CompilationUnitBase u, Throwable throwable) { final ICompilerProblem prob = new InternalCompilerProblem2( u.getRootFileSpecification().getPath(), throwable, "SWFTags"); return new ISWFTagsRequestResult() { @Override public ICompilerProblem[] getProblems() { return new ICompilerProblem[] {prob}; } @Override public boolean addToFrame(SWFFrame f) { return false; } @Override public String getDoABCTagName() { return ""; } @Override public DoABCTag getDoABCTag() { return null; } }; } }; private static final RequestMaker<IOutgoingDependenciesRequestResult, ICompilationUnit, CompilationUnitBase> outgoingDependenciesRequestMaker = new RequestMaker<IOutgoingDependenciesRequestResult, ICompilationUnit, CompilationUnitBase>() { @Override protected Callable<IOutgoingDependenciesRequestResult> getCallable(final CompilationUnitBase u) { return new Callable<IOutgoingDependenciesRequestResult>() { @Override public IOutgoingDependenciesRequestResult call() throws Exception { return u.processOutgoingDependenciesRequest(); } }; } @Override protected IOutgoingDependenciesRequestResult getResultForThrowable(CompilationUnitBase u, Throwable throwable) { final ICompilerProblem prob = new InternalCompilerProblem2( u.getRootFileSpecification().getPath(), throwable, "outgoing dependency"); return new IOutgoingDependenciesRequestResult() { @Override public ICompilerProblem[] getProblems() { return new ICompilerProblem[] {prob}; } }; } }; /** * Master constructor. Other accesible constructors end up funneling down to * this one. * * @param project * @param path * @param basePriority * @param doInitDefinitionPromises - if true will "complete" the * construction by allocating an empty list of definitions, and also * initialize the name field. If false, caller must initialize * this.definitionPromises and this.name * @param scopeBuiltFromSyntaxTree If true, the request that builds the file * scope requires that the request that builds the syntax tree be completed * first. */ private CompilationUnitBase(CompilerProject project, String path, DefinitionPriority.BasePriority basePriority, boolean doInitDefinitionPromises, boolean scopeBuiltFromSyntaxTree) { this.project = project; this.absoluteFilename = FilenameNormalization.normalize(path); definitionPriority = new DefinitionPriority(basePriority, 0); syntaxTreeRequest = new AtomicReference<IRequest<ISyntaxTreeRequestResult, ICompilationUnit>>(); fileScopeRequest = new AtomicReference<IRequest<IFileScopeRequestResult, ICompilationUnit>>(); abc = new AtomicReference<IRequest<IABCBytesRequestResult, ICompilationUnit>>(); tags = new AtomicReference<IRequest<ISWFTagsRequestResult, ICompilationUnit>>(); problems = new AtomicReference<IRequest<IOutgoingDependenciesRequestResult, ICompilationUnit>>(); embedCompilationUnits = new HashSet<ICompilationUnit>(); fileSpecificationGetter = project.getWorkspace(); state = new AtomicInteger(); if (doInitDefinitionPromises) { definitionPromises = Collections.emptyList(); name = computeName(); } this.scopeBuiltFromSyntaxTree = scopeBuiltFromSyntaxTree; } /** * This is the most generic constructor * * @param project * @param path * @param basePriority * @param qnamesOfDefinitions is a collection with all the definition promises for the compilation unit */ protected CompilationUnitBase(CompilerProject project, String path, DefinitionPriority.BasePriority basePriority, Collection<String> qnamesOfDefinitions) { this(project, path, basePriority, false, qnamesOfDefinitions.isEmpty()); definitionPromises = createDefinitionPromisesFromQnames(qnamesOfDefinitions, this); name = computeName(); // now that the definitions are set, we can cache the name } /** * Use this constructor if derived class has no definition promises */ protected CompilationUnitBase(CompilerProject project, String path, DefinitionPriority.BasePriority basePriority, boolean scopeBuiltFromSytaxTree) { this(project, path, basePriority, true, scopeBuiltFromSytaxTree); } /** * Use this constructor if the derived class has at most one definition promise * * @param project * @param path * @param basePriority * @param qnameOfDefinition is the definition promise, or null */ protected CompilationUnitBase(CompilerProject project, String path, DefinitionPriority.BasePriority basePriority, String qnameOfDefinition) { this(project, path, basePriority, qnameOfDefinition == null ? Collections.<String>emptyList() : Collections.<String>singletonList(qnameOfDefinition)); } private static List<IDefinition> createDefinitionPromisesFromQnames(Collection<String> qnames, ICompilationUnit cu) { List<IDefinition> ret = new ArrayList<IDefinition>(); for (String qname : qnames) { if (qname != null) // some ctors pass in null qname string - it makes things easier. Just ignore them ret.add(ASProjectScope.createDefinitionPromise(qname, cu)); } return ret; } /** * Helper method that atomically updates the state of this compilation unit * to reflect that the specified operation is now complete. * @param operation The operation that is now complete. */ private final void operationComplete(ICompilationUnit.Operation operation) { boolean done = false; do { int currentState = state.get(); int newState = currentState | operation.mask; done = state.compareAndSet(currentState, newState); } while (!done); } /** * Atomically checks the state of this compilation unit to determine * if all the specified operations are complete. * @param operations The set of operation to check. * @return true if all the specified operations are complete, false otherwise. */ protected final boolean operationsCompleted(EnumSet<ICompilationUnit.Operation> operations) { int currentState = state.get(); for (ICompilationUnit.Operation operation : operations) { if ((currentState & operation.mask) == 0) return false; } return true; } /** * @return List of definition promises */ @Override public List<IDefinition> getDefinitionPromises() { return definitionPromises; } @Override public CompilerProject getProject() { return project; } /** * Builds a {@link ISyntaxTreeRequestResult}. In many cases the {@link ISyntaxTreeRequestResult} * is built by parsing a source file into a syntax tree. * <p> * Called by this class from potentially any thread. This class guarantees * that this method will only be entered from one thread at a time for a * given instance of this class, so implementations do not need to make this * a synchronized method. * * @return The {@link ISyntaxTreeRequestResult} for this compilation unit. * @throws InterruptedException */ protected abstract ISyntaxTreeRequestResult handleSyntaxTreeRequest() throws InterruptedException; /** * Builds a {@link IFileScopeRequestResult}. In many cases the * {@link IFileScopeRequestResult} is built by walking an AST produced by a parser. * <p> * Called by this class from potentially any thread. This class guarantees * that this method will only be entered from one thread at a time for a * given instance of this class, so implementations do not need to make this * a synchronized method. * * @return The {@link IFileScopeRequestResult} for this compilation unit. */ protected abstract IFileScopeRequestResult handleFileScopeRequest() throws InterruptedException; /** * Builds a IABCBytesRequestResult. In many cases the IABCBytesRequestResult * is built running a code generator over an AST produced by a parser. * <p> * Called by this class from potentially any thread. This class guarantees * that this method will only be entered from one thread at a time for a * given instance of this class, so implementations do not need to make this * a synchronized method. * * @return The IABCBytesRequestResult for this compilation unit. */ protected abstract IABCBytesRequestResult handleABCBytesRequest() throws InterruptedException; /** * Builds a ISWFTagsRequestResult. In many cases the ISWFTagsRequestResult is built * getting the IABCBytesRequestResult and wrapping its abc bytes in a * DoABCTag. * <p> * Called by this class from potentially any thread. This class guarantees * that this method will only be entered from one thread at a time for a * given instance of this class, so implementations do not need to make this * a synchronized method. * * @return The ISWFTagsRequestResult for this compilation unit. */ protected abstract ISWFTagsRequestResult handleSWFTagsRequest() throws InterruptedException; /** * Builds a IOutgoingDependenciesRequestResult. In many cases the * IOutgoingDependenciesRequestResult is built getting the walking an AST. * <p> * Called by this class from potentially any thread. This class guarantees * that this method will only be entered from one thread at a time for a * given instance of this class, so implementations do not need to make this * a synchronized method. * * @return The IOutgoingDependenciesRequestResult for this compilation unit. */ protected abstract IOutgoingDependenciesRequestResult handleOutgoingDependenciesRequest () throws InterruptedException; @Override public IRequest<ISyntaxTreeRequestResult, ICompilationUnit> getSyntaxTreeRequest() { return syntaxTreeRequestMaker.getRequest(this, syntaxTreeRequest, project.getWorkspace(), scopeBuiltFromSyntaxTree); } @Override public IRequest<IFileScopeRequestResult, ICompilationUnit> getFileScopeRequest() { return fileScopeRequestMaker.getRequest(this, fileScopeRequest, project.getWorkspace(), true); } @Override public IRequest<IABCBytesRequestResult, ICompilationUnit> getABCBytesRequest() { return abcBytesRequestMaker.getRequest(this, abc, project.getWorkspace(), false); } @Override public IRequest<ISWFTagsRequestResult, ICompilationUnit> getSWFTagsRequest() { return swfTagsRequestMaker.getRequest(this, tags, project.getWorkspace(), false); } @Override public IRequest<IOutgoingDependenciesRequestResult, ICompilationUnit> getOutgoingDependenciesRequest() { return outgoingDependenciesRequestMaker.getRequest(this, problems, project.getWorkspace(), false); } private Collection<IDefinition> getAllDefinitions() throws InterruptedException { Collection<IDefinition> definitions; if (definitionPromises.isEmpty()) { IFileScopeRequestResult fileScopeRequestResult = getFileScopeRequest().get(); definitions = fileScopeRequestResult.getExternallyVisibleDefinitions(); } else { definitions = definitionPromises; } return definitions; } @Override public List<String> getShortNames() throws InterruptedException { Collection<IDefinition> definitions = getAllDefinitions(); List<String> shortNames = new ArrayList<String>(definitions.size()); for (IDefinition definition : definitions) { shortNames.add(definition.getBaseName()); } return shortNames; } @Override public List<String> getQualifiedNames() throws InterruptedException { Collection<IDefinition> definitions = getAllDefinitions(); List<String> qualifiedNames = new ArrayList<String>(definitions.size()); for (IDefinition definition : definitions) { qualifiedNames.add(definition.getQualifiedName()); } return qualifiedNames; } @Override public final String getAbsoluteFilename() { return absoluteFilename; } protected final String getFilenameNoPath() { return FilenameUtils.getName(absoluteFilename); } protected IFileSpecification getRootFileSpecification() { final String fileName = getAbsoluteFilename(); return fileSpecificationGetter.getFileSpecification(fileName); } protected IFileSpecificationGetter getFileSpecificationGetter() { return fileSpecificationGetter; } @Override public boolean clean(Map<ICompilerProject, Set<File>> invalidatedSWCFiles, Map<ICompilerProject, Set<ICompilationUnit>> cusToUpdate, final boolean clearFileScope) { IWorkspaceProfilingDelegate profilingDelegate = project.getWorkspace().getProfilingDelegate(); if (profilingDelegate != null) profilingDelegate.operationStarted(this, Operation.INVALIDATE_CU); project.removeDependencies(Collections.<ICompilationUnit>singletonList(this)); if (clearFileScope) { clearIncludedFilesFromWorkspace(); // If this compilation unit is invisible don't try to // remove definitions from the project scope. if (!isInvisible()) project.getScope().removeCompilationUnits(Collections.<ICompilationUnit>singletonList(this)); fileScopeRequest.set(null); // If we are are invalidating the file scope, // we should also invalidate the ast. syntaxTreeRequest.set(null); project.removeAnyUnfoundDependencies(this); } abc.set(null); tags.set(null); problems.set(null); embedCompilationUnits.clear(); project.resetScopeCacheForCompilationUnit(this); if (clearFileScope) updateDefinitions(cusToUpdate); // delegate to a virtual method that sub-classes can override to // do additional cleaning. handleClean(clearFileScope, invalidatedSWCFiles); if (profilingDelegate != null) profilingDelegate.operationCompleted(this, Operation.INVALIDATE_CU); return true; } /** * This method is used to update the Workspace * includeFilesToIncludingCompilationUnitMapping map when a file get's cleaned. It * goes to great pains to not reparse the file, as if the file hasn't been parsed, * there shouldn't be any files related to this compilation unit in the map. */ public void clearIncludedFilesFromWorkspace() { try { IRequest<ISyntaxTreeRequestResult, ICompilationUnit> req = syntaxTreeRequest.get(); if (req != null && req.isDone()) project.getWorkspace().removeIncludedFilesToCompilationUnit(this, req.get().getIncludedFiles()); } catch (InterruptedException e) { // this should never happen, as req is only ever called when it's already done. } } /** * This method is overriden by base classes to * clean additional processing results not cleaned by * the clean method above. * @param invalidatedSWCFiles */ protected void handleClean(boolean clearFileScope, Map<ICompilerProject, Set<File>> invalidatedSWCFiles) { } private void updateDefinitions(Map<ICompilerProject, Set<ICompilationUnit>> cusToUpdate) { // If this compilation unit is the delegate // of an invisible compilation unit, then do // *not* add any definition to the project // symbol table. if (isInvisible()) return; // Add back in any definition promises or FileScopes depending how the // input file was added to the project if (definitionPromises.isEmpty()) // no definitionPromises, so parse file { Set<ICompilationUnit> cus = cusToUpdate.get(getProject()); if (cus == null) { cus = new HashSet<ICompilationUnit>(); cusToUpdate.put(getProject(), cus); } cus.add(this); } else // add definition promises { for (IDefinition definitionPromise : definitionPromises) { if (definitionPromise instanceof DefinitionPromise) ((DefinitionPromise)definitionPromise).clean(); project.getScope().addDefinition(definitionPromise); } } } protected final void startProfile(Operation operation) { IWorkspaceProfilingDelegate profilingDelegate = project.getWorkspace().getProfilingDelegate(); if (profilingDelegate == null) return; profilingDelegate.operationStarted(this, operation); } protected final void stopProfile(Operation operation) { IWorkspaceProfilingDelegate profilingDelegate = project.getWorkspace().getProfilingDelegate(); if (profilingDelegate == null) return; profilingDelegate.operationCompleted(this, operation); } private ISyntaxTreeRequestResult processSyntaxTreeRequest() throws InterruptedException { ISyntaxTreeRequestResult result = handleSyntaxTreeRequest(); IASNode ast = result.getAST(); verifyAST(ast); operationComplete(ICompilationUnit.Operation.GET_SYNTAX_TREE); return result; } protected void verifyAST(IASNode ast) { if (ast != null) assert ((NodeBase)ast).verify() : "AST failed verification"; } protected final void addScopeToProjectScope(ASFileScope[] scopes) { ASProjectScope projectScope = project.getScope(); for (ASFileScope scope : scopes) { assert scope.verify() : "Scope failed verification"; projectScope.addScopeForCompilationUnit(this, scope); } } private IFileScopeRequestResult processFileScopeRequest() throws InterruptedException { FileScopeRequestResultBase result = (FileScopeRequestResultBase)handleFileScopeRequest(); // add the scopes for this compilation unit ASFileScope[] scopes = result.getFileScopes(); addScopeToProjectScope(scopes); operationComplete(ICompilationUnit.Operation.GET_FILESCOPE); return result; } private IABCBytesRequestResult processABCBytesRequest() throws InterruptedException { IABCBytesRequestResult result = handleABCBytesRequest(); operationComplete(ICompilationUnit.Operation.GET_ABC_BYTES); removeAST(); return result; } private ISWFTagsRequestResult processSWFTTagsRequest() throws InterruptedException { ISWFTagsRequestResult result = handleSWFTagsRequest(); operationComplete(ICompilationUnit.Operation.GET_SWF_TAGS); return result; } private IOutgoingDependenciesRequestResult processOutgoingDependenciesRequest () throws InterruptedException { IOutgoingDependenciesRequestResult result = handleOutgoingDependenciesRequest(); operationComplete(ICompilationUnit.Operation.GET_SEMANTIC_PROBLEMS); removeAST(); return result; } /** * Iterate through all imports within a IFileNodeAccumulator and start parsing any * non-wildcard imports. This will help speed discovery of dependencies to * allow better thread utilization. * * @param fna IFileNodeAccumulator */ protected void startParsingImports(IFileNodeAccumulator fna) { ASProjectScope projectScope = getProject().getScope(); List<IImportNode> importNodes = fna.getImportNodes(); Set<ICompilationUnit> compilationUnits = new HashSet<ICompilationUnit>(); for (IImportNode importNode : importNodes) { if (!importNode.isWildcardImport()) { String importName = importNode.getImportName(); int index = importName.lastIndexOf('.'); if (index != -1) { String className = importName.substring(index + 1); compilationUnits.addAll(projectScope.getCompilationUnitsByDefinitionName(className)); } } } // Now that we have the compilation units from the imports, // start a parallel build on them. for (ICompilationUnit compilationUnit : compilationUnits) { compilationUnit.startBuildAsync(TargetType.SWF); } } /** * Iterate through all specified embeds, adding the * EmbedCompilationUnit dependencies, and removing any EmbedCompilationUnits * which no longer exist * <p> * This function adds an empty definition dependency to the {@link DependencyGraph}, * so it will not be printed to the link-report * @throws InterruptedException */ protected void updateEmbedCompilationUnitDependencies(List<IEmbedResolver> embedNodes, Collection<ICompilerProblem> problems) throws InterruptedException { Set<ICompilationUnit> previousEmbedCompilationUnits = new HashSet<ICompilationUnit>(embedCompilationUnits); embedCompilationUnits.clear(); CompilerProject project = getProject(); for (IEmbedResolver embedNode : embedNodes) { ICompilationUnit cu = embedNode.resolveCompilationUnit(project); if (cu != null) { // if the cu has already been part of this CU, nothing to do if (!previousEmbedCompilationUnits.remove(cu)) { // this dependency targets the embedNode's (import etc) data qname project.addDependency(this, cu, DependencyType.EXPRESSION, cu.getQualifiedNames().get(0)); } embedCompilationUnits.add(cu); } } // remove any CUs which are no longer referenced in this file project.removeCompilationUnits(previousEmbedCompilationUnits); } @Override public Collection<String> getEmbeddedFilenames() { if (embedCompilationUnits.isEmpty()) return Collections.emptySet(); Set<String> filenames = new HashSet<String>(); for (ICompilationUnit cu : embedCompilationUnits) { EmbedData data = ((EmbedCompilationUnit)cu).getEmbedData(); String sourcePath = (String)data.getAttribute(EmbedAttribute.SOURCE); // the source attribute can be null or empty if the file doesn't exist // or wasn't specified. if (sourcePath != null && !sourcePath.isEmpty()) filenames.add(sourcePath); } return filenames; } protected void removeAST() { // don't do anything by default } @Override public String getName() { assert name.equals(computeName()); // verify that our cache is valid, but only if asserts are enabled return name; } /** * Puts together the compilation unit's name. * Normally would be cached, as it can be slow. */ private String computeName() { final String filename = FilenameUtils.getName(getAbsoluteFilename()).replace('.', '_'); final String encodedAbsolutePath = StringEncoder.stringToHashCodeString(getAbsoluteFilename()); String encodedName = encodedAbsolutePath + ":" + filename; if (definitionPromises.isEmpty()) { return encodedName; } try { List<String> qualifiedNames = getQualifiedNames(); Collections.sort(qualifiedNames, new LinkReportWriter.QNameComparator()); return encodedName + ":" + Joiner.on(' ').join(qualifiedNames); } catch (InterruptedException e) { } assert false : "Should not get here, because we should be able to compute qnames for def promises without getting interrupted!"; return null; } /** * @return a string, which is helpful when inspecting in a debugger. */ @Override public String toString() { return getAbsoluteFilename(); } @Override public IDefinitionPriority getDefinitionPriority() { return definitionPriority; } @Override public void clearProject() { project = null; } @Override public void waitForBuildFinish(final Collection<ICompilerProblem> problems, TargetType targetType) throws InterruptedException { assert problems != null : "Expected 'problems'. Do not ignore problems."; Collections.addAll(problems, getSyntaxTreeRequest().get().getProblems()); Collections.addAll(problems, getFileScopeRequest().get().getProblems()); Collections.addAll(problems, getOutgoingDependenciesRequest().get().getProblems()); Collections.addAll(problems, getABCBytesRequest().get().getProblems()); Collections.addAll(problems, getSWFTagsRequest().get().getProblems()); } @Override public void startBuildAsync(TargetType targetType) { boolean onlyDoOutgoing = false; if (!onlyDoOutgoing) { getSyntaxTreeRequest(); getFileScopeRequest(); } getOutgoingDependenciesRequest(); if (!onlyDoOutgoing) { getABCBytesRequest(); getSWFTagsRequest(); } } /* * Sets the {@link IFileSpecificationGetter} used by this * {@link ICompilationUnit} to open files. * <p> * This method should be called very shortly after the constructor and * before any other methods on this class or its sub-classes are called * (except {@link #makeInvisible(InvisibleCompilationUnit)}). * The {@link IFileSpecificationGetter} * should really be a constructor argument, but that would require updating * all the sub-classes of this class and the factory class that constructs * {@link ICompilationUnit}'s. * * @param getter The {@link IFileSpecificationGetter} used by this * {@link ICompilationUnit} to open files. */ public void setFileSpecificationGetter(IFileSpecificationGetter getter) { fileSpecificationGetter = getter; } /** * Marks this {@link ICompilationUnit} as the delegate of an * {@link IInvisibleCompilationUnit}, which will prevent this * {@link ICompilationUnit} from contributing any symbols to the containing * {@link ICompilerProject}'s {@link ASProjectScope}. * <p> * This method should be called very shortly after the constructor and * before any other methods on this class or its sub-classes are called ( * except {@link #setFileSpecificationGetter(IFileSpecificationGetter)} ). * <p> * This method must only be called zero or one time on each * {@link ICompilationUnit}. */ public void makeInvisible(InvisibleCompilationUnit invisibleCU) { assert invisibleCURef == null : "makeInvisible should called exactly once or not at all"; invisibleCURef = new InvisibleCompilationUnitRef(this, invisibleCU, getProject().getWorkspace().getInvisibleCompilationUnitReferenceQueue()); } @Override public final boolean isInvisible() { return invisibleCURef != null; } protected Map<String, String> getEncodedDebugFiles() throws InterruptedException { assert (this instanceof ASCompilationUnit || this instanceof MXMLCompilationUnit) : "getEncodedDebugFiles should only be called by AS or MXML compilation units"; // only file on the source path are encoded, and only ASProject's have // source path, so bail if it's not an ASProject if (!(getProject() instanceof ASProject)) return Collections.<String, String>emptyMap(); ASProject asProject = (ASProject)getProject(); // bail if the file isn't on the source path File sourceFile = new File(getAbsoluteFilename()); if (!asProject.isFileOnSourcePath(sourceFile)) return Collections.<String, String>emptyMap(); // as we're only dealing with AS or MXML compilation units on the source path, // there should only ever be one definition, so just grab the first def. IDefinition def = Iterables.getOnlyElement(getAllDefinitions()); String packagePath = def.getPackageName().replace('.', File.separatorChar); String filenameNoPath = getFilenameNoPath(); String rootPath; if (packagePath.isEmpty()) rootPath = getAbsoluteFilename().replace(File.separatorChar + filenameNoPath, ""); else rootPath = getAbsoluteFilename().replace(File.separatorChar + packagePath + File.separatorChar + filenameNoPath, ""); String encodedPath = rootPath + ';' + packagePath + ';' + getFilenameNoPath(); Map<String, String> encodedDebugFiles = new HashMap<String, String>(1); encodedDebugFiles.put(getAbsoluteFilename(), encodedPath); return encodedDebugFiles; } }