| /* |
| * Copyright (c) 2013, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html |
| * |
| * 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 com.google.dart.engine.internal.resolver; |
| |
| import com.google.dart.engine.AnalysisEngine; |
| import com.google.dart.engine.ast.Combinator; |
| import com.google.dart.engine.ast.CompilationUnit; |
| import com.google.dart.engine.ast.CompilationUnitMember; |
| import com.google.dart.engine.ast.Directive; |
| import com.google.dart.engine.ast.ExportDirective; |
| import com.google.dart.engine.ast.FunctionTypeAlias; |
| import com.google.dart.engine.ast.HideCombinator; |
| import com.google.dart.engine.ast.ImportDirective; |
| import com.google.dart.engine.ast.NamespaceDirective; |
| import com.google.dart.engine.ast.NodeList; |
| import com.google.dart.engine.ast.ShowCombinator; |
| import com.google.dart.engine.ast.SimpleIdentifier; |
| import com.google.dart.engine.ast.StringInterpolation; |
| import com.google.dart.engine.ast.StringLiteral; |
| import com.google.dart.engine.ast.TypeAlias; |
| import com.google.dart.engine.ast.UriBasedDirective; |
| import com.google.dart.engine.context.AnalysisException; |
| import com.google.dart.engine.element.Element; |
| import com.google.dart.engine.element.ExportElement; |
| import com.google.dart.engine.element.FunctionElement; |
| import com.google.dart.engine.element.ImportElement; |
| import com.google.dart.engine.element.LibraryElement; |
| import com.google.dart.engine.element.NamespaceCombinator; |
| import com.google.dart.engine.error.AnalysisError; |
| import com.google.dart.engine.error.AnalysisErrorListener; |
| import com.google.dart.engine.error.CompileTimeErrorCode; |
| import com.google.dart.engine.error.ErrorCode; |
| import com.google.dart.engine.error.StaticWarningCode; |
| import com.google.dart.engine.internal.builder.AngularCompilationUnitBuilder; |
| import com.google.dart.engine.internal.builder.EnumMemberBuilder; |
| import com.google.dart.engine.internal.builder.PolymerCompilationUnitBuilder; |
| import com.google.dart.engine.internal.constant.ConstantValueComputer; |
| import com.google.dart.engine.internal.context.InternalAnalysisContext; |
| import com.google.dart.engine.internal.context.PerformanceStatistics; |
| import com.google.dart.engine.internal.context.RecordingErrorListener; |
| import com.google.dart.engine.internal.element.ExportElementImpl; |
| import com.google.dart.engine.internal.element.HideElementCombinatorImpl; |
| import com.google.dart.engine.internal.element.ImportElementImpl; |
| import com.google.dart.engine.internal.element.LibraryElementImpl; |
| import com.google.dart.engine.internal.element.PrefixElementImpl; |
| import com.google.dart.engine.internal.element.ShowElementCombinatorImpl; |
| import com.google.dart.engine.internal.scope.Namespace; |
| import com.google.dart.engine.internal.scope.NamespaceBuilder; |
| import com.google.dart.engine.sdk.DartSdk; |
| import com.google.dart.engine.source.DartUriResolver; |
| import com.google.dart.engine.source.Source; |
| import com.google.dart.engine.source.SourceKind; |
| import com.google.dart.engine.utilities.general.TimeCounter.TimeCounterHandle; |
| import com.google.dart.engine.utilities.instrumentation.Instrumentation; |
| import com.google.dart.engine.utilities.instrumentation.InstrumentationBuilder; |
| import com.google.dart.engine.utilities.io.UriUtilities; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Instances of the class {@code LibraryResolver} are used to resolve one or more mutually dependent |
| * libraries within a single context. |
| * |
| * @coverage dart.engine.resolver |
| */ |
| public class LibraryResolver { |
| /** |
| * Instances of the class {@code TypeAliasInfo} hold information about a {@link TypeAlias}. |
| */ |
| private static class TypeAliasInfo { |
| private Library library; |
| private Source source; |
| private FunctionTypeAlias typeAlias; |
| |
| /** |
| * Initialize a newly created information holder with the given information. |
| * |
| * @param library the library containing the type alias |
| * @param source the source of the file containing the type alias |
| * @param typeAlias the type alias being remembered |
| */ |
| public TypeAliasInfo(Library library, Source source, FunctionTypeAlias typeAlias) { |
| this.library = library; |
| this.source = source; |
| this.typeAlias = typeAlias; |
| } |
| } |
| |
| /** |
| * The analysis context in which the libraries are being analyzed. |
| */ |
| private InternalAnalysisContext analysisContext; |
| |
| /** |
| * The listener to which analysis errors will be reported, this error listener is either |
| * references {@link #recordingErrorListener}, or it unions the passed |
| * {@link AnalysisErrorListener} with the {@link #recordingErrorListener}. |
| */ |
| private RecordingErrorListener errorListener; |
| |
| /** |
| * A source object representing the core library (dart:core). |
| */ |
| private Source coreLibrarySource; |
| |
| /** |
| * The object representing the core library. |
| */ |
| private Library coreLibrary; |
| |
| /** |
| * The object used to access the types from the core library. |
| */ |
| private TypeProvider typeProvider; |
| |
| /** |
| * A table mapping library sources to the information being maintained for those libraries. |
| */ |
| private HashMap<Source, Library> libraryMap = new HashMap<Source, Library>(); |
| |
| /** |
| * A collection containing the libraries that are being resolved together. |
| */ |
| private Set<Library> librariesInCycles; |
| |
| /** |
| * Initialize a newly created library resolver to resolve libraries within the given context. |
| * |
| * @param analysisContext the analysis context in which the library is being analyzed |
| */ |
| public LibraryResolver(InternalAnalysisContext analysisContext) { |
| this.analysisContext = analysisContext; |
| this.errorListener = new RecordingErrorListener(); |
| coreLibrarySource = analysisContext.getSourceFactory().forUri(DartSdk.DART_CORE); |
| } |
| |
| /** |
| * Return the analysis context in which the libraries are being analyzed. |
| * |
| * @return the analysis context in which the libraries are being analyzed |
| */ |
| public InternalAnalysisContext getAnalysisContext() { |
| return analysisContext; |
| } |
| |
| /** |
| * Return the listener to which analysis errors will be reported. |
| * |
| * @return the listener to which analysis errors will be reported |
| */ |
| public RecordingErrorListener getErrorListener() { |
| return errorListener; |
| } |
| |
| /** |
| * Return an array containing information about all of the libraries that were resolved. |
| * |
| * @return an array containing the libraries that were resolved |
| */ |
| public Set<Library> getResolvedLibraries() { |
| return librariesInCycles; |
| } |
| |
| /** |
| * Resolve the library specified by the given source in the given context. The library is assumed |
| * to be embedded in the given source. |
| * |
| * @param librarySource the source specifying the defining compilation unit of the library to be |
| * resolved |
| * @param modificationStamp the time stamp of the source from which the compilation unit was |
| * created |
| * @param unit the compilation unit representing the embedded library |
| * @param fullAnalysis {@code true} if a full analysis should be performed |
| * @return the element representing the resolved library |
| * @throws AnalysisException if the library could not be resolved for some reason |
| */ |
| public LibraryElement resolveEmbeddedLibrary(Source librarySource, long modificationStamp, |
| CompilationUnit unit, boolean fullAnalysis) throws AnalysisException { |
| |
| InstrumentationBuilder instrumentation = Instrumentation.builder("dart.engine.LibraryResolver.resolveEmbeddedLibrary"); |
| try { |
| instrumentation.metric("fullAnalysis", fullAnalysis); |
| instrumentation.data("fullName", librarySource.getFullName()); |
| // |
| // Create the objects representing the library being resolved and the core library. |
| // |
| Library targetLibrary = createLibraryWithUnit(librarySource, modificationStamp, unit); |
| coreLibrary = libraryMap.get(coreLibrarySource); |
| if (coreLibrary == null) { |
| // This will be true unless the library being analyzed is the core library. |
| coreLibrary = createLibrary(coreLibrarySource); |
| if (coreLibrary == null) { |
| LibraryResolver2.missingCoreLibrary(analysisContext, coreLibrarySource); |
| } |
| } |
| instrumentation.metric("createLibrary", "complete"); |
| // |
| // Compute the set of libraries that need to be resolved together. |
| // |
| computeEmbeddedLibraryDependencies(targetLibrary, unit); |
| librariesInCycles = computeLibrariesInCycles(targetLibrary); |
| // |
| // Build the element models representing the libraries being resolved. This is done in three |
| // steps: |
| // |
| // 1. Build the basic element models without making any connections between elements other than |
| // the basic parent/child relationships. This includes building the elements representing the |
| // libraries. |
| // 2. Build the elements for the import and export directives. This requires that we have the |
| // elements built for the referenced libraries, but because of the possibility of circular |
| // references needs to happen after all of the library elements have been created. |
| // 3. Build the rest of the type model by connecting superclasses, mixins, and interfaces. This |
| // requires that we be able to compute the names visible in the libraries being resolved, |
| // which in turn requires that we have resolved the import directives. |
| // |
| buildElementModels(); |
| instrumentation.metric("buildElementModels", "complete"); |
| LibraryElement coreElement = coreLibrary.getLibraryElement(); |
| if (coreElement == null) { |
| throw new AnalysisException("Could not resolve dart:core"); |
| } |
| buildDirectiveModels(); |
| instrumentation.metric("buildDirectiveModels", "complete"); |
| typeProvider = new TypeProviderImpl(coreElement); |
| buildTypeAliases(); |
| buildTypeHierarchies(); |
| instrumentation.metric("buildTypeHierarchies", "complete"); |
| // |
| // Perform resolution and type analysis. |
| // |
| // TODO(brianwilkerson) Decide whether we want to resolve all of the libraries or whether we |
| // want to only resolve the target library. The advantage to resolving everything is that we |
| // have already done part of the work so we'll avoid duplicated effort. The disadvantage of |
| // resolving everything is that we might do extra work that we don't really care about. Another |
| // possibility is to add a parameter to this method and punt the decision to the clients. |
| // |
| //if (analyzeAll) { |
| resolveReferencesAndTypes(); |
| instrumentation.metric("resolveReferencesAndTypes", "complete"); |
| //} else { |
| // resolveReferencesAndTypes(targetLibrary); |
| //} |
| performConstantEvaluation(); |
| instrumentation.metric("performConstantEvaluation", "complete"); |
| return targetLibrary.getLibraryElement(); |
| } finally { |
| instrumentation.log(); |
| } |
| } |
| |
| /** |
| * Resolve the library specified by the given source in the given context. |
| * <p> |
| * Note that because Dart allows circular imports between libraries, it is possible that more than |
| * one library will need to be resolved. In such cases the error listener can receive errors from |
| * multiple libraries. |
| * |
| * @param librarySource the source specifying the defining compilation unit of the library to be |
| * resolved |
| * @param fullAnalysis {@code true} if a full analysis should be performed |
| * @return the element representing the resolved library |
| * @throws AnalysisException if the library could not be resolved for some reason |
| */ |
| public LibraryElement resolveLibrary(Source librarySource, boolean fullAnalysis) |
| throws AnalysisException { |
| InstrumentationBuilder instrumentation = Instrumentation.builder("dart.engine.LibraryResolver.resolveLibrary"); |
| try { |
| instrumentation.metric("fullAnalysis", fullAnalysis); |
| instrumentation.data("fullName", librarySource.getFullName()); |
| // |
| // Create the objects representing the library being resolved and the core library. |
| // |
| Library targetLibrary = createLibrary(librarySource); |
| coreLibrary = libraryMap.get(coreLibrarySource); |
| if (coreLibrary == null) { |
| // This will be true unless the library being analyzed is the core library. |
| coreLibrary = createLibraryOrNull(coreLibrarySource); |
| if (coreLibrary == null) { |
| LibraryResolver2.missingCoreLibrary(analysisContext, coreLibrarySource); |
| } |
| } |
| instrumentation.metric("createLibrary", "complete"); |
| // |
| // Compute the set of libraries that need to be resolved together. |
| // |
| computeLibraryDependencies(targetLibrary); |
| librariesInCycles = computeLibrariesInCycles(targetLibrary); |
| // |
| // Build the element models representing the libraries being resolved. This is done in three |
| // steps: |
| // |
| // 1. Build the basic element models without making any connections between elements other |
| // than the basic parent/child relationships. This includes building the elements |
| // representing the libraries, but excludes members defined in enums. |
| // 2. Build the elements for the import and export directives. This requires that we have the |
| // elements built for the referenced libraries, but because of the possibility of circular |
| // references needs to happen after all of the library elements have been created. |
| // 3. Build the members in enum declarations. |
| // 4. Build the rest of the type model by connecting superclasses, mixins, and interfaces. This |
| // requires that we be able to compute the names visible in the libraries being resolved, |
| // which in turn requires that we have resolved the import directives. |
| // |
| buildElementModels(); |
| instrumentation.metric("buildElementModels", "complete"); |
| LibraryElement coreElement = coreLibrary.getLibraryElement(); |
| if (coreElement == null) { |
| throw new AnalysisException("Could not resolve dart:core"); |
| } |
| buildDirectiveModels(); |
| instrumentation.metric("buildDirectiveModels", "complete"); |
| typeProvider = new TypeProviderImpl(coreElement); |
| buildEnumMembers(); |
| buildTypeAliases(); |
| buildTypeHierarchies(); |
| buildImplicitConstructors(); |
| instrumentation.metric("buildTypeHierarchies", "complete"); |
| // |
| // Perform resolution and type analysis. |
| // |
| // TODO(brianwilkerson) Decide whether we want to resolve all of the libraries or whether we |
| // want to only resolve the target library. The advantage to resolving everything is that we |
| // have already done part of the work so we'll avoid duplicated effort. The disadvantage of |
| // resolving everything is that we might do extra work that we don't really care about. Another |
| // possibility is to add a parameter to this method and punt the decision to the clients. |
| // |
| //if (analyzeAll) { |
| resolveReferencesAndTypes(); |
| instrumentation.metric("resolveReferencesAndTypes", "complete"); |
| //} else { |
| // resolveReferencesAndTypes(targetLibrary); |
| //} |
| performConstantEvaluation(); |
| instrumentation.metric("performConstantEvaluation", "complete"); |
| instrumentation.metric("librariesInCycles", librariesInCycles.size()); |
| for (Library lib : librariesInCycles) { |
| instrumentation.metric( |
| "librariesInCycles-CompilationUnitSources-Size", |
| lib.getCompilationUnitSources().size()); |
| } |
| |
| return targetLibrary.getLibraryElement(); |
| } finally { |
| instrumentation.log(15); //Log if >= than 15ms |
| } |
| } |
| |
| /** |
| * Create an object to represent the information about the library defined by the compilation unit |
| * with the given source. |
| * |
| * @param librarySource the source of the library's defining compilation unit |
| * @return the library object that was created |
| * @throws AnalysisException if the library source is not valid |
| */ |
| protected Library createLibrary(Source librarySource) throws AnalysisException { |
| Library library = new Library(analysisContext, errorListener, librarySource); |
| libraryMap.put(librarySource, library); |
| return library; |
| } |
| |
| /** |
| * Add a dependency to the given map from the referencing library to the referenced library. |
| * |
| * @param dependencyMap the map to which the dependency is to be added |
| * @param referencingLibrary the library that references the referenced library |
| * @param referencedLibrary the library referenced by the referencing library |
| */ |
| private void addDependencyToMap(HashMap<Library, ArrayList<Library>> dependencyMap, |
| Library referencingLibrary, Library referencedLibrary) { |
| ArrayList<Library> dependentLibraries = dependencyMap.get(referencedLibrary); |
| if (dependentLibraries == null) { |
| dependentLibraries = new ArrayList<Library>(); |
| dependencyMap.put(referencedLibrary, dependentLibraries); |
| } |
| dependentLibraries.add(referencingLibrary); |
| } |
| |
| /** |
| * Given a library that is part of a cycle that includes the root library, add to the given set of |
| * libraries all of the libraries reachable from the root library that are also included in the |
| * cycle. |
| * |
| * @param library the library to be added to the collection of libraries in cycles |
| * @param librariesInCycle a collection of the libraries that are in the cycle |
| * @param dependencyMap a table mapping libraries to the collection of libraries from which those |
| * libraries are referenced |
| */ |
| private void addLibrariesInCycle(Library library, Set<Library> librariesInCycle, |
| HashMap<Library, ArrayList<Library>> dependencyMap) { |
| if (librariesInCycle.add(library)) { |
| ArrayList<Library> dependentLibraries = dependencyMap.get(library); |
| if (dependentLibraries != null) { |
| for (Library dependentLibrary : dependentLibraries) { |
| addLibrariesInCycle(dependentLibrary, librariesInCycle, dependencyMap); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Add the given library, and all libraries reachable from it that have not already been visited, |
| * to the given dependency map. |
| * |
| * @param library the library currently being added to the dependency map |
| * @param dependencyMap the dependency map being computed |
| * @param visitedLibraries the libraries that have already been visited, used to prevent infinite |
| * recursion |
| */ |
| private void addToDependencyMap(Library library, |
| HashMap<Library, ArrayList<Library>> dependencyMap, Set<Library> visitedLibraries) { |
| if (visitedLibraries.add(library)) { |
| for (Library referencedLibrary : library.getImportsAndExports()) { |
| addDependencyToMap(dependencyMap, library, referencedLibrary); |
| addToDependencyMap(referencedLibrary, dependencyMap, visitedLibraries); |
| } |
| if (!library.getExplicitlyImportsCore() && library != coreLibrary) { |
| addDependencyToMap(dependencyMap, library, coreLibrary); |
| } |
| } |
| } |
| |
| /** |
| * Build the element model representing the combinators declared by the given directive. |
| * |
| * @param directive the directive that declares the combinators |
| * @return an array containing the import combinators that were built |
| */ |
| // TODO(brianwilkerson) Move with buildDirectiveModels(). |
| private NamespaceCombinator[] buildCombinators(NamespaceDirective directive) { |
| ArrayList<NamespaceCombinator> combinators = new ArrayList<NamespaceCombinator>(); |
| for (Combinator combinator : directive.getCombinators()) { |
| if (combinator instanceof HideCombinator) { |
| HideElementCombinatorImpl hide = new HideElementCombinatorImpl(); |
| hide.setHiddenNames(getIdentifiers(((HideCombinator) combinator).getHiddenNames())); |
| combinators.add(hide); |
| } else { |
| ShowElementCombinatorImpl show = new ShowElementCombinatorImpl(); |
| show.setOffset(combinator.getOffset()); |
| show.setEnd(combinator.getEnd()); |
| show.setShownNames(getIdentifiers(((ShowCombinator) combinator).getShownNames())); |
| combinators.add(show); |
| } |
| } |
| return combinators.toArray(new NamespaceCombinator[combinators.size()]); |
| } |
| |
| /** |
| * Every library now has a corresponding {@link LibraryElement}, so it is now possible to resolve |
| * the import and export directives. |
| * |
| * @throws AnalysisException if the defining compilation unit for any of the libraries could not |
| * be accessed |
| */ |
| // TODO(brianwilkerson) The body of this method probably wants to be moved into a separate class. |
| private void buildDirectiveModels() throws AnalysisException { |
| for (Library library : librariesInCycles) { |
| HashMap<String, PrefixElementImpl> nameToPrefixMap = new HashMap<String, PrefixElementImpl>(); |
| ArrayList<ImportElement> imports = new ArrayList<ImportElement>(); |
| ArrayList<ExportElement> exports = new ArrayList<ExportElement>(); |
| for (Directive directive : library.getDefiningCompilationUnit().getDirectives()) { |
| if (directive instanceof ImportDirective) { |
| ImportDirective importDirective = (ImportDirective) directive; |
| String uriContent = importDirective.getUriContent(); |
| if (DartUriResolver.isDartExtUri(uriContent)) { |
| library.getLibraryElement().setHasExtUri(true); |
| } |
| Source importedSource = importDirective.getSource(); |
| if (importedSource != null) { |
| // The imported source will be null if the URI in the import directive was invalid. |
| Library importedLibrary = libraryMap.get(importedSource); |
| if (importedLibrary != null) { |
| ImportElementImpl importElement = new ImportElementImpl(directive.getOffset()); |
| StringLiteral uriLiteral = importDirective.getUri(); |
| importElement.setUriOffset(uriLiteral.getOffset()); |
| importElement.setUriEnd(uriLiteral.getEnd()); |
| importElement.setUri(uriContent); |
| importElement.setDeferred(importDirective.getDeferredToken() != null); |
| importElement.setCombinators(buildCombinators(importDirective)); |
| LibraryElement importedLibraryElement = importedLibrary.getLibraryElement(); |
| if (importedLibraryElement != null) { |
| importElement.setImportedLibrary(importedLibraryElement); |
| } |
| SimpleIdentifier prefixNode = ((ImportDirective) directive).getPrefix(); |
| if (prefixNode != null) { |
| importElement.setPrefixOffset(prefixNode.getOffset()); |
| String prefixName = prefixNode.getName(); |
| PrefixElementImpl prefix = nameToPrefixMap.get(prefixName); |
| if (prefix == null) { |
| prefix = new PrefixElementImpl(prefixNode); |
| nameToPrefixMap.put(prefixName, prefix); |
| } |
| importElement.setPrefix(prefix); |
| prefixNode.setStaticElement(prefix); |
| } |
| directive.setElement(importElement); |
| imports.add(importElement); |
| |
| if (analysisContext.computeKindOf(importedSource) != SourceKind.LIBRARY) { |
| ErrorCode errorCode = importElement.isDeferred() |
| ? StaticWarningCode.IMPORT_OF_NON_LIBRARY |
| : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY; |
| errorListener.onError(new AnalysisError( |
| library.getLibrarySource(), |
| uriLiteral.getOffset(), |
| uriLiteral.getLength(), |
| errorCode, |
| uriLiteral.toSource())); |
| } |
| } |
| } |
| } else if (directive instanceof ExportDirective) { |
| ExportDirective exportDirective = (ExportDirective) directive; |
| Source exportedSource = exportDirective.getSource(); |
| if (exportedSource != null) { |
| // The exported source will be null if the URI in the export directive was invalid. |
| Library exportedLibrary = libraryMap.get(exportedSource); |
| if (exportedLibrary != null) { |
| ExportElementImpl exportElement = new ExportElementImpl(); |
| StringLiteral uriLiteral = exportDirective.getUri(); |
| exportElement.setUriOffset(uriLiteral.getOffset()); |
| exportElement.setUriEnd(uriLiteral.getEnd()); |
| exportElement.setUri(exportDirective.getUriContent()); |
| exportElement.setCombinators(buildCombinators(exportDirective)); |
| LibraryElement exportedLibraryElement = exportedLibrary.getLibraryElement(); |
| if (exportedLibraryElement != null) { |
| exportElement.setExportedLibrary(exportedLibraryElement); |
| } |
| directive.setElement(exportElement); |
| exports.add(exportElement); |
| |
| if (analysisContext.computeKindOf(exportedSource) != SourceKind.LIBRARY) { |
| errorListener.onError(new AnalysisError( |
| library.getLibrarySource(), |
| uriLiteral.getOffset(), |
| uriLiteral.getLength(), |
| CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY, |
| uriLiteral.toSource())); |
| } |
| } |
| } |
| } |
| } |
| Source librarySource = library.getLibrarySource(); |
| if (!library.getExplicitlyImportsCore() && !coreLibrarySource.equals(librarySource)) { |
| ImportElementImpl importElement = new ImportElementImpl(-1); |
| importElement.setImportedLibrary(coreLibrary.getLibraryElement()); |
| importElement.setSynthetic(true); |
| imports.add(importElement); |
| } |
| LibraryElementImpl libraryElement = library.getLibraryElement(); |
| libraryElement.setImports(imports.toArray(new ImportElement[imports.size()])); |
| libraryElement.setExports(exports.toArray(new ExportElement[exports.size()])); |
| if (libraryElement.getEntryPoint() == null) { |
| Namespace namespace = new NamespaceBuilder().createExportNamespaceForLibrary(libraryElement); |
| Element element = namespace.get(LibraryElementBuilder.ENTRY_POINT_NAME); |
| if (element instanceof FunctionElement) { |
| libraryElement.setEntryPoint((FunctionElement) element); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Build element models for all of the libraries in the current cycle. |
| * |
| * @throws AnalysisException if any of the element models cannot be built |
| */ |
| private void buildElementModels() throws AnalysisException { |
| for (Library library : librariesInCycles) { |
| LibraryElementBuilder builder = new LibraryElementBuilder( |
| getAnalysisContext(), |
| getErrorListener()); |
| LibraryElementImpl libraryElement = builder.buildLibrary(library); |
| library.setLibraryElement(libraryElement); |
| } |
| } |
| |
| /** |
| * Build the members in enum declarations. This cannot be done while building the rest of the |
| * element model because it depends on being able to access core types, which cannot happen until |
| * the rest of the element model has been built (when resolving the core library). |
| * |
| * @throws AnalysisException if any of the enum members could not be built |
| */ |
| private void buildEnumMembers() throws AnalysisException { |
| TimeCounterHandle timeCounter = PerformanceStatistics.resolve.start(); |
| try { |
| for (Library library : librariesInCycles) { |
| for (Source source : library.getCompilationUnitSources()) { |
| EnumMemberBuilder builder = new EnumMemberBuilder(typeProvider); |
| library.getAST(source).accept(builder); |
| } |
| } |
| } finally { |
| timeCounter.stop(); |
| } |
| } |
| |
| /** |
| * Finish steps that the {@link #buildTypeHierarchies()} could not perform, see |
| * {@link ImplicitConstructorBuilder}. |
| * |
| * @throws AnalysisException if any of the type hierarchies could not be resolved |
| */ |
| private void buildImplicitConstructors() throws AnalysisException { |
| TimeCounterHandle timeCounter = PerformanceStatistics.resolve.start(); |
| try { |
| for (Library library : librariesInCycles) { |
| for (Source source : library.getCompilationUnitSources()) { |
| ImplicitConstructorBuilder visitor = new ImplicitConstructorBuilder( |
| library, |
| source, |
| typeProvider); |
| library.getAST(source).accept(visitor); |
| } |
| } |
| } finally { |
| timeCounter.stop(); |
| } |
| } |
| |
| /** |
| * Resolve the types referenced by function type aliases across all of the function type aliases |
| * defined in the current cycle. |
| * |
| * @throws AnalysisException if any of the function type aliases could not be resolved |
| */ |
| private void buildTypeAliases() throws AnalysisException { |
| TimeCounterHandle timeCounter = PerformanceStatistics.resolve.start(); |
| try { |
| List<TypeAliasInfo> typeAliases = new ArrayList<TypeAliasInfo>(); |
| for (Library library : librariesInCycles) { |
| for (Source source : library.getCompilationUnitSources()) { |
| CompilationUnit ast = library.getAST(source); |
| for (CompilationUnitMember member : ast.getDeclarations()) { |
| if (member instanceof FunctionTypeAlias) { |
| typeAliases.add(new TypeAliasInfo(library, source, (FunctionTypeAlias) member)); |
| } |
| } |
| } |
| } |
| // TODO(brianwilkerson) We need to sort the type aliases such that all aliases referenced by |
| // an alias T are resolved before we resolve T. |
| for (TypeAliasInfo info : typeAliases) { |
| TypeResolverVisitor visitor = new TypeResolverVisitor( |
| info.library, |
| info.source, |
| typeProvider); |
| info.typeAlias.accept(visitor); |
| } |
| } finally { |
| timeCounter.stop(); |
| } |
| } |
| |
| /** |
| * Resolve the type hierarchy across all of the types declared in the libraries in the current |
| * cycle. |
| * |
| * @throws AnalysisException if any of the type hierarchies could not be resolved |
| */ |
| private void buildTypeHierarchies() throws AnalysisException { |
| TimeCounterHandle timeCounter = PerformanceStatistics.resolve.start(); |
| try { |
| for (Library library : librariesInCycles) { |
| for (Source source : library.getCompilationUnitSources()) { |
| TypeResolverVisitor visitor = new TypeResolverVisitor(library, source, typeProvider); |
| library.getAST(source).accept(visitor); |
| } |
| } |
| } finally { |
| timeCounter.stop(); |
| } |
| } |
| |
| /** |
| * Compute a dependency map of libraries reachable from the given library. A dependency map is a |
| * table that maps individual libraries to a list of the libraries that either import or export |
| * those libraries. |
| * <p> |
| * This map is used to compute all of the libraries involved in a cycle that include the root |
| * library. Given that we only add libraries that are reachable from the root library, when we |
| * work backward we are guaranteed to only get libraries in the cycle. |
| * |
| * @param library the library currently being added to the dependency map |
| */ |
| private HashMap<Library, ArrayList<Library>> computeDependencyMap(Library library) { |
| HashMap<Library, ArrayList<Library>> dependencyMap = new HashMap<Library, ArrayList<Library>>(); |
| addToDependencyMap(library, dependencyMap, new HashSet<Library>()); |
| return dependencyMap; |
| } |
| |
| /** |
| * Recursively traverse the libraries reachable from the given library, creating instances of the |
| * class {@link Library} to represent them, and record the references in the library objects. |
| * |
| * @param library the library to be processed to find libraries that have not yet been traversed |
| * @throws AnalysisException if some portion of the library graph could not be traversed |
| */ |
| private void computeEmbeddedLibraryDependencies(Library library, CompilationUnit unit) |
| throws AnalysisException { |
| Source librarySource = library.getLibrarySource(); |
| HashSet<Source> exportedSources = new HashSet<Source>(); |
| HashSet<Source> importedSources = new HashSet<Source>(); |
| for (Directive directive : unit.getDirectives()) { |
| if (directive instanceof ExportDirective) { |
| Source exportSource = resolveSource(librarySource, (ExportDirective) directive); |
| if (exportSource != null) { |
| exportedSources.add(exportSource); |
| } |
| } else if (directive instanceof ImportDirective) { |
| Source importSource = resolveSource(librarySource, (ImportDirective) directive); |
| if (importSource != null) { |
| importedSources.add(importSource); |
| } |
| } |
| } |
| computeLibraryDependenciesFromDirectives( |
| library, |
| importedSources.toArray(new Source[importedSources.size()]), |
| exportedSources.toArray(new Source[exportedSources.size()])); |
| } |
| |
| /** |
| * Return a collection containing all of the libraries reachable from the given library that are |
| * contained in a cycle that includes the given library. |
| * |
| * @param library the library that must be included in any cycles whose members are to be returned |
| * @return all of the libraries referenced by the given library that have a circular reference |
| * back to the given library |
| */ |
| private Set<Library> computeLibrariesInCycles(Library library) { |
| HashMap<Library, ArrayList<Library>> dependencyMap = computeDependencyMap(library); |
| Set<Library> librariesInCycle = new HashSet<Library>(); |
| addLibrariesInCycle(library, librariesInCycle, dependencyMap); |
| return librariesInCycle; |
| } |
| |
| /** |
| * Recursively traverse the libraries reachable from the given library, creating instances of the |
| * class {@link Library} to represent them, and record the references in the library objects. |
| * |
| * @param library the library to be processed to find libraries that have not yet been traversed |
| * @throws AnalysisException if some portion of the library graph could not be traversed |
| */ |
| private void computeLibraryDependencies(Library library) throws AnalysisException { |
| Source librarySource = library.getLibrarySource(); |
| computeLibraryDependenciesFromDirectives( |
| library, |
| analysisContext.computeImportedLibraries(librarySource), |
| analysisContext.computeExportedLibraries(librarySource)); |
| } |
| |
| /** |
| * Recursively traverse the libraries reachable from the given library, creating instances of the |
| * class {@link Library} to represent them, and record the references in the library objects. |
| * |
| * @param library the library to be processed to find libraries that have not yet been traversed |
| * @param importedSources an array containing the sources that are imported into the given library |
| * @param exportedSources an array containing the sources that are exported from the given library |
| * @throws AnalysisException if some portion of the library graph could not be traversed |
| */ |
| private void computeLibraryDependenciesFromDirectives(Library library, Source[] importedSources, |
| Source[] exportedSources) throws AnalysisException { |
| ArrayList<Library> importedLibraries = new ArrayList<Library>(); |
| boolean explicitlyImportsCore = false; |
| for (Source importedSource : importedSources) { |
| if (importedSource.equals(coreLibrarySource)) { |
| explicitlyImportsCore = true; |
| } |
| Library importedLibrary = libraryMap.get(importedSource); |
| if (importedLibrary == null) { |
| importedLibrary = createLibraryOrNull(importedSource); |
| if (importedLibrary != null) { |
| computeLibraryDependencies(importedLibrary); |
| } |
| } |
| if (importedLibrary != null) { |
| importedLibraries.add(importedLibrary); |
| } |
| } |
| library.setImportedLibraries(importedLibraries.toArray(new Library[importedLibraries.size()])); |
| |
| ArrayList<Library> exportedLibraries = new ArrayList<Library>(); |
| for (Source exportedSource : exportedSources) { |
| Library exportedLibrary = libraryMap.get(exportedSource); |
| if (exportedLibrary == null) { |
| exportedLibrary = createLibraryOrNull(exportedSource); |
| if (exportedLibrary != null) { |
| computeLibraryDependencies(exportedLibrary); |
| } |
| } |
| if (exportedLibrary != null) { |
| exportedLibraries.add(exportedLibrary); |
| } |
| } |
| library.setExportedLibraries(exportedLibraries.toArray(new Library[exportedLibraries.size()])); |
| |
| library.setExplicitlyImportsCore(explicitlyImportsCore); |
| if (!explicitlyImportsCore && !coreLibrarySource.equals(library.getLibrarySource())) { |
| Library importedLibrary = libraryMap.get(coreLibrarySource); |
| if (importedLibrary == null) { |
| importedLibrary = createLibraryOrNull(coreLibrarySource); |
| if (importedLibrary != null) { |
| computeLibraryDependencies(importedLibrary); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Create an object to represent the information about the library defined by the compilation unit |
| * with the given source. Return the library object that was created, or {@code null} if the |
| * source is not valid. |
| * |
| * @param librarySource the source of the library's defining compilation unit |
| * @return the library object that was created |
| */ |
| private Library createLibraryOrNull(Source librarySource) { |
| if (!analysisContext.exists(librarySource)) { |
| return null; |
| } |
| Library library = new Library(analysisContext, errorListener, librarySource); |
| libraryMap.put(librarySource, library); |
| return library; |
| } |
| |
| /** |
| * Create an object to represent the information about the library defined by the compilation unit |
| * with the given source. |
| * |
| * @param librarySource the source of the library's defining compilation unit |
| * @param modificationStamp the modification time of the source from which the compilation unit |
| * was created |
| * @param unit the compilation unit that defines the library |
| * @return the library object that was created |
| * @throws AnalysisException if the library source is not valid |
| */ |
| private Library createLibraryWithUnit(Source librarySource, long modificationStamp, |
| CompilationUnit unit) throws AnalysisException { |
| Library library = new Library(analysisContext, errorListener, librarySource); |
| library.setDefiningCompilationUnit(modificationStamp, unit); |
| libraryMap.put(librarySource, library); |
| return library; |
| } |
| |
| /** |
| * Return an array containing the lexical identifiers associated with the nodes in the given list. |
| * |
| * @param names the AST nodes representing the identifiers |
| * @return the lexical identifiers associated with the nodes in the list |
| */ |
| // TODO(brianwilkerson) Move with buildDirectiveModels(). |
| private String[] getIdentifiers(NodeList<SimpleIdentifier> names) { |
| int count = names.size(); |
| String[] identifiers = new String[count]; |
| for (int i = 0; i < count; i++) { |
| identifiers[i] = names.get(i).getName(); |
| } |
| return identifiers; |
| } |
| |
| /** |
| * Compute a value for all of the constants in the libraries being analyzed. |
| */ |
| private void performConstantEvaluation() { |
| TimeCounterHandle timeCounter = PerformanceStatistics.resolve.start(); |
| try { |
| ConstantValueComputer computer = new ConstantValueComputer( |
| typeProvider, |
| analysisContext.getDeclaredVariables()); |
| for (Library library : librariesInCycles) { |
| for (Source source : library.getCompilationUnitSources()) { |
| try { |
| CompilationUnit unit = library.getAST(source); |
| if (unit != null) { |
| computer.add(unit); |
| } |
| } catch (AnalysisException exception) { |
| AnalysisEngine.getInstance().getLogger().logError( |
| "Internal Error: Could not access AST for " + source.getFullName() |
| + " during constant evaluation", |
| exception); |
| } |
| } |
| } |
| computer.computeValues(); |
| } finally { |
| timeCounter.stop(); |
| } |
| } |
| |
| /** |
| * Resolve the identifiers and perform type analysis in the libraries in the current cycle. |
| * |
| * @throws AnalysisException if any of the identifiers could not be resolved or if any of the |
| * libraries could not have their types analyzed |
| */ |
| private void resolveReferencesAndTypes() throws AnalysisException { |
| for (Library library : librariesInCycles) { |
| resolveReferencesAndTypesInLibrary(library); |
| } |
| } |
| |
| /** |
| * Resolve the identifiers and perform type analysis in the given library. |
| * |
| * @param library the library to be resolved |
| * @throws AnalysisException if any of the identifiers could not be resolved or if the types in |
| * the library cannot be analyzed |
| */ |
| private void resolveReferencesAndTypesInLibrary(Library library) throws AnalysisException { |
| TimeCounterHandle timeCounter = PerformanceStatistics.resolve.start(); |
| try { |
| for (Source source : library.getCompilationUnitSources()) { |
| CompilationUnit ast = library.getAST(source); |
| ast.accept(new VariableResolverVisitor(library, source, typeProvider)); |
| ResolverVisitor visitor = new ResolverVisitor(library, source, typeProvider); |
| ast.accept(visitor); |
| } |
| } finally { |
| timeCounter.stop(); |
| } |
| // Angular |
| timeCounter = PerformanceStatistics.angular.start(); |
| try { |
| for (Source source : library.getCompilationUnitSources()) { |
| CompilationUnit ast = library.getAST(source); |
| new AngularCompilationUnitBuilder(errorListener, source, ast).build(); |
| } |
| } finally { |
| timeCounter.stop(); |
| } |
| // Polymer |
| timeCounter = PerformanceStatistics.polymer.start(); |
| try { |
| for (Source source : library.getCompilationUnitSources()) { |
| CompilationUnit ast = library.getAST(source); |
| new PolymerCompilationUnitBuilder(ast).build(); |
| } |
| } finally { |
| timeCounter.stop(); |
| } |
| } |
| |
| /** |
| * Return the result of resolving the URI of the given URI-based directive against the URI of the |
| * given library, or {@code null} if the URI is not valid. |
| * |
| * @param librarySource the source representing the library containing the directive |
| * @param directive the directive which URI should be resolved |
| * @return the result of resolving the URI against the URI of the library |
| */ |
| private Source resolveSource(Source librarySource, UriBasedDirective directive) { |
| StringLiteral uriLiteral = directive.getUri(); |
| if (uriLiteral instanceof StringInterpolation) { |
| return null; |
| } |
| String uriContent = uriLiteral.getStringValue().trim(); |
| if (uriContent == null || uriContent.isEmpty()) { |
| return null; |
| } |
| uriContent = UriUtilities.encode(uriContent); |
| return analysisContext.getSourceFactory().resolveUri(librarySource, uriContent); |
| } |
| } |