/*
* 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.ast.Combinator;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.Directive;
import com.google.dart.engine.ast.ExportDirective;
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.StringLiteral;
import com.google.dart.engine.context.AnalysisContext;
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.context.ResolvableCompilationUnit;
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.sdk.DirectoryBasedDartSdk;
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.translation.DartBlockBody;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* 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 LibraryResolver2 {
/**
* Report that the core library could not be resolved in the given analysis context and throw an
* exception.
*
* @param analysisContext the analysis context in which the failure occurred
* @param coreLibrarySource the source representing the core library
* @throws AnalysisException always
*/
@DartBlockBody({"throw new AnalysisException(\"Could not resolve dart:core\");"})
public static void missingCoreLibrary(AnalysisContext analysisContext, Source coreLibrarySource)
throws AnalysisException {
InstrumentationBuilder instrumentation = Instrumentation.builder("ErrorNoCoreLibrary");
try {
DartSdk sdk = analysisContext.getSourceFactory().getDartSdk();
if (sdk == null) {
instrumentation.data("sdkPath", "--null--");
} else if (sdk instanceof DirectoryBasedDartSdk) {
File directory = ((DirectoryBasedDartSdk) sdk).getDirectory();
if (directory == null) {
instrumentation.data("sdkDirectoryIsNull", true);
} else {
instrumentation.data("sdkDirectoryIsNull", false);
instrumentation.data("sdkPath", directory.getAbsolutePath());
instrumentation.data("sdkDirectoryExists", directory.exists());
}
} else {
instrumentation.data("sdkPath", "--unknown--");
}
instrumentation.data("coreLibraryPath", coreLibrarySource.getFullName());
} finally {
instrumentation.log();
}
throw new AnalysisException("Could not resolve dart:core");
}
/**
* 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 ResolvableLibrary 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, ResolvableLibrary> libraryMap = new HashMap<Source, ResolvableLibrary>();
/**
* A collection containing the libraries that are being resolved together.
*/
private List<ResolvableLibrary> librariesInCycle;
/**
* 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 LibraryResolver2(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 List<ResolvableLibrary> getResolvedLibraries() {
return librariesInCycle;
}
/**
* 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,
List<ResolvableLibrary> librariesInCycle) throws AnalysisException {
InstrumentationBuilder instrumentation = Instrumentation.builder("dart.engine.LibraryResolver.resolveLibrary");
try {
instrumentation.data("fullName", librarySource.getFullName());
//
// Build the map of libraries that are known.
//
this.librariesInCycle = librariesInCycle;
libraryMap = buildLibraryMap();
ResolvableLibrary targetLibrary = libraryMap.get(librarySource);
coreLibrary = libraryMap.get(coreLibrarySource);
instrumentation.metric("buildLibraryMap", "complete");
//
// 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) {
missingCoreLibrary(analysisContext, coreLibrarySource);
}
buildDirectiveModels();
instrumentation.metric("buildDirectiveModels", "complete");
typeProvider = new TypeProviderImpl(coreElement);
buildEnumMembers();
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");
instrumentation.metric("librariesInCycles", librariesInCycle.size());
for (ResolvableLibrary lib : librariesInCycle) {
instrumentation.metric(
"librariesInCycles-CompilationUnitSources-Size",
lib.getCompilationUnitSources().length);
}
return targetLibrary.getLibraryElement();
} finally {
instrumentation.log();
}
}
/**
* 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 (ResolvableLibrary library : librariesInCycle) {
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 && analysisContext.exists(importedSource)) {
// The imported source will be null if the URI in the import directive was invalid.
ResolvableLibrary importedLibrary = libraryMap.get(importedSource);
if (importedLibrary != null) {
ImportElementImpl importElement = new ImportElementImpl(directive.getOffset());
StringLiteral uriLiteral = importDirective.getUri();
if (uriLiteral != null) {
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 && analysisContext.exists(exportedSource)) {
// The exported source will be null if the URI in the export directive was invalid.
ResolvableLibrary exportedLibrary = libraryMap.get(exportedSource);
if (exportedLibrary != null) {
ExportElementImpl exportElement = new ExportElementImpl();
StringLiteral uriLiteral = exportDirective.getUri();
if (uriLiteral != null) {
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 (ResolvableLibrary library : librariesInCycle) {
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 (ResolvableLibrary library : librariesInCycle) {
for (Source source : library.getCompilationUnitSources()) {
EnumMemberBuilder builder = new EnumMemberBuilder(typeProvider);
library.getAST(source).accept(builder);
}
}
} finally {
timeCounter.stop();
}
}
private HashMap<Source, ResolvableLibrary> buildLibraryMap() {
HashMap<Source, ResolvableLibrary> libraryMap = new HashMap<Source, ResolvableLibrary>();
int libraryCount = librariesInCycle.size();
for (int i = 0; i < libraryCount; i++) {
ResolvableLibrary library = librariesInCycle.get(i);
library.setErrorListener(errorListener);
libraryMap.put(library.getLibrarySource(), library);
ResolvableLibrary[] dependencies = library.getImportsAndExports();
int dependencyCount = dependencies.length;
for (int j = 0; j < dependencyCount; j++) {
ResolvableLibrary dependency = dependencies[j];
//dependency.setErrorListener(errorListener);
libraryMap.put(dependency.getLibrarySource(), dependency);
}
}
return libraryMap;
}
/**
* 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 (ResolvableLibrary library : librariesInCycle) {
for (ResolvableCompilationUnit unit : library.getResolvableCompilationUnits()) {
Source source = unit.getSource();
CompilationUnit ast = unit.getCompilationUnit();
TypeResolverVisitor visitor = new TypeResolverVisitor(library, source, typeProvider);
ast.accept(visitor);
}
}
} finally {
timeCounter.stop();
}
}
/**
* 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 (ResolvableLibrary library : librariesInCycle) {
for (ResolvableCompilationUnit unit : library.getResolvableCompilationUnits()) {
CompilationUnit ast = unit.getCompilationUnit();
if (ast != null) {
computer.add(ast);
}
}
}
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 (ResolvableLibrary library : librariesInCycle) {
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(ResolvableLibrary library)
throws AnalysisException {
TimeCounterHandle timeCounter = PerformanceStatistics.resolve.start();
try {
for (ResolvableCompilationUnit unit : library.getResolvableCompilationUnits()) {
Source source = unit.getSource();
CompilationUnit ast = unit.getCompilationUnit();
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 (ResolvableCompilationUnit unit : library.getResolvableCompilationUnits()) {
Source source = unit.getSource();
CompilationUnit ast = unit.getCompilationUnit();
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();
}
}
}