| Index: dart/sdk/lib/_internal/compiler/implementation/deferred_load.dart
|
| diff --git a/dart/sdk/lib/_internal/compiler/implementation/deferred_load.dart b/dart/sdk/lib/_internal/compiler/implementation/deferred_load.dart
|
| index c623bd824bb34c53c0a0b03c53419d2257f93bdf..1ccca2b70bdb4d3d852330c19544ad4627f25f03 100644
|
| --- a/dart/sdk/lib/_internal/compiler/implementation/deferred_load.dart
|
| +++ b/dart/sdk/lib/_internal/compiler/implementation/deferred_load.dart
|
| @@ -5,6 +5,7 @@
|
| library deferred_load;
|
|
|
| import 'dart:uri';
|
| +import 'dart:collection';
|
|
|
| import 'dart2jslib.dart'
|
| show Compiler,
|
| @@ -17,23 +18,39 @@ import 'dart2jslib.dart'
|
| import 'elements/elements.dart'
|
| show ClassElement,
|
| Element,
|
| + Elements,
|
| + FunctionElement,
|
| LibraryElement,
|
| - MetadataAnnotation;
|
| + MetadataAnnotation,
|
| + ScopeContainerElement;
|
|
|
| import 'util/util.dart'
|
| show Link;
|
|
|
| import 'tree/tree.dart'
|
| - show LibraryTag;
|
| + show LibraryTag,
|
| + Node,
|
| + Visitor;
|
| +
|
| +import 'resolution/resolution.dart'
|
| + show TreeElements;
|
|
|
| class DeferredLoadTask extends CompilerTask {
|
| - final Set<LibraryElement> deferredLibraries = new Set<LibraryElement>();
|
| + final Set<LibraryElement> deferredLibraries =
|
| + new LinkedHashSet<LibraryElement>();
|
| +
|
| + /// Records all elements that are deferred.
|
| + ///
|
| + /// Long term, we want to split deferred element into more than one
|
| + /// file (one for each library that is deferred), and this field
|
| + /// should become obsolete.
|
| + final Set<Element> allDeferredElements = new LinkedHashSet<Element>();
|
|
|
| ClassElement cachedDeferredLibraryClass;
|
|
|
| DeferredLoadTask(Compiler compiler) : super(compiler);
|
|
|
| - String get name => 'Lazy';
|
| + String get name => 'Deferred Loading';
|
|
|
| /// DeferredLibrary from dart:async
|
| ClassElement get deferredLibraryClass {
|
| @@ -58,23 +75,116 @@ class DeferredLoadTask extends CompilerTask {
|
| }
|
|
|
| bool isDeferred(Element element) {
|
| - // TODO(ahe): This is really a graph coloring problem. We should
|
| - // make sure that libraries and elements only used by a deferred
|
| - // library are also deferred.
|
| - // Also, if something is deferred depends on your
|
| - // perspective. Inside a deferred library, other elements of the
|
| - // same library are not deferred. We should add an extra parameter
|
| - // to this method to indicate "from where".
|
| + element = element.implementation;
|
| + return allDeferredElements.contains(element);
|
| + }
|
| +
|
| + bool isExplicitlyDeferred(Element element) {
|
| + element = element.implementation;
|
| return deferredLibraries.contains(element.getLibrary());
|
| }
|
|
|
| - void registerMainApp(LibraryElement mainApp) {
|
| - if (mainApp == null) return;
|
| + void onResolutionComplete(FunctionElement main) {
|
| + if (main == null) return;
|
| + LibraryElement mainApp = main.getLibrary();
|
| measureElement(mainApp, () {
|
| deferredLibraries.addAll(findDeferredLibraries(mainApp));
|
| + if (deferredLibraries.isEmpty) return;
|
| +
|
| + Map<LibraryElement, Set<Element>> deferredElements =
|
| + new LinkedHashMap<LibraryElement, Set<Element>>();
|
| + Set<Element> eagerElements = new LinkedHashSet<Element>();
|
| +
|
| + // Iterate through the local members of the main script. Create
|
| + // a root-set of elements that must be loaded eagerly
|
| + // (everything that is directly referred to from the main
|
| + // script, but not imported from a deferred library), as well as
|
| + // root-sets for deferred libraries.
|
| + mainApp.forEachLocalMember((Element e) {
|
| + for (Element dependency in allElementsResolvedFrom(e)) {
|
| + if (isExplicitlyDeferred(dependency)) {
|
| + Set<Element> deferredElementsFromLibrary =
|
| + deferredElements.putIfAbsent(
|
| + dependency.getLibrary(),
|
| + () => new LinkedHashSet<Element>());
|
| + deferredElementsFromLibrary.add(dependency);
|
| + } else if (mainApp != dependency.getLibrary()) {
|
| + eagerElements.add(dependency);
|
| + }
|
| + }
|
| + });
|
| +
|
| + // Also add "global" dependencies to the eager root-set. These
|
| + // are things that the backend need but cannot associate with a
|
| + // particular element, for example, startRootIsolate. This set
|
| + // also contains elements for which we lack precise information.
|
| + eagerElements.addAll(compiler.globalDependencies.backendDependencies);
|
| +
|
| + addTransitiveClosureTo(eagerElements);
|
| +
|
| + for (Set<Element> e in deferredElements.values) {
|
| + addTransitiveClosureTo(e);
|
| + e.removeAll(eagerElements);
|
| + for (Element element in e) {
|
| + allDeferredElements.add(element);
|
| + }
|
| + }
|
| +
|
| + // TODO(ahe): The following code has no effect yet. I'm
|
| + // including it as a comment for how to extend this to support
|
| + // multiple deferred files.
|
| + Map<Element, List<LibraryElement>> reverseMap =
|
| + new LinkedHashMap<Element, List<LibraryElement>>();
|
| +
|
| + deferredElements.forEach((LibraryElement library, Set<Element> map) {
|
| + for (Element element in map) {
|
| + List<LibraryElement> libraries =
|
| + reverseMap.putIfAbsent(element, () => <LibraryElement>[]);
|
| + libraries.add(library);
|
| + }
|
| + });
|
| +
|
| + // Now compute the output files based on the lists in reverseMap.
|
| + // TODO(ahe): Do that.
|
| });
|
| }
|
|
|
| + /// Returns all elements in the tree map of [element], but not the
|
| + /// transitive closure.
|
| + Set<Element> allElementsResolvedFrom(Element element) {
|
| + element = element.implementation;
|
| + Set<Element> result = new LinkedHashSet<Element>();
|
| + if (element.isGenerativeConstructor()) {
|
| + element = element.getEnclosingClass();
|
| + }
|
| + if (element.isClass()) {
|
| + ClassElement cls = element;
|
| + cls.forEachLocalMember((Element e) {
|
| + result.addAll(DependencyCollector.collect(e, compiler));
|
| + });
|
| + for (var type in cls.allSupertypes) {
|
| + result.add(type.element);
|
| + }
|
| + result.add(cls);
|
| + } else if (Elements.isStaticOrTopLevel(element)) {
|
| + result.addAll(DependencyCollector.collect(element, compiler));
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + void addTransitiveClosureTo(Set<Element> elements) {
|
| + Set<Element> workSet = new LinkedHashSet.from(elements);
|
| + Set<Element> closure = new LinkedHashSet<Element>();
|
| + while (!workSet.isEmpty) {
|
| + Element current = workSet.first;
|
| + workSet.remove(current);
|
| + if (closure.contains(current)) continue;
|
| + workSet.addAll(allElementsResolvedFrom(current));
|
| + closure.add(current);
|
| + }
|
| + elements.addAll(closure);
|
| + }
|
| +
|
| Link<LibraryElement> findDeferredLibraries(LibraryElement library) {
|
| Link<LibraryElement> link = const Link<LibraryElement>();
|
| for (LibraryTag tag in library.tags) {
|
| @@ -104,3 +214,34 @@ class DeferredLoadTask extends CompilerTask {
|
| return link;
|
| }
|
| }
|
| +
|
| +class DependencyCollector extends Visitor {
|
| + final Set<Element> dependencies = new LinkedHashSet<Element>();
|
| + final TreeElements elements;
|
| + final Compiler compiler;
|
| +
|
| + DependencyCollector(this.elements, this.compiler);
|
| +
|
| + visitNode(Node node) {
|
| + Element dependency = elements[node];
|
| + if (dependency != null) {
|
| + TreeElements elements =
|
| + compiler.enqueuer.resolution.getCachedElements(dependency);
|
| + if (elements != null) {
|
| + dependencies.add(elements.currentElement);
|
| + }
|
| + }
|
| + node.visitChildren(this);
|
| + }
|
| +
|
| + static Set<Element> collect(Element element, Compiler compiler) {
|
| + TreeElements elements =
|
| + compiler.enqueuer.resolution.getCachedElements(element);
|
| + if (elements == null) return new LinkedHashSet<Element>();
|
| + Node node = element.parseNode(compiler);
|
| + var collector = new DependencyCollector(elements, compiler);
|
| + node.accept(collector);
|
| + collector.dependencies.addAll(elements.backendDependencies);
|
| + return collector.dependencies;
|
| + }
|
| +}
|
|
|