Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library deferred_load; | 5 library deferred_load; |
| 6 | 6 |
| 7 import 'dart:uri'; | 7 import 'dart:uri'; |
| 8 import 'dart:collection'; | |
| 8 | 9 |
| 9 import 'dart2jslib.dart' | 10 import 'dart2jslib.dart' |
| 10 show Compiler, | 11 show Compiler, |
| 11 CompilerTask, | 12 CompilerTask, |
| 12 ConstructedConstant, | 13 ConstructedConstant, |
| 13 MessageKind, | 14 MessageKind, |
| 14 SourceString, | 15 SourceString, |
| 15 StringConstant; | 16 StringConstant; |
| 16 | 17 |
| 17 import 'elements/elements.dart' | 18 import 'elements/elements.dart' |
| 18 show ClassElement, | 19 show ClassElement, |
| 19 Element, | 20 Element, |
| 21 Elements, | |
| 22 FunctionElement, | |
| 20 LibraryElement, | 23 LibraryElement, |
| 21 MetadataAnnotation; | 24 MetadataAnnotation, |
| 25 ScopeContainerElement; | |
| 22 | 26 |
| 23 import 'util/util.dart' | 27 import 'util/util.dart' |
| 24 show Link; | 28 show Link; |
| 25 | 29 |
| 26 import 'tree/tree.dart' | 30 import 'tree/tree.dart' |
| 27 show LibraryTag; | 31 show LibraryTag, |
| 32 Node, | |
| 33 Visitor; | |
| 34 | |
| 35 import 'resolution/resolution.dart' | |
| 36 show TreeElements; | |
| 28 | 37 |
| 29 class DeferredLoadTask extends CompilerTask { | 38 class DeferredLoadTask extends CompilerTask { |
| 30 final Set<LibraryElement> deferredLibraries = new Set<LibraryElement>(); | 39 final Set<LibraryElement> deferredLibraries = new Set<LibraryElement>(); |
| 40 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.
| |
| 31 | 41 |
| 32 ClassElement cachedDeferredLibraryClass; | 42 ClassElement cachedDeferredLibraryClass; |
| 33 | 43 |
| 34 DeferredLoadTask(Compiler compiler) : super(compiler); | 44 DeferredLoadTask(Compiler compiler) : super(compiler); |
| 35 | 45 |
| 36 String get name => 'Lazy'; | 46 String get name => 'Lazy'; |
|
kasperl
2013/03/06 10:26:51
Deferred?
ahe
2013/03/06 16:01:41
Done.
| |
| 37 | 47 |
| 38 /// DeferredLibrary from dart:async | 48 /// DeferredLibrary from dart:async |
| 39 ClassElement get deferredLibraryClass { | 49 ClassElement get deferredLibraryClass { |
| 40 if (cachedDeferredLibraryClass == null) { | 50 if (cachedDeferredLibraryClass == null) { |
| 41 cachedDeferredLibraryClass = findDeferredLibraryClass(); | 51 cachedDeferredLibraryClass = findDeferredLibraryClass(); |
| 42 } | 52 } |
| 43 return cachedDeferredLibraryClass; | 53 return cachedDeferredLibraryClass; |
| 44 } | 54 } |
| 45 | 55 |
| 46 ClassElement findDeferredLibraryClass() { | 56 ClassElement findDeferredLibraryClass() { |
| 47 var uri = new Uri.fromComponents(scheme: 'dart', path: 'async'); | 57 var uri = new Uri.fromComponents(scheme: 'dart', path: 'async'); |
| 48 LibraryElement asyncLibrary = | 58 LibraryElement asyncLibrary = |
| 49 compiler.libraryLoader.loadLibrary(uri, null, uri); | 59 compiler.libraryLoader.loadLibrary(uri, null, uri); |
| 50 var element = asyncLibrary.find(const SourceString('DeferredLibrary')); | 60 var element = asyncLibrary.find(const SourceString('DeferredLibrary')); |
| 51 if (element == null) { | 61 if (element == null) { |
| 52 compiler.internalErrorOnElement( | 62 compiler.internalErrorOnElement( |
| 53 asyncLibrary, | 63 asyncLibrary, |
| 54 'dart:async library does not contain required class: ' | 64 'dart:async library does not contain required class: ' |
| 55 'DeferredLibrary'); | 65 'DeferredLibrary'); |
| 56 } | 66 } |
| 57 return element; | 67 return element; |
| 58 } | 68 } |
| 59 | 69 |
| 60 bool isDeferred(Element element) { | 70 bool isDeferred(Element element) { |
| 61 // TODO(ahe): This is really a graph coloring problem. We should | 71 element = element.implementation; |
| 62 // make sure that libraries and elements only used by a deferred | 72 return deferredElements.contains(element); |
| 63 // library are also deferred. | 73 } |
| 64 // Also, if something is deferred depends on your | 74 |
| 65 // perspective. Inside a deferred library, other elements of the | 75 bool isExplicitlyDeferred(Element element) { |
| 66 // same library are not deferred. We should add an extra parameter | 76 element = element.implementation; |
| 67 // to this method to indicate "from where". | |
| 68 return deferredLibraries.contains(element.getLibrary()); | 77 return deferredLibraries.contains(element.getLibrary()); |
| 69 } | 78 } |
| 70 | 79 |
| 71 void registerMainApp(LibraryElement mainApp) { | 80 void onResolutionComplete(FunctionElement main) { |
| 72 if (mainApp == null) return; | 81 if (main == null) return; |
| 82 LibraryElement mainApp = main.getLibrary(); | |
| 73 measureElement(mainApp, () { | 83 measureElement(mainApp, () { |
| 74 deferredLibraries.addAll(findDeferredLibraries(mainApp)); | 84 deferredLibraries.addAll(findDeferredLibraries(mainApp)); |
| 85 if (deferredLibraries.isEmpty) return; | |
| 86 | |
| 87 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.
| |
| 88 new LinkedHashMap<LibraryElement, Set<Element>>(); | |
| 89 Set<Element> eagerElements = new LinkedHashSet<Element>(); | |
| 90 | |
| 91 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.
| |
| 92 for (Element dependency in allElementsResolvedFrom(e)) { | |
| 93 if (isExplicitlyDeferred(dependency)) { | |
| 94 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.
| |
| 95 () => new LinkedHashSet<Element>()) | |
| 96 .add(dependency); | |
| 97 } else if (mainApp != dependency.getLibrary()) { | |
| 98 eagerElements.add(dependency); | |
| 99 } | |
| 100 } | |
| 101 }); | |
| 102 | |
| 103 eagerElements.addAll(compiler.globalDependencies.backendDependencies); | |
| 104 | |
| 105 computeTransitiveClosure(eagerElements); | |
| 106 | |
| 107 for (Set<Element> e in deferredElements.values) { | |
| 108 computeTransitiveClosure(e); | |
| 109 e.removeAll(eagerElements); | |
| 110 for (Element element in e) { | |
| 111 this.deferredElements.add(element); | |
| 112 } | |
| 113 } | |
| 114 | |
|
kasperl
2013/03/06 10:26:51
Too many newlines.
ahe
2013/03/06 16:01:41
Done.
| |
| 115 | |
| 116 // 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
| |
| 117 Map<Element, List<LibraryElement>> reverseMap = | |
| 118 new LinkedHashMap<Element, List<LibraryElement>>(); | |
| 119 | |
| 120 deferredElements.forEach((LibraryElement lib, Set<Element> map) { | |
| 121 for (Element e in map) { | |
| 122 reverseMap.putIfAbsent(e, | |
|
kasperl
2013/03/06 10:26:51
You an extra local here too?
ahe
2013/03/06 16:01:41
Done.
| |
| 123 () => <LibraryElement>[]).add(lib); | |
| 124 } | |
| 125 }); | |
| 126 | |
| 127 // 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.
| |
| 128 | |
| 75 }); | 129 }); |
| 76 } | 130 } |
| 77 | 131 |
| 132 /// Returns all elements in the tree map of [element], but not the | |
| 133 /// transitive closure. | |
| 134 Set<Element> allElementsResolvedFrom(Element element) { | |
| 135 element = element.implementation; | |
| 136 // 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.
| |
| 137 // top-level functions, but I only record constructors. | |
| 138 Set<Element> result = new LinkedHashSet<Element>(); | |
| 139 if (element.isGenerativeConstructor()) { | |
| 140 element = element.getEnclosingClass(); | |
| 141 } | |
| 142 if (element.isClass()) { | |
| 143 ClassElement cls = element; | |
| 144 cls.forEachLocalMember((Element e) { | |
| 145 result.addAll(DependencyCollector.collect(e, compiler)); | |
| 146 }); | |
| 147 for (var type in cls.allSupertypes) { | |
| 148 result.add(type.element); | |
| 149 } | |
| 150 result.add(cls); | |
| 151 } else if (Elements.isStaticOrTopLevel(element)) { | |
| 152 result.addAll(DependencyCollector.collect(element, compiler)); | |
| 153 } | |
| 154 return result; | |
| 155 } | |
| 156 | |
| 157 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.
| |
| 158 Set<Element> workSet = new LinkedHashSet.from(elements); | |
| 159 Set<Element> closure = new LinkedHashSet<Element>(); | |
| 160 while (!workSet.isEmpty) { | |
| 161 Element current = workSet.first; | |
| 162 workSet.remove(current); | |
| 163 if (closure.contains(current)) continue; | |
| 164 workSet.addAll(allElementsResolvedFrom(current)); | |
| 165 closure.add(current); | |
| 166 } | |
| 167 elements.addAll(closure); | |
| 168 } | |
| 169 | |
| 78 Link<LibraryElement> findDeferredLibraries(LibraryElement library) { | 170 Link<LibraryElement> findDeferredLibraries(LibraryElement library) { |
| 79 Link<LibraryElement> link = const Link<LibraryElement>(); | 171 Link<LibraryElement> link = const Link<LibraryElement>(); |
| 80 for (LibraryTag tag in library.tags) { | 172 for (LibraryTag tag in library.tags) { |
| 81 Link<MetadataAnnotation> metadata = tag.metadata; | 173 Link<MetadataAnnotation> metadata = tag.metadata; |
| 82 if (metadata == null) continue; | 174 if (metadata == null) continue; |
| 83 for (MetadataAnnotation metadata in tag.metadata) { | 175 for (MetadataAnnotation metadata in tag.metadata) { |
| 84 metadata.ensureResolved(compiler); | 176 metadata.ensureResolved(compiler); |
| 85 Element element = metadata.value.computeType(compiler).element; | 177 Element element = metadata.value.computeType(compiler).element; |
| 86 if (element == deferredLibraryClass) { | 178 if (element == deferredLibraryClass) { |
| 87 ConstructedConstant value = metadata.value; | 179 ConstructedConstant value = metadata.value; |
| 88 StringConstant nameField = value.fields[0]; | 180 StringConstant nameField = value.fields[0]; |
| 89 SourceString expectedName = nameField.toDartString().source; | 181 SourceString expectedName = nameField.toDartString().source; |
| 90 LibraryElement deferredLibrary = library.getLibraryFromTag(tag); | 182 LibraryElement deferredLibrary = library.getLibraryFromTag(tag); |
| 91 link = link.prepend(deferredLibrary); | 183 link = link.prepend(deferredLibrary); |
| 92 SourceString actualName = | 184 SourceString actualName = |
| 93 new SourceString(deferredLibrary.getLibraryOrScriptName()); | 185 new SourceString(deferredLibrary.getLibraryOrScriptName()); |
| 94 if (expectedName != actualName) { | 186 if (expectedName != actualName) { |
| 95 compiler.reportErrorCode( | 187 compiler.reportErrorCode( |
| 96 metadata, | 188 metadata, |
| 97 MessageKind.DEFERRED_LIBRARY_NAME_MISMATCH, | 189 MessageKind.DEFERRED_LIBRARY_NAME_MISMATCH, |
| 98 { 'expectedName': expectedName.slowToString(), | 190 { 'expectedName': expectedName.slowToString(), |
| 99 'actualName': actualName.slowToString()}); | 191 'actualName': actualName.slowToString()}); |
| 100 } | 192 } |
| 101 } | 193 } |
| 102 } | 194 } |
| 103 } | 195 } |
| 104 return link; | 196 return link; |
| 105 } | 197 } |
| 106 } | 198 } |
| 199 | |
| 200 class DependencyCollector extends Visitor { | |
| 201 final Set<Element> dependencies = new LinkedHashSet<Element>(); | |
| 202 final TreeElements elements; | |
| 203 final Compiler compiler; | |
| 204 | |
| 205 DependencyCollector(this.elements, this.compiler); | |
| 206 | |
| 207 visitNode(Node node) { | |
| 208 Element dependency = elements[node]; | |
| 209 if (dependency != null) { | |
| 210 TreeElements elements = | |
| 211 compiler.enqueuer.resolution.getCachedElements(dependency); | |
| 212 if (elements != null) { | |
| 213 dependencies.add(elements.currentElement); | |
| 214 } | |
| 215 } | |
| 216 node.visitChildren(this); | |
| 217 } | |
| 218 | |
| 219 static Set<Element> collect(Element element, Compiler compiler) { | |
| 220 TreeElements elements = | |
| 221 compiler.enqueuer.resolution.getCachedElements(element); | |
| 222 if (elements == null) return new LinkedHashSet<Element>(); | |
| 223 Node node = element.parseNode(compiler); | |
| 224 var collector = new DependencyCollector(elements, compiler); | |
| 225 node.accept(collector); | |
| 226 collector.dependencies.addAll(elements.backendDependencies); | |
| 227 return collector.dependencies; | |
| 228 } | |
| 229 } | |
| OLD | NEW |