blob: e8093f3d608d50f7d9da60ef3808a120b8140162 [file] [log] [blame]
/*
* 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);
}
}