| Index: sdk/lib/_internal/compiler/implementation/library_loader.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/library_loader.dart b/sdk/lib/_internal/compiler/implementation/library_loader.dart
|
| deleted file mode 100644
|
| index ff7d59ae25c578070bcbb5c6a2221b60b70e6c01..0000000000000000000000000000000000000000
|
| --- a/sdk/lib/_internal/compiler/implementation/library_loader.dart
|
| +++ /dev/null
|
| @@ -1,1034 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library dart2js.library_loader;
|
| -
|
| -import 'dart:async';
|
| -import 'dart2jslib.dart'
|
| - show Compiler,
|
| - CompilerTask,
|
| - MessageKind,
|
| - Script,
|
| - invariant;
|
| -import 'elements/elements.dart'
|
| - show CompilationUnitElement,
|
| - Element,
|
| - LibraryElement,
|
| - PrefixElement;
|
| -import 'elements/modelx.dart'
|
| - show CompilationUnitElementX,
|
| - DeferredLoaderGetterElementX,
|
| - ErroneousElementX,
|
| - LibraryElementX,
|
| - PrefixElementX;
|
| -import 'helpers/helpers.dart'; // Included for debug helpers.
|
| -import 'native/native.dart' as native;
|
| -import 'tree/tree.dart';
|
| -import 'util/util.dart' show Link, LinkBuilder;
|
| -
|
| -/**
|
| - * [CompilerTask] for loading libraries and setting up the import/export scopes.
|
| - *
|
| - * The library loader uses four different kinds of URIs in different parts of
|
| - * the loading process.
|
| - *
|
| - * ## User URI ##
|
| - *
|
| - * A 'user URI' is a URI provided by the user in code and as the main entry URI
|
| - * at the command line. These generally come in 3 versions:
|
| - *
|
| - * * A relative URI such as 'foo.dart', '../bar.dart', and 'baz/boz.dart'.
|
| - *
|
| - * * A dart URI such as 'dart:core' and 'dart:_js_helper'.
|
| - *
|
| - * * A package URI such as 'package:foo.dart' and 'package:bar/baz.dart'.
|
| - *
|
| - * A user URI can also be absolute, like 'file:///foo.dart' or
|
| - * 'http://example.com/bar.dart', but such URIs cannot necessarily be used for
|
| - * locating source files, since the scheme must be supported by the input
|
| - * provider. The standard input provider for dart2js only supports the 'file'
|
| - * and 'http' scheme.
|
| - *
|
| - * ## Resolved URI ##
|
| - *
|
| - * A 'resolved URI' is a (user) URI that has been resolved to an absolute URI
|
| - * based on the readable URI (see below) from which it was loaded. A URI with an
|
| - * explicit scheme (such as 'dart:', 'package:' or 'file:') is already resolved.
|
| - * A relative URI like for instance '../foo/bar.dart' is translated into an
|
| - * resolved URI in one of three ways:
|
| - *
|
| - * * If provided as the main entry URI at the command line, the URI is resolved
|
| - * relative to the current working directory, say
|
| - * 'file:///current/working/dir/', and the resolved URI is therefore
|
| - * 'file:///current/working/foo/bar.dart'.
|
| - *
|
| - * * If the relative URI is provided in an import, export or part tag, and the
|
| - * readable URI of the enclosing compilation unit is a file URI,
|
| - * 'file://some/path/baz.dart', then the resolved URI is
|
| - * 'file://some/foo/bar.dart'.
|
| - *
|
| - * * If the relative URI is provided in an import, export or part tag, and the
|
| - * readable URI of the enclosing compilation unit is a package URI,
|
| - * 'package:some/path/baz.dart', then the resolved URI is
|
| - * 'package:some/foo/bar.dart'.
|
| - *
|
| - * The resolved URI thus preserves the scheme through resolution: A readable
|
| - * file URI results in an resolved file URI and a readable package URI results
|
| - * in an resolved package URI. Note that since a dart URI is not a readable URI,
|
| - * import, export or part tags within platform libraries are not interpreted as
|
| - * dart URIs but instead relative to the library source file location.
|
| - *
|
| - * The resolved URI of a library is also used as the canonical URI
|
| - * ([LibraryElement.canonicalUri]) by which we identify which libraries are
|
| - * identical. This means that libraries loaded through the 'package' scheme will
|
| - * resolve to the same library when loaded from within using relative URIs (see
|
| - * for instance the test 'standalone/package/package1_test.dart'). But loading a
|
| - * platform library using a relative URI will _not_ result in the same library
|
| - * as when loaded through the dart URI.
|
| - *
|
| - * ## Readable URI ##
|
| - *
|
| - * A 'readable URI' is an absolute URI whose scheme is either 'package' or
|
| - * something supported by the input provider, normally 'file'. Dart URIs such as
|
| - * 'dart:core' and 'dart:_js_helper' are not readable themselves but are instead
|
| - * resolved into a readable URI using the library root URI provided from the
|
| - * command line and the list of platform libraries found in
|
| - * 'sdk/lib/_internal/libraries.dart'. This is done through the
|
| - * [Compiler.translateResolvedUri] method which checks whether a library by that
|
| - * name exists and in case of internal libraries whether access is granted.
|
| - *
|
| - * ## Resource URI ##
|
| - *
|
| - * A 'resource URI' is an absolute URI with a scheme supported by the input
|
| - * provider. For the standard implementation this means a URI with the 'file'
|
| - * scheme. Readable URIs are converted into resource URIs as part of the
|
| - * [Compiler.readScript] method. In the standard implementation the package URIs
|
| - * are converted to file URIs using the package root URI provided on the
|
| - * command line as base. If the package root URI is
|
| - * 'file:///current/working/dir/' then the package URI 'package:foo/bar.dart'
|
| - * will be resolved to the resource URI
|
| - * 'file:///current/working/dir/foo/bar.dart'.
|
| - *
|
| - * The distinction between readable URI and resource URI is necessary to ensure
|
| - * that these imports
|
| - *
|
| - * import 'package:foo.dart' as a;
|
| - * import 'packages/foo.dart' as b;
|
| - *
|
| - * do _not_ resolve to the same library when the package root URI happens to
|
| - * point to the 'packages' folder.
|
| - *
|
| - */
|
| -abstract class LibraryLoaderTask implements CompilerTask {
|
| - factory LibraryLoaderTask(Compiler compiler) = _LibraryLoaderTask;
|
| -
|
| - /// Returns all libraries that have been loaded.
|
| - Iterable<LibraryElement> get libraries;
|
| -
|
| - /// Looks up the library with the [canonicalUri].
|
| - LibraryElement lookupLibrary(Uri canonicalUri);
|
| -
|
| - /// Loads the library specified by the [resolvedUri] and returns its
|
| - /// [LibraryElement].
|
| - ///
|
| - /// If the library is not already loaded, the method creates the
|
| - /// [LibraryElement] for the library and computes the import/export scope,
|
| - /// loading and computing the import/export scopes of all required libraries
|
| - /// in the process. The method handles cyclic dependency between libraries.
|
| - Future<LibraryElement> loadLibrary(Uri resolvedUri);
|
| -
|
| - /// Reset the library loader task to prepare for compilation. If provided,
|
| - /// libraries matching [reuseLibrary] are reused.
|
| - ///
|
| - /// This method is used for incremental compilation.
|
| - void reset({bool reuseLibrary(LibraryElement library)});
|
| -
|
| - /// Asynchronous version of [reset].
|
| - Future resetAsync(Future<bool> reuseLibrary(LibraryElement library));
|
| -}
|
| -
|
| -/// Handle for creating synthesized/patch libraries during library loading.
|
| -abstract class LibraryLoader {
|
| - /// This method must be called when a new synthesized/patch library has been
|
| - /// created to ensure that [library] will part of library dependency graph
|
| - /// used for computing import/export scopes.
|
| - void registerNewLibrary(LibraryElement library);
|
| -
|
| - /// This method must be called when a new synthesized/patch library has been
|
| - /// scanned in order to process the library tags in [library] and thus handle
|
| - /// imports/exports/parts in the synthesized/patch library.
|
| - Future processLibraryTags(LibraryElement library);
|
| -}
|
| -
|
| -/**
|
| - * [CombinatorFilter] is a succinct representation of a list of combinators from
|
| - * a library dependency tag.
|
| - */
|
| -class CombinatorFilter {
|
| - const CombinatorFilter();
|
| -
|
| - /**
|
| - * Returns [:true:] if [element] is excluded by this filter.
|
| - */
|
| - bool exclude(Element element) => false;
|
| -
|
| - /**
|
| - * Creates a filter based on the combinators of [tag].
|
| - */
|
| - factory CombinatorFilter.fromTag(LibraryDependency tag) {
|
| - if (tag == null || tag.combinators == null) {
|
| - return const CombinatorFilter();
|
| - }
|
| -
|
| - // If the list of combinators contain at least one [:show:] we can create
|
| - // a positive list of elements to include, otherwise we create a negative
|
| - // list of elements to exclude.
|
| - bool show = false;
|
| - Set<String> nameSet;
|
| - for (Combinator combinator in tag.combinators) {
|
| - if (combinator.isShow) {
|
| - show = true;
|
| - var set = new Set<String>();
|
| - for (Identifier identifier in combinator.identifiers) {
|
| - set.add(identifier.source);
|
| - }
|
| - if (nameSet == null) {
|
| - nameSet = set;
|
| - } else {
|
| - nameSet = nameSet.intersection(set);
|
| - }
|
| - }
|
| - }
|
| - if (nameSet == null) {
|
| - nameSet = new Set<String>();
|
| - }
|
| - for (Combinator combinator in tag.combinators) {
|
| - if (combinator.isHide) {
|
| - for (Identifier identifier in combinator.identifiers) {
|
| - if (show) {
|
| - // We have a positive list => Remove hidden elements.
|
| - nameSet.remove(identifier.source);
|
| - } else {
|
| - // We have no positive list => Accumulate hidden elements.
|
| - nameSet.add(identifier.source);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return show ? new ShowFilter(nameSet) : new HideFilter(nameSet);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * A list of combinators represented as a list of element names to include.
|
| - */
|
| -class ShowFilter extends CombinatorFilter {
|
| - final Set<String> includedNames;
|
| -
|
| - ShowFilter(this.includedNames);
|
| -
|
| - bool exclude(Element element) => !includedNames.contains(element.name);
|
| -}
|
| -
|
| -/**
|
| - * A list of combinators represented as a list of element names to exclude.
|
| - */
|
| -class HideFilter extends CombinatorFilter {
|
| - final Set<String> excludedNames;
|
| -
|
| - HideFilter(this.excludedNames);
|
| -
|
| - bool exclude(Element element) => excludedNames.contains(element.name);
|
| -}
|
| -
|
| -/**
|
| - * Implementation class for [LibraryLoader]. The distinction between
|
| - * [LibraryLoader] and [LibraryLoaderTask] is made to hide internal members from
|
| - * the [LibraryLoader] interface.
|
| - */
|
| -class _LibraryLoaderTask extends CompilerTask implements LibraryLoaderTask {
|
| - _LibraryLoaderTask(Compiler compiler) : super(compiler);
|
| - String get name => 'LibraryLoader';
|
| -
|
| - final Map<Uri, LibraryElement> libraryCanonicalUriMap =
|
| - new Map<Uri, LibraryElement>();
|
| - final Map<Uri, LibraryElement> libraryResourceUriMap =
|
| - new Map<Uri, LibraryElement>();
|
| - final Map<String, LibraryElement> libraryNames =
|
| - new Map<String, LibraryElement>();
|
| -
|
| - LibraryDependencyHandler currentHandler;
|
| -
|
| - Iterable<LibraryElement> get libraries => libraryCanonicalUriMap.values;
|
| -
|
| - LibraryElement lookupLibrary(Uri canonicalUri) {
|
| - return libraryCanonicalUriMap[canonicalUri];
|
| - }
|
| -
|
| - void reset({bool reuseLibrary(LibraryElement library)}) {
|
| - measure(() {
|
| - assert(currentHandler == null);
|
| -
|
| - Iterable<LibraryElement> reusedLibraries = null;
|
| - if (reuseLibrary != null) {
|
| - reusedLibraries = compiler.reuseLibraryTask.measure(() {
|
| - // Call [toList] to force eager calls to [reuseLibrary].
|
| - return libraryCanonicalUriMap.values.where(reuseLibrary).toList();
|
| - });
|
| - }
|
| -
|
| - resetImplementation(reusedLibraries);
|
| - });
|
| - }
|
| -
|
| - void resetImplementation(Iterable<LibraryElement> reusedLibraries) {
|
| - measure(() {
|
| - libraryCanonicalUriMap.clear();
|
| - libraryResourceUriMap.clear();
|
| - libraryNames.clear();
|
| -
|
| - if (reusedLibraries != null) {
|
| - reusedLibraries.forEach(mapLibrary);
|
| - }
|
| - });
|
| - }
|
| -
|
| - Future resetAsync(Future<bool> reuseLibrary(LibraryElement library)) {
|
| - return measure(() {
|
| - assert(currentHandler == null);
|
| -
|
| - Future<LibraryElement> wrapper(LibraryElement library) {
|
| - try {
|
| - return reuseLibrary(library).then(
|
| - (bool reuse) => reuse ? library : null);
|
| - } catch (exception, trace) {
|
| - compiler.diagnoseCrashInUserCode(
|
| - 'Uncaught exception in reuseLibrary', exception, trace);
|
| - rethrow;
|
| - }
|
| - }
|
| -
|
| - List<Future<LibraryElement>> reusedLibrariesFuture =
|
| - compiler.reuseLibraryTask.measure(
|
| - () => libraryCanonicalUriMap.values.map(wrapper).toList());
|
| -
|
| - return Future.wait(reusedLibrariesFuture).then(
|
| - (List<LibraryElement> reusedLibraries) {
|
| - resetImplementation(reusedLibraries.where((e) => e != null));
|
| - });
|
| - });
|
| - }
|
| -
|
| - /// Insert [library] in the internal maps. Used for compiler reuse.
|
| - void mapLibrary(LibraryElement library) {
|
| - libraryCanonicalUriMap[library.canonicalUri] = library;
|
| -
|
| - Uri resourceUri = library.entryCompilationUnit.script.resourceUri;
|
| - libraryResourceUriMap[resourceUri] = library;
|
| -
|
| - String name = library.getLibraryOrScriptName();
|
| - libraryNames[name] = library;
|
| - }
|
| -
|
| - Future<LibraryElement> loadLibrary(Uri resolvedUri) {
|
| - return measure(() {
|
| - assert(currentHandler == null);
|
| - // TODO(johnniwinther): Ensure that currentHandler correctly encloses the
|
| - // loading of a library cluster.
|
| - currentHandler = new LibraryDependencyHandler(this);
|
| - return createLibrary(currentHandler, null, resolvedUri)
|
| - .then((LibraryElement library) {
|
| - return compiler.withCurrentElement(library, () {
|
| - return measure(() {
|
| - currentHandler.computeExports();
|
| - Map<Uri, LibraryElement> loadedLibraries = <Uri, LibraryElement>{};
|
| - currentHandler.loadedLibraries.forEach(
|
| - (LibraryElement loadedLibrary) {
|
| - loadedLibraries[loadedLibrary.canonicalUri] = loadedLibrary;
|
| - });
|
| - currentHandler = null;
|
| - return compiler.onLibrariesLoaded(loadedLibraries)
|
| - .then((_) => library);
|
| - });
|
| - });
|
| - });
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Processes the library tags in [library].
|
| - *
|
| - * The imported/exported libraries are loaded and processed recursively but
|
| - * the import/export scopes are not set up.
|
| - */
|
| - Future processLibraryTags(LibraryDependencyHandler handler,
|
| - LibraryElement library) {
|
| - int tagState = TagState.NO_TAG_SEEN;
|
| -
|
| - /**
|
| - * If [value] is less than [tagState] complain and return
|
| - * [tagState]. Otherwise return the new value for [tagState]
|
| - * (transition function for state machine).
|
| - */
|
| - int checkTag(int value, LibraryTag tag) {
|
| - if (tagState > value) {
|
| - compiler.reportFatalError(
|
| - tag,
|
| - MessageKind.GENERIC, {'text': 'Error: Out of order.'});
|
| - return tagState;
|
| - }
|
| - return TagState.NEXT[value];
|
| - }
|
| -
|
| - bool importsDartCore = false;
|
| - var libraryDependencies = new LinkBuilder<LibraryDependency>();
|
| - Uri base = library.entryCompilationUnit.script.readableUri;
|
| -
|
| - return Future.forEach(library.tags, (LibraryTag tag) {
|
| - return compiler.withCurrentElement(library, () {
|
| - if (tag.isImport) {
|
| - Import import = tag;
|
| - tagState = checkTag(TagState.IMPORT_OR_EXPORT, import);
|
| - if (import.uri.dartString.slowToString() == 'dart:core') {
|
| - importsDartCore = true;
|
| - }
|
| - libraryDependencies.addLast(import);
|
| - } else if (tag.isExport) {
|
| - tagState = checkTag(TagState.IMPORT_OR_EXPORT, tag);
|
| - libraryDependencies.addLast(tag);
|
| - } else if (tag.isLibraryName) {
|
| - tagState = checkTag(TagState.LIBRARY, tag);
|
| - if (library.libraryTag != null) {
|
| - compiler.internalError(tag, "Duplicated library declaration.");
|
| - } else {
|
| - library.libraryTag = tag;
|
| - }
|
| - } else if (tag.isPart) {
|
| - Part part = tag;
|
| - StringNode uri = part.uri;
|
| - Uri resolvedUri = base.resolve(uri.dartString.slowToString());
|
| - tagState = checkTag(TagState.SOURCE, part);
|
| - return scanPart(part, resolvedUri, library);
|
| - } else {
|
| - compiler.internalError(tag, "Unhandled library tag.");
|
| - }
|
| - });
|
| - }).then((_) {
|
| - return compiler.onLibraryScanned(library, handler);
|
| - }).then((_) {
|
| - return compiler.withCurrentElement(library, () {
|
| - checkDuplicatedLibraryName(library);
|
| -
|
| - // Import dart:core if not already imported.
|
| - if (!importsDartCore && library.canonicalUri != Compiler.DART_CORE) {
|
| - return createLibrary(handler, null, Compiler.DART_CORE)
|
| - .then((LibraryElement coreLibrary) {
|
| - handler.registerDependency(library, null, coreLibrary);
|
| - });
|
| - }
|
| - });
|
| - }).then((_) {
|
| - return Future.forEach(libraryDependencies.toList(), (tag) {
|
| - return compiler.withCurrentElement(library, () {
|
| - return registerLibraryFromTag(handler, library, tag);
|
| - });
|
| - });
|
| - });
|
| - }
|
| -
|
| - /// True if the uris are pointing to a library that is shared between dart2js
|
| - /// and the core libraries. By construction they must be imported into the
|
| - /// runtime, and, at the same time, into dart2js. This can lead to
|
| - /// duplicated imports, like in the docgen.
|
| - // TODO(johnniwinther): is this necessary, or should we change docgen not
|
| - // to include both libraries (compiler and lib) at the same time?
|
| - bool _isSharedDart2jsLibrary(Uri uri1, Uri uri2) {
|
| - bool inJsLibShared(Uri uri) {
|
| - List<String> segments = uri.pathSegments;
|
| - if (segments.length < 3) return false;
|
| - if (segments[segments.length - 2] != 'shared') return false;
|
| - return (segments[segments.length - 3] == 'js_lib');
|
| - }
|
| - return inJsLibShared(uri1) && inJsLibShared(uri2);
|
| - }
|
| -
|
| - void checkDuplicatedLibraryName(LibraryElement library) {
|
| - Uri resourceUri = library.entryCompilationUnit.script.resourceUri;
|
| - LibraryName tag = library.libraryTag;
|
| - LibraryElement existing =
|
| - libraryResourceUriMap.putIfAbsent(resourceUri, () => library);
|
| - if (!identical(existing, library)) {
|
| - if (tag != null) {
|
| - compiler.withCurrentElement(library, () {
|
| - compiler.reportWarning(tag.name,
|
| - MessageKind.DUPLICATED_LIBRARY_RESOURCE,
|
| - {'libraryName': tag.name,
|
| - 'resourceUri': resourceUri,
|
| - 'canonicalUri1': library.canonicalUri,
|
| - 'canonicalUri2': existing.canonicalUri});
|
| - });
|
| - } else {
|
| - compiler.reportHint(library,
|
| - MessageKind.DUPLICATED_RESOURCE,
|
| - {'resourceUri': resourceUri,
|
| - 'canonicalUri1': library.canonicalUri,
|
| - 'canonicalUri2': existing.canonicalUri});
|
| - }
|
| - } else if (tag != null) {
|
| - String name = library.getLibraryOrScriptName();
|
| - existing = libraryNames.putIfAbsent(name, () => library);
|
| - if (!identical(existing, library) &&
|
| - !_isSharedDart2jsLibrary(resourceUri, existing.canonicalUri)) {
|
| - compiler.withCurrentElement(library, () {
|
| - compiler.reportWarning(tag.name,
|
| - MessageKind.DUPLICATED_LIBRARY_NAME,
|
| - {'libraryName': name});
|
| - });
|
| - compiler.withCurrentElement(existing, () {
|
| - compiler.reportWarning(existing.libraryTag.name,
|
| - MessageKind.DUPLICATED_LIBRARY_NAME,
|
| - {'libraryName': name});
|
| - });
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Handle a part tag in the scope of [library]. The [resolvedUri] given is
|
| - * used as is, any URI resolution should be done beforehand.
|
| - */
|
| - Future scanPart(Part part, Uri resolvedUri, LibraryElement library) {
|
| - if (!resolvedUri.isAbsolute) throw new ArgumentError(resolvedUri);
|
| - Uri readableUri = compiler.translateResolvedUri(library, resolvedUri, part);
|
| - if (readableUri == null) return new Future.value();
|
| - return compiler.withCurrentElement(library, () {
|
| - return compiler.readScript(part, readableUri).
|
| - then((Script sourceScript) {
|
| - if (sourceScript == null) return;
|
| -
|
| - CompilationUnitElement unit =
|
| - new CompilationUnitElementX(sourceScript, library);
|
| - compiler.withCurrentElement(unit, () {
|
| - compiler.scanner.scan(unit);
|
| - if (unit.partTag == null) {
|
| - compiler.reportError(unit, MessageKind.MISSING_PART_OF_TAG);
|
| - }
|
| - });
|
| - });
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Handle an import/export tag by loading the referenced library and
|
| - * registering its dependency in [handler] for the computation of the import/
|
| - * export scope.
|
| - */
|
| - Future registerLibraryFromTag(LibraryDependencyHandler handler,
|
| - LibraryElement library,
|
| - LibraryDependency tag) {
|
| - Uri base = library.entryCompilationUnit.script.readableUri;
|
| - Uri resolvedUri = base.resolve(tag.uri.dartString.slowToString());
|
| - return createLibrary(handler, library, resolvedUri, tag.uri)
|
| - .then((LibraryElement loadedLibrary) {
|
| - if (loadedLibrary == null) return;
|
| - compiler.withCurrentElement(library, () {
|
| - handler.registerDependency(library, tag, loadedLibrary);
|
| - });
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Create (or reuse) a library element for the library specified by the
|
| - * [resolvedUri].
|
| - *
|
| - * If a new library is created, the [handler] is notified.
|
| - */
|
| - Future<LibraryElement> createLibrary(LibraryDependencyHandler handler,
|
| - LibraryElement importingLibrary,
|
| - Uri resolvedUri,
|
| - [Node node]) {
|
| - // TODO(johnniwinther): Create erroneous library elements for missing
|
| - // libraries.
|
| - Uri readableUri =
|
| - compiler.translateResolvedUri(importingLibrary, resolvedUri, node);
|
| - if (readableUri == null) return new Future.value();
|
| - LibraryElement library = libraryCanonicalUriMap[resolvedUri];
|
| - if (library != null) {
|
| - return new Future.value(library);
|
| - }
|
| - return compiler.withCurrentElement(importingLibrary, () {
|
| - return compiler.readScript(node, readableUri).then((Script script) {
|
| - if (script == null) return null;
|
| - LibraryElement element =
|
| - createLibrarySync(handler, script, resolvedUri);
|
| - return processLibraryTags(handler, element).then((_) {
|
| - compiler.withCurrentElement(element, () {
|
| - handler.registerLibraryExports(element);
|
| - });
|
| - return element;
|
| - });
|
| - });
|
| - });
|
| - }
|
| -
|
| - LibraryElement createLibrarySync(
|
| - LibraryDependencyHandler handler,
|
| - Script script,
|
| - Uri resolvedUri) {
|
| - LibraryElement element = new LibraryElementX(script, resolvedUri);
|
| - return compiler.withCurrentElement(element, () {
|
| - if (handler != null) {
|
| - handler.registerNewLibrary(element);
|
| - libraryCanonicalUriMap[resolvedUri] = element;
|
| - }
|
| - native.maybeEnableNative(compiler, element);
|
| - compiler.scanner.scanLibrary(element);
|
| - return element;
|
| - });
|
| - }
|
| -}
|
| -
|
| -
|
| -/**
|
| - * The fields of this class models a state machine for checking script
|
| - * tags come in the correct order.
|
| - */
|
| -class TagState {
|
| - static const int NO_TAG_SEEN = 0;
|
| - static const int LIBRARY = 1;
|
| - static const int IMPORT_OR_EXPORT = 2;
|
| - static const int SOURCE = 3;
|
| - static const int RESOURCE = 4;
|
| -
|
| - /** Next state. */
|
| - static const List<int> NEXT =
|
| - const <int>[NO_TAG_SEEN,
|
| - IMPORT_OR_EXPORT, // Only one library tag is allowed.
|
| - IMPORT_OR_EXPORT,
|
| - SOURCE,
|
| - RESOURCE];
|
| -}
|
| -
|
| -/**
|
| - * An [import] tag and the [importedLibrary] imported through [import].
|
| - */
|
| -class ImportLink {
|
| - final Import import;
|
| - final LibraryElement importedLibrary;
|
| -
|
| - ImportLink(this.import, this.importedLibrary);
|
| -
|
| - /**
|
| - * Imports the library into the [importingLibrary].
|
| - */
|
| - void importLibrary(Compiler compiler, LibraryElement importingLibrary) {
|
| - assert(invariant(importingLibrary,
|
| - importedLibrary.exportsHandled,
|
| - message: 'Exports not handled on $importedLibrary'));
|
| - var combinatorFilter = new CombinatorFilter.fromTag(import);
|
| - if (import != null && import.prefix != null) {
|
| - String prefix = import.prefix.source;
|
| - Element existingElement = importingLibrary.find(prefix);
|
| - PrefixElement prefixElement;
|
| - if (existingElement == null || !existingElement.isPrefix) {
|
| - prefixElement = new PrefixElementX(prefix,
|
| - importingLibrary.entryCompilationUnit, import.getBeginToken());
|
| - } else {
|
| - prefixElement = existingElement;
|
| - }
|
| - importingLibrary.addToScope(prefixElement, compiler);
|
| - importedLibrary.forEachExport((Element element) {
|
| - if (combinatorFilter.exclude(element)) return;
|
| - prefixElement.addImport(element, import, compiler);
|
| - });
|
| - if (import.isDeferred) {
|
| - prefixElement.addImport(
|
| - new DeferredLoaderGetterElementX(prefixElement),
|
| - import, compiler);
|
| - // TODO(sigurdm): When we remove support for the annotation based
|
| - // syntax the [PrefixElement] constructor should receive this
|
| - // information.
|
| - prefixElement.markAsDeferred(import);
|
| - }
|
| - } else {
|
| - importedLibrary.forEachExport((Element element) {
|
| - compiler.withCurrentElement(importingLibrary, () {
|
| - if (combinatorFilter.exclude(element)) return;
|
| - importingLibrary.addImport(element, import, compiler);
|
| - });
|
| - });
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * The combinator filter computed from an export tag and the library dependency
|
| - * node for the library that declared the export tag. This represents an edge in
|
| - * the library dependency graph.
|
| - */
|
| -class ExportLink {
|
| - final Export export;
|
| - final CombinatorFilter combinatorFilter;
|
| - final LibraryDependencyNode exportNode;
|
| -
|
| - ExportLink(Export export, LibraryDependencyNode this.exportNode)
|
| - : this.export = export,
|
| - this.combinatorFilter = new CombinatorFilter.fromTag(export);
|
| -
|
| - /**
|
| - * Exports [element] to the dependent library unless [element] is filtered by
|
| - * the export combinators. Returns [:true:] if the set pending exports of the
|
| - * dependent library was modified.
|
| - */
|
| - bool exportElement(Element element) {
|
| - if (combinatorFilter.exclude(element)) return false;
|
| - return exportNode.addElementToPendingExports(element, export);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * A node in the library dependency graph.
|
| - *
|
| - * This class is used to collect the library dependencies expressed through
|
| - * import and export tags, and as the work-list entry in computations of library
|
| - * exports performed in [LibraryDependencyHandler.computeExports].
|
| - */
|
| -class LibraryDependencyNode {
|
| - final LibraryElement library;
|
| -
|
| - // TODO(ahe): Remove [hashCodeCounter] and [hashCode] when
|
| - // VM implementation of Object.hashCode is not slow.
|
| - final int hashCode = ++hashCodeCounter;
|
| - static int hashCodeCounter = 0;
|
| -
|
| -
|
| - /**
|
| - * A linked list of the import tags that import [library] mapped to the
|
| - * corresponding libraries. This is used to propagate exports into imports
|
| - * after the export scopes have been computed.
|
| - */
|
| - Link<ImportLink> imports = const Link<ImportLink>();
|
| -
|
| - /**
|
| - * A linked list of the export tags the dependent upon this node library.
|
| - * This is used to propagate exports during the computation of export scopes.
|
| - */
|
| - Link<ExportLink> dependencies = const Link<ExportLink>();
|
| -
|
| - /**
|
| - * The export scope for [library] which is gradually computed by the work-list
|
| - * computation in [LibraryDependencyHandler.computeExports].
|
| - */
|
| - Map<String, Element> exportScope =
|
| - new Map<String, Element>();
|
| -
|
| - /// Map from exported elements to the export directives that exported them.
|
| - Map<Element, Link<Export>> exporters = new Map<Element, Link<Export>>();
|
| -
|
| - /**
|
| - * The set of exported elements that need to be propageted to dependent
|
| - * libraries as part of the work-list computation performed in
|
| - * [LibraryDependencyHandler.computeExports]. Each export element is mapped
|
| - * to a list of exports directives that export it.
|
| - */
|
| - Map<Element, Link<Export>> pendingExportMap =
|
| - new Map<Element, Link<Export>>();
|
| -
|
| - LibraryDependencyNode(LibraryElement this.library);
|
| -
|
| - /**
|
| - * Registers that the library of this node imports [importLibrary] through the
|
| - * [import] tag.
|
| - */
|
| - void registerImportDependency(Import import,
|
| - LibraryElement importedLibrary) {
|
| - imports = imports.prepend(new ImportLink(import, importedLibrary));
|
| - }
|
| -
|
| - /**
|
| - * Registers that the library of this node is exported by
|
| - * [exportingLibraryNode] through the [export] tag.
|
| - */
|
| - void registerExportDependency(Export export,
|
| - LibraryDependencyNode exportingLibraryNode) {
|
| - dependencies =
|
| - dependencies.prepend(new ExportLink(export, exportingLibraryNode));
|
| - }
|
| -
|
| - /**
|
| - * Registers all non-private locally declared members of the library of this
|
| - * node to be exported. This forms the basis for the work-list computation of
|
| - * the export scopes performed in [LibraryDependencyHandler.computeExports].
|
| - */
|
| - void registerInitialExports() {
|
| - for (Element element in library.getNonPrivateElementsInScope()) {
|
| - pendingExportMap[element] = const Link<Export>();
|
| - }
|
| - }
|
| -
|
| - void registerHandledExports(LibraryElement exportedLibraryElement,
|
| - Export export,
|
| - CombinatorFilter filter) {
|
| - assert(invariant(library, exportedLibraryElement.exportsHandled));
|
| - for (Element exportedElement in exportedLibraryElement.exports) {
|
| - if (!filter.exclude(exportedElement)) {
|
| - Link<Export> exports =
|
| - pendingExportMap.putIfAbsent(exportedElement,
|
| - () => const Link<Export>());
|
| - pendingExportMap[exportedElement] = exports.prepend(export);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Registers the compute export scope with the node library.
|
| - */
|
| - void registerExports() {
|
| - library.setExports(exportScope.values.toList());
|
| - }
|
| -
|
| - /**
|
| - * Registers the imports of the node library.
|
| - */
|
| - void registerImports(Compiler compiler) {
|
| - for (ImportLink link in imports) {
|
| - link.importLibrary(compiler, library);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Copies and clears pending export set for this node.
|
| - */
|
| - Map<Element, Link<Export>> pullPendingExports() {
|
| - Map<Element, Link<Export>> pendingExports =
|
| - new Map<Element, Link<Export>>.from(pendingExportMap);
|
| - pendingExportMap.clear();
|
| - return pendingExports;
|
| - }
|
| -
|
| - /**
|
| - * Adds [element] to the export scope for this node. If the [element] name
|
| - * is a duplicate, an error element is inserted into the export scope.
|
| - */
|
| - Element addElementToExportScope(Compiler compiler, Element element,
|
| - Link<Export> exports) {
|
| - String name = element.name;
|
| -
|
| - void reportDuplicateExport(Element duplicate,
|
| - Link<Export> duplicateExports,
|
| - {bool reportError: true}) {
|
| - assert(invariant(library, !duplicateExports.isEmpty,
|
| - message: "No export for $duplicate from ${duplicate.library} "
|
| - "in $library."));
|
| - compiler.withCurrentElement(library, () {
|
| - for (Export export in duplicateExports) {
|
| - if (reportError) {
|
| - compiler.reportError(export,
|
| - MessageKind.DUPLICATE_EXPORT, {'name': name});
|
| - reportError = false;
|
| - } else {
|
| - compiler.reportInfo(export,
|
| - MessageKind.DUPLICATE_EXPORT_CONT, {'name': name});
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - void reportDuplicateExportDecl(Element duplicate,
|
| - Link<Export> duplicateExports) {
|
| - assert(invariant(library, !duplicateExports.isEmpty,
|
| - message: "No export for $duplicate from ${duplicate.library} "
|
| - "in $library."));
|
| - compiler.reportInfo(duplicate, MessageKind.DUPLICATE_EXPORT_DECL,
|
| - {'name': name, 'uriString': duplicateExports.head.uri});
|
| - }
|
| -
|
| - Element existingElement = exportScope[name];
|
| - if (existingElement != null && existingElement != element) {
|
| - if (existingElement.isErroneous) {
|
| - reportDuplicateExport(element, exports);
|
| - reportDuplicateExportDecl(element, exports);
|
| - element = existingElement;
|
| - } else if (existingElement.library == library) {
|
| - // Do nothing. [existingElement] hides [element].
|
| - } else if (element.library == library) {
|
| - // [element] hides [existingElement].
|
| - exportScope[name] = element;
|
| - exporters[element] = exports;
|
| - } else {
|
| - // Declared elements hide exported elements.
|
| - Link<Export> existingExports = exporters[existingElement];
|
| - reportDuplicateExport(existingElement, existingExports);
|
| - reportDuplicateExport(element, exports, reportError: false);
|
| - reportDuplicateExportDecl(existingElement, existingExports);
|
| - reportDuplicateExportDecl(element, exports);
|
| - element = exportScope[name] = new ErroneousElementX(
|
| - MessageKind.DUPLICATE_EXPORT, {'name': name}, name, library);
|
| - }
|
| - } else {
|
| - exportScope[name] = element;
|
| - exporters[element] = exports;
|
| - }
|
| - return element;
|
| - }
|
| -
|
| - /**
|
| - * Propagates the exported [element] to all library nodes that depend upon
|
| - * this node. If the propagation updated any pending exports, [:true:] is
|
| - * returned.
|
| - */
|
| - bool propagateElement(Element element) {
|
| - bool change = false;
|
| - for (ExportLink link in dependencies) {
|
| - if (link.exportElement(element)) {
|
| - change = true;
|
| - }
|
| - }
|
| - return change;
|
| - }
|
| -
|
| - /**
|
| - * Adds [element] to the pending exports of this node and returns [:true:] if
|
| - * the pending export set was modified. The combinators of [export] are used
|
| - * to filter the element.
|
| - */
|
| - bool addElementToPendingExports(Element element, Export export) {
|
| - bool changed = false;
|
| - if (!identical(exportScope[element.name], element)) {
|
| - Link<Export> exports = pendingExportMap.putIfAbsent(element, () {
|
| - changed = true;
|
| - return const Link<Export>();
|
| - });
|
| - pendingExportMap[element] = exports.prepend(export);
|
| - }
|
| - return changed;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Helper class used for computing the possibly cyclic import/export scopes of
|
| - * a set of libraries.
|
| - *
|
| - * This class is used by [ScannerTask.scanLibrary] to collect all newly loaded
|
| - * libraries and to compute their import/export scopes through a fixed-point
|
| - * algorithm.
|
| - */
|
| -class LibraryDependencyHandler implements LibraryLoader {
|
| - final _LibraryLoaderTask task;
|
| -
|
| - /**
|
| - * Newly loaded libraries and their corresponding node in the library
|
| - * dependency graph. Libraries that have already been fully loaded are not
|
| - * part of the dependency graph of this handler since their export scopes have
|
| - * already been computed.
|
| - */
|
| - Map<LibraryElement, LibraryDependencyNode> nodeMap =
|
| - new Map<LibraryElement, LibraryDependencyNode>();
|
| -
|
| - LibraryDependencyHandler(this.task);
|
| -
|
| - Compiler get compiler => task.compiler;
|
| -
|
| - /// The libraries loaded with this handler.
|
| - Iterable<LibraryElement> get loadedLibraries => nodeMap.keys;
|
| -
|
| - /**
|
| - * Performs a fixed-point computation on the export scopes of all registered
|
| - * libraries and creates the import/export of the libraries based on the
|
| - * fixed-point.
|
| - */
|
| - void computeExports() {
|
| - bool changed = true;
|
| - while (changed) {
|
| - changed = false;
|
| - Map<LibraryDependencyNode, Map<Element, Link<Export>>> tasks =
|
| - new Map<LibraryDependencyNode, Map<Element, Link<Export>>>();
|
| -
|
| - // Locally defined elements take precedence over exported
|
| - // elements. So we must propagate local elements first. We
|
| - // ensure this by pulling the pending exports before
|
| - // propagating. This enforces that we handle exports
|
| - // breadth-first, with locally defined elements being level 0.
|
| - nodeMap.forEach((_, LibraryDependencyNode node) {
|
| - Map<Element, Link<Export>> pendingExports = node.pullPendingExports();
|
| - tasks[node] = pendingExports;
|
| - });
|
| - tasks.forEach((LibraryDependencyNode node,
|
| - Map<Element, Link<Export>> pendingExports) {
|
| - pendingExports.forEach((Element element, Link<Export> exports) {
|
| - element = node.addElementToExportScope(compiler, element, exports);
|
| - if (node.propagateElement(element)) {
|
| - changed = true;
|
| - }
|
| - });
|
| - });
|
| - }
|
| -
|
| - // Setup export scopes. These have to be set before computing the import
|
| - // scopes to avoid accessing uncomputed export scopes during handling of
|
| - // imports.
|
| - nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) {
|
| - node.registerExports();
|
| - });
|
| -
|
| - // Setup import scopes.
|
| - nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) {
|
| - node.registerImports(compiler);
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Registers that [library] depends on [loadedLibrary] through [tag].
|
| - */
|
| - void registerDependency(LibraryElement library,
|
| - LibraryDependency tag,
|
| - LibraryElement loadedLibrary) {
|
| - if (tag != null) {
|
| - library.recordResolvedTag(tag, loadedLibrary);
|
| - }
|
| - if (tag is Export) {
|
| - // [loadedLibrary] is exported by [library].
|
| - LibraryDependencyNode exportingNode = nodeMap[library];
|
| - if (loadedLibrary.exportsHandled) {
|
| - // Export scope already computed on [loadedLibrary].
|
| - var combinatorFilter = new CombinatorFilter.fromTag(tag);
|
| - exportingNode.registerHandledExports(
|
| - loadedLibrary, tag, combinatorFilter);
|
| - return;
|
| - }
|
| - LibraryDependencyNode exportedNode = nodeMap[loadedLibrary];
|
| - assert(invariant(loadedLibrary, exportedNode != null,
|
| - message: "$loadedLibrary has not been registered"));
|
| - assert(invariant(library, exportingNode != null,
|
| - message: "$library has not been registered"));
|
| - exportedNode.registerExportDependency(tag, exportingNode);
|
| - } else if (tag == null || tag is Import) {
|
| - // [loadedLibrary] is imported by [library].
|
| - LibraryDependencyNode importingNode = nodeMap[library];
|
| - assert(invariant(library, importingNode != null,
|
| - message: "$library has not been registered"));
|
| - importingNode.registerImportDependency(tag, loadedLibrary);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Registers [library] for the processing of its import/export scope.
|
| - */
|
| - void registerNewLibrary(LibraryElement library) {
|
| - nodeMap[library] = new LibraryDependencyNode(library);
|
| - compiler.onLibraryCreated(library);
|
| - }
|
| -
|
| - /**
|
| - * Registers all top-level entities of [library] as starting point for the
|
| - * fixed-point computation of the import/export scopes.
|
| - */
|
| - void registerLibraryExports(LibraryElement library) {
|
| - nodeMap[library].registerInitialExports();
|
| - }
|
| -
|
| - Future processLibraryTags(LibraryElement library) {
|
| - return task.processLibraryTags(this, library);
|
| - }
|
| -}
|
|
|