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'; | |
ngeoffray
2013/03/07 11:21:14
show LinkedHashSet?
ahe
2013/03/11 12:28:01
Done.
| |
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 = |
40 new LinkedHashSet<LibraryElement>(); | |
41 | |
42 /// Records all elements that are deferred. | |
43 /// | |
44 /// Long term, we want to split deferred element into more than one | |
45 /// file (one for each library that is deferred), and this field | |
46 /// should become obsolete. | |
47 final Set<Element> allDeferredElements = new LinkedHashSet<Element>(); | |
31 | 48 |
32 ClassElement cachedDeferredLibraryClass; | 49 ClassElement cachedDeferredLibraryClass; |
33 | 50 |
34 DeferredLoadTask(Compiler compiler) : super(compiler); | 51 DeferredLoadTask(Compiler compiler) : super(compiler); |
35 | 52 |
36 String get name => 'Lazy'; | 53 String get name => 'Deferred Loading'; |
37 | 54 |
38 /// DeferredLibrary from dart:async | 55 /// DeferredLibrary from dart:async |
39 ClassElement get deferredLibraryClass { | 56 ClassElement get deferredLibraryClass { |
40 if (cachedDeferredLibraryClass == null) { | 57 if (cachedDeferredLibraryClass == null) { |
41 cachedDeferredLibraryClass = findDeferredLibraryClass(); | 58 cachedDeferredLibraryClass = findDeferredLibraryClass(); |
42 } | 59 } |
43 return cachedDeferredLibraryClass; | 60 return cachedDeferredLibraryClass; |
44 } | 61 } |
45 | 62 |
46 ClassElement findDeferredLibraryClass() { | 63 ClassElement findDeferredLibraryClass() { |
47 var uri = new Uri.fromComponents(scheme: 'dart', path: 'async'); | 64 var uri = new Uri.fromComponents(scheme: 'dart', path: 'async'); |
48 LibraryElement asyncLibrary = | 65 LibraryElement asyncLibrary = |
49 compiler.libraryLoader.loadLibrary(uri, null, uri); | 66 compiler.libraryLoader.loadLibrary(uri, null, uri); |
50 var element = asyncLibrary.find(const SourceString('DeferredLibrary')); | 67 var element = asyncLibrary.find(const SourceString('DeferredLibrary')); |
51 if (element == null) { | 68 if (element == null) { |
52 compiler.internalErrorOnElement( | 69 compiler.internalErrorOnElement( |
53 asyncLibrary, | 70 asyncLibrary, |
54 'dart:async library does not contain required class: ' | 71 'dart:async library does not contain required class: ' |
55 'DeferredLibrary'); | 72 'DeferredLibrary'); |
56 } | 73 } |
57 return element; | 74 return element; |
58 } | 75 } |
59 | 76 |
60 bool isDeferred(Element element) { | 77 bool isDeferred(Element element) { |
61 // TODO(ahe): This is really a graph coloring problem. We should | 78 element = element.implementation; |
62 // make sure that libraries and elements only used by a deferred | 79 return allDeferredElements.contains(element); |
63 // library are also deferred. | 80 } |
64 // Also, if something is deferred depends on your | 81 |
65 // perspective. Inside a deferred library, other elements of the | 82 bool isExplicitlyDeferred(Element element) { |
66 // same library are not deferred. We should add an extra parameter | 83 element = element.implementation; |
67 // to this method to indicate "from where". | |
68 return deferredLibraries.contains(element.getLibrary()); | 84 return deferredLibraries.contains(element.getLibrary()); |
69 } | 85 } |
70 | 86 |
71 void registerMainApp(LibraryElement mainApp) { | 87 void onResolutionComplete(FunctionElement main) { |
72 if (mainApp == null) return; | 88 if (main == null) return; |
89 LibraryElement mainApp = main.getLibrary(); | |
73 measureElement(mainApp, () { | 90 measureElement(mainApp, () { |
74 deferredLibraries.addAll(findDeferredLibraries(mainApp)); | 91 deferredLibraries.addAll(findDeferredLibraries(mainApp)); |
92 if (deferredLibraries.isEmpty) return; | |
93 | |
94 Map<LibraryElement, Set<Element>> deferredElements = | |
95 new LinkedHashMap<LibraryElement, Set<Element>>(); | |
96 Set<Element> eagerElements = new LinkedHashSet<Element>(); | |
97 | |
98 // Iterate through the local members of the main script. Create | |
99 // a root-set of elements that must be loaded eagerly | |
100 // (everything that is directly referred to from the main | |
101 // script, but not imported from a deferred library), as well as | |
102 // root-sets for deferred libraries. | |
103 mainApp.forEachLocalMember((Element e) { | |
104 for (Element dependency in allElementsResolvedFrom(e)) { | |
105 if (isExplicitlyDeferred(dependency)) { | |
106 Set<Element> deferredElementsFromLibrary = | |
107 deferredElements.putIfAbsent( | |
108 dependency.getLibrary(), | |
109 () => new LinkedHashSet<Element>()); | |
110 deferredElementsFromLibrary.add(dependency); | |
111 } else if (mainApp != dependency.getLibrary()) { | |
ngeoffray
2013/03/07 11:21:14
Nitpicking, but I would reverse the operators here
ahe
2013/03/11 12:28:01
Done.
| |
112 eagerElements.add(dependency); | |
113 } | |
114 } | |
115 }); | |
116 | |
117 // Also add "global" dependencies to the eager root-set. These | |
118 // are things that the backend need but cannot associate with a | |
119 // particular element, for example, startRootIsolate. This set | |
120 // also contains elements for which we lack precise information. | |
121 eagerElements.addAll(compiler.globalDependencies.otherDependencies); | |
122 | |
123 addTransitiveClosureTo(eagerElements); | |
124 | |
125 for (Set<Element> e in deferredElements.values) { | |
126 addTransitiveClosureTo(e); | |
127 e.removeAll(eagerElements); | |
128 for (Element element in e) { | |
129 allDeferredElements.add(element); | |
130 } | |
131 } | |
132 | |
133 // TODO(ahe): The following code has no effect yet. I'm | |
134 // including it as a comment for how to extend this to support | |
135 // multiple deferred files. | |
136 Map<Element, List<LibraryElement>> reverseMap = | |
137 new LinkedHashMap<Element, List<LibraryElement>>(); | |
138 | |
139 deferredElements.forEach((LibraryElement library, Set<Element> map) { | |
140 for (Element element in map) { | |
141 List<LibraryElement> libraries = | |
142 reverseMap.putIfAbsent(element, () => <LibraryElement>[]); | |
143 libraries.add(library); | |
144 } | |
145 }); | |
146 | |
147 // Now compute the output files based on the lists in reverseMap. | |
148 // TODO(ahe): Do that. | |
75 }); | 149 }); |
76 } | 150 } |
77 | 151 |
152 /// Returns all elements in the tree map of [element], but not the | |
153 /// transitive closure. | |
154 Set<Element> allElementsResolvedFrom(Element element) { | |
155 element = element.implementation; | |
156 Set<Element> result = new LinkedHashSet<Element>(); | |
157 if (element.isGenerativeConstructor()) { | |
158 element = element.getEnclosingClass(); | |
ngeoffray
2013/03/07 11:21:14
This looks magic. Is it to get the fields? Please
ahe
2013/03/11 12:28:01
I've added a comment.
| |
159 } | |
160 if (element.isClass()) { | |
161 ClassElement cls = element; | |
162 cls.forEachLocalMember((Element e) { | |
163 result.addAll(DependencyCollector.collect(e, compiler)); | |
164 }); | |
165 for (var type in cls.allSupertypes) { | |
166 result.add(type.element); | |
167 } | |
168 result.add(cls); | |
169 } else if (Elements.isStaticOrTopLevel(element)) { | |
170 result.addAll(DependencyCollector.collect(element, compiler)); | |
171 } | |
ngeoffray
2013/03/07 11:21:14
Why can't you end up with a member here? Should yo
ahe
2013/03/11 12:28:01
I can end up with a member, but that is not releva
| |
172 return result; | |
173 } | |
174 | |
175 void addTransitiveClosureTo(Set<Element> elements) { | |
176 Set<Element> workSet = new LinkedHashSet.from(elements); | |
177 Set<Element> closure = new LinkedHashSet<Element>(); | |
178 while (!workSet.isEmpty) { | |
179 Element current = workSet.first; | |
180 workSet.remove(current); | |
181 if (closure.contains(current)) continue; | |
182 workSet.addAll(allElementsResolvedFrom(current)); | |
183 closure.add(current); | |
184 } | |
185 elements.addAll(closure); | |
186 } | |
187 | |
78 Link<LibraryElement> findDeferredLibraries(LibraryElement library) { | 188 Link<LibraryElement> findDeferredLibraries(LibraryElement library) { |
79 Link<LibraryElement> link = const Link<LibraryElement>(); | 189 Link<LibraryElement> link = const Link<LibraryElement>(); |
80 for (LibraryTag tag in library.tags) { | 190 for (LibraryTag tag in library.tags) { |
81 Link<MetadataAnnotation> metadata = tag.metadata; | 191 Link<MetadataAnnotation> metadata = tag.metadata; |
82 if (metadata == null) continue; | 192 if (metadata == null) continue; |
83 for (MetadataAnnotation metadata in tag.metadata) { | 193 for (MetadataAnnotation metadata in tag.metadata) { |
84 metadata.ensureResolved(compiler); | 194 metadata.ensureResolved(compiler); |
85 Element element = metadata.value.computeType(compiler).element; | 195 Element element = metadata.value.computeType(compiler).element; |
86 if (element == deferredLibraryClass) { | 196 if (element == deferredLibraryClass) { |
87 ConstructedConstant value = metadata.value; | 197 ConstructedConstant value = metadata.value; |
88 StringConstant nameField = value.fields[0]; | 198 StringConstant nameField = value.fields[0]; |
89 SourceString expectedName = nameField.toDartString().source; | 199 SourceString expectedName = nameField.toDartString().source; |
90 LibraryElement deferredLibrary = library.getLibraryFromTag(tag); | 200 LibraryElement deferredLibrary = library.getLibraryFromTag(tag); |
91 link = link.prepend(deferredLibrary); | 201 link = link.prepend(deferredLibrary); |
92 SourceString actualName = | 202 SourceString actualName = |
93 new SourceString(deferredLibrary.getLibraryOrScriptName()); | 203 new SourceString(deferredLibrary.getLibraryOrScriptName()); |
94 if (expectedName != actualName) { | 204 if (expectedName != actualName) { |
95 compiler.reportErrorCode( | 205 compiler.reportErrorCode( |
96 metadata, | 206 metadata, |
97 MessageKind.DEFERRED_LIBRARY_NAME_MISMATCH, | 207 MessageKind.DEFERRED_LIBRARY_NAME_MISMATCH, |
98 { 'expectedName': expectedName.slowToString(), | 208 { 'expectedName': expectedName.slowToString(), |
99 'actualName': actualName.slowToString()}); | 209 'actualName': actualName.slowToString()}); |
100 } | 210 } |
101 } | 211 } |
102 } | 212 } |
103 } | 213 } |
104 return link; | 214 return link; |
105 } | 215 } |
106 } | 216 } |
217 | |
218 class DependencyCollector extends Visitor { | |
219 final Set<Element> dependencies = new LinkedHashSet<Element>(); | |
220 final TreeElements elements; | |
221 final Compiler compiler; | |
222 | |
223 DependencyCollector(this.elements, this.compiler); | |
224 | |
225 visitNode(Node node) { | |
226 Element dependency = elements[node]; | |
227 if (dependency != null) { | |
228 TreeElements elements = | |
229 compiler.enqueuer.resolution.getCachedElements(dependency); | |
230 if (elements != null) { | |
231 dependencies.add(elements.currentElement); | |
232 } | |
233 } | |
234 node.visitChildren(this); | |
235 } | |
236 | |
237 static Set<Element> collect(Element element, Compiler compiler) { | |
238 TreeElements elements = | |
239 compiler.enqueuer.resolution.getCachedElements(element); | |
240 if (elements == null) return new LinkedHashSet<Element>(); | |
241 Node node = element.parseNode(compiler); | |
242 var collector = new DependencyCollector(elements, compiler); | |
243 node.accept(collector); | |
244 collector.dependencies.addAll(elements.otherDependencies); | |
245 return collector.dependencies; | |
246 } | |
247 } | |
OLD | NEW |