Chromium Code Reviews| 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..024f9d49af24ef7c6c282c0c8f38abda18185166 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,17 +18,26 @@ 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<Element> deferredElements = new LinkedHashSet<Element>(); | 
| 
 
kasperl
2013/03/06 10:26:51
Is there a reason this needs to be linked when the
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| ClassElement cachedDeferredLibraryClass; | 
| @@ -58,23 +68,105 @@ 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 deferredElements.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 = | 
| 
 
kasperl
2013/03/06 10:26:51
Not sure the shadowing (field/local) here helps th
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| + new LinkedHashMap<LibraryElement, Set<Element>>(); | 
| + Set<Element> eagerElements = new LinkedHashSet<Element>(); | 
| + | 
| + mainApp.forEachLocalMember((Element e) { | 
| 
 
kasperl
2013/03/06 10:26:51
Maybe add a comment here that briefly outlines wha
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| + for (Element dependency in allElementsResolvedFrom(e)) { | 
| + if (isExplicitlyDeferred(dependency)) { | 
| + deferredElements.putIfAbsent(dependency.getLibrary(), | 
| 
 
kasperl
2013/03/06 10:26:51
I think this would be easier to read if you pulled
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| + () => new LinkedHashSet<Element>()) | 
| + .add(dependency); | 
| + } else if (mainApp != dependency.getLibrary()) { | 
| + eagerElements.add(dependency); | 
| + } | 
| + } | 
| + }); | 
| + | 
| + eagerElements.addAll(compiler.globalDependencies.backendDependencies); | 
| + | 
| + computeTransitiveClosure(eagerElements); | 
| + | 
| + for (Set<Element> e in deferredElements.values) { | 
| + computeTransitiveClosure(e); | 
| + e.removeAll(eagerElements); | 
| + for (Element element in e) { | 
| + this.deferredElements.add(element); | 
| + } | 
| + } | 
| + | 
| 
 
kasperl
2013/03/06 10:26:51
Too many newlines.
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| + | 
| + // Pseudo code to be implemented: | 
| 
 
kasperl
2013/03/06 10:26:51
Not sure I understand how to review this based on
 
ahe
2013/03/06 16:01:41
I updated the comment to be a TODO. Hope it is cle
 
 | 
| + Map<Element, List<LibraryElement>> reverseMap = | 
| + new LinkedHashMap<Element, List<LibraryElement>>(); | 
| + | 
| + deferredElements.forEach((LibraryElement lib, Set<Element> map) { | 
| + for (Element e in map) { | 
| + reverseMap.putIfAbsent(e, | 
| 
 
kasperl
2013/03/06 10:26:51
You an extra local here too?
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| + () => <LibraryElement>[]).add(lib); | 
| + } | 
| + }); | 
| + | 
| + // Now compute the output files based on the lists in reverseMap. | 
| 
 
kasperl
2013/03/06 10:26:51
Change to a TODO or add the code?
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| + | 
| }); | 
| } | 
| + /// Returns all elements in the tree map of [element], but not the | 
| + /// transitive closure. | 
| + Set<Element> allElementsResolvedFrom(Element element) { | 
| + element = element.implementation; | 
| + // TODO(ahe): This doesn't work. I need to record classes and | 
| 
 
kasperl
2013/03/06 10:26:51
Hmm. The code seems to deal with classes, statics,
 
ahe
2013/03/06 16:01:41
This was an old note to myself I forgot to remove.
 
 | 
| + // top-level functions, but I only record constructors. | 
| + 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 computeTransitiveClosure(Set<Element> elements) { | 
| 
 
kasperl
2013/03/06 10:26:51
The method names makes it sound like you'll be ret
 
ahe
2013/03/06 16:01:41
Done.
 
 | 
| + 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 +196,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; | 
| + } | 
| +} |