| 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..a9045d0c20d2c459472177dd562aa4e5ae0f5983 100644
 | 
| --- a/dart/sdk/lib/_internal/compiler/implementation/deferred_load.dart
 | 
| +++ b/dart/sdk/lib/_internal/compiler/implementation/deferred_load.dart
 | 
| @@ -4,7 +4,12 @@
 | 
|  
 | 
|  library deferred_load;
 | 
|  
 | 
| -import 'dart:uri';
 | 
| +import 'dart:uri'
 | 
| +       show Uri;
 | 
| +
 | 
| +import 'dart:collection'
 | 
| +       show LinkedHashMap,
 | 
| +            LinkedHashSet;
 | 
|  
 | 
|  import 'dart2jslib.dart'
 | 
|         show Compiler,
 | 
| @@ -17,23 +22,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 +79,135 @@ 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;
 | 
| +
 | 
| +      // TODO(ahe): Enforce the following invariants on
 | 
| +      // [deferredElements] and [eagerElements]:
 | 
| +      // 1. Only static or top-level elements are recorded.
 | 
| +      // 2. Only implementation is stored.
 | 
| +      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 (dependency.getLibrary() != mainApp) {
 | 
| +            eagerElements.add(dependency.implementation);
 | 
| +          }
 | 
| +        }
 | 
| +      });
 | 
| +
 | 
| +      // 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.otherDependencies);
 | 
| +
 | 
| +      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()) {
 | 
| +      // When instantiating a class, we record a reference to the
 | 
| +      // constructor, not the class itself.
 | 
| +      element = element.getEnclosingClass().implementation;
 | 
| +    }
 | 
| +    if (element.isClass()) {
 | 
| +      // If we see a class, add everything its instance members refer
 | 
| +      // to.  Static members are not relevant.
 | 
| +      ClassElement cls = element.declaration;
 | 
| +      cls.forEachLocalMember((Element e) {
 | 
| +        if (!e.isInstanceMember()) return;
 | 
| +        result.addAll(DependencyCollector.collect(e.implementation, compiler));
 | 
| +      });
 | 
| +      if (cls.implementation != cls) {
 | 
| +        // TODO(ahe): Why doesn't ClassElement.forEachLocalMember do this?
 | 
| +        cls.implementation.forEachLocalMember((Element e) {
 | 
| +          if (!e.isInstanceMember()) return;
 | 
| +          result.addAll(DependencyCollector.collect(e.implementation,
 | 
| +                                                    compiler));
 | 
| +        });
 | 
| +      }
 | 
| +      for (var type in cls.allSupertypes) {
 | 
| +        result.add(type.element.implementation);
 | 
| +      }
 | 
| +      result.add(cls.implementation);
 | 
| +    } else if (Elements.isStaticOrTopLevel(element)) {
 | 
| +      result.addAll(DependencyCollector.collect(element, compiler));
 | 
| +    }
 | 
| +    // Other elements, in particular instance members, are ignored as
 | 
| +    // they are processed as part of the class.
 | 
| +    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 +237,29 @@ 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) {
 | 
| +    node.visitChildren(this);
 | 
| +    Element dependency = elements[node];
 | 
| +    if (dependency == null) return;
 | 
| +    dependencies.add(dependency.implementation);
 | 
| +  }
 | 
| +
 | 
| +  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.otherDependencies);
 | 
| +    return collector.dependencies;
 | 
| +  }
 | 
| +}
 | 
| 
 |