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