OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 library deferred_load; | |
6 | |
7 import 'constants/expressions.dart'; | |
8 import 'constants/values.dart' show | |
9 ConstantValue, | |
10 ConstructedConstantValue, | |
11 DeferredConstantValue, | |
12 StringConstantValue; | |
13 | |
14 import 'dart2jslib.dart' show | |
15 Backend, | |
16 Compiler, | |
17 CompilerTask, | |
18 invariant, | |
19 MessageKind; | |
20 | |
21 import 'dart_backend/dart_backend.dart' show | |
22 DartBackend; | |
23 | |
24 import 'js_backend/js_backend.dart' show | |
25 JavaScriptBackend; | |
26 | |
27 import 'elements/elements.dart' show | |
28 AstElement, | |
29 ClassElement, | |
30 Element, | |
31 ElementKind, | |
32 Elements, | |
33 FunctionElement, | |
34 LibraryElement, | |
35 MetadataAnnotation, | |
36 PrefixElement, | |
37 ScopeContainerElement, | |
38 TypedefElement, | |
39 VoidElement; | |
40 | |
41 import 'util/util.dart' show | |
42 Link, makeUnique; | |
43 | |
44 import 'util/setlet.dart' show | |
45 Setlet; | |
46 | |
47 import 'tree/tree.dart' show | |
48 Import, | |
49 LibraryTag, | |
50 LibraryDependency, | |
51 LiteralDartString, | |
52 LiteralString, | |
53 NewExpression, | |
54 Node; | |
55 | |
56 import 'tree/tree.dart' as ast; | |
57 | |
58 import 'resolution/resolution.dart' show | |
59 AnalyzableElementX, | |
60 TreeElements; | |
61 | |
62 /// A "hunk" of the program that will be loaded whenever one of its [imports] | |
63 /// are loaded. | |
64 /// | |
65 /// Elements that are only used in one deferred import, is in an OutputUnit with | |
66 /// the deferred import as single element in the [imports] set. | |
67 /// | |
68 /// Whenever a deferred Element is shared between several deferred imports it is | |
69 /// in an output unit with those imports in the [imports] Set. | |
70 /// | |
71 /// OutputUnits are equal if their [imports] are equal. | |
72 class OutputUnit { | |
73 /// The deferred imports that will load this output unit when one of them is | |
74 /// loaded. | |
75 final Setlet<Import> imports = new Setlet<Import>(); | |
76 | |
77 /// A unique name representing this [OutputUnit]. | |
78 /// Based on the set of [imports]. | |
79 String name; | |
80 | |
81 String toString() => "OutputUnit($name)"; | |
82 | |
83 bool operator==(OutputUnit other) { | |
84 return imports.length == other.imports.length && | |
85 imports.containsAll(other.imports); | |
86 } | |
87 | |
88 int get hashCode { | |
89 int sum = 0; | |
90 for (Import import in imports) { | |
91 sum = (sum + import.hashCode) & 0x3FFFFFFF; // Stay in 30 bit range. | |
92 } | |
93 return sum; | |
94 } | |
95 } | |
96 | |
97 /// For each deferred import, find elements and constants to be loaded when that | |
98 /// import is loaded. Elements that are used by several deferred imports are in | |
99 /// shared OutputUnits. | |
100 class DeferredLoadTask extends CompilerTask { | |
101 /// The name of this task. | |
102 String get name => 'Deferred Loading'; | |
103 | |
104 /// DeferredLibrary from dart:async | |
105 ClassElement get deferredLibraryClass => compiler.deferredLibraryClass; | |
106 | |
107 /// A synthetic [Import] representing the loading of the main | |
108 /// program. | |
109 final Import _fakeMainImport = new Import(null, new LiteralString(null, | |
110 new LiteralDartString("main")), null, null, null); | |
111 | |
112 /// The OutputUnit that will be loaded when the program starts. | |
113 final OutputUnit mainOutputUnit = new OutputUnit(); | |
114 | |
115 /// A set containing (eventually) all output units that will result from the | |
116 /// program. | |
117 final Set<OutputUnit> allOutputUnits = new Set<OutputUnit>(); | |
118 | |
119 /// Will be `true` if the program contains deferred libraries. | |
120 bool isProgramSplit = false; | |
121 | |
122 /// A mapping from the name of a defer import to all the output units it | |
123 /// depends on in a list of lists to be loaded in the order they appear. | |
124 /// | |
125 /// For example {"lib1": [[lib1_lib2_lib3], [lib1_lib2, lib1_lib3], | |
126 /// [lib1]]} would mean that in order to load "lib1" first the hunk | |
127 /// lib1_lib2_lib2 should be loaded, then the hunks lib1_lib2 and lib1_lib3 | |
128 /// can be loaded in parallel. And finally lib1 can be loaded. | |
129 final Map<String, List<OutputUnit>> hunksToLoad = | |
130 new Map<String, List<OutputUnit>>(); | |
131 final Map<Import, String> importDeferName = new Map<Import, String>(); | |
132 | |
133 /// A mapping from elements and constants to their output unit. Query this via | |
134 /// [outputUnitForElement] | |
135 final Map<Element, OutputUnit> _elementToOutputUnit = | |
136 new Map<Element, OutputUnit>(); | |
137 | |
138 /// A mapping from constants to their output unit. Query this via | |
139 /// [outputUnitForConstant] | |
140 final Map<ConstantValue, OutputUnit> _constantToOutputUnit = | |
141 new Map<ConstantValue, OutputUnit>(); | |
142 | |
143 /// All the imports with a [DeferredLibrary] annotation, mapped to the | |
144 /// [LibraryElement] they import. | |
145 /// The main library is included in this set for convenience. | |
146 final Map<Import, LibraryElement> _allDeferredImports = | |
147 new Map<Import, LibraryElement>(); | |
148 | |
149 // For each deferred import we want to know exactly what elements have to | |
150 // be loaded. | |
151 Map<Import, Set<Element>> _importedDeferredBy = null; | |
152 Map<Import, Set<ConstantValue>> _constantsDeferredBy = null; | |
153 | |
154 Set<Element> _mainElements = new Set<Element>(); | |
155 | |
156 DeferredLoadTask(Compiler compiler) : super(compiler) { | |
157 mainOutputUnit.imports.add(_fakeMainImport); | |
158 } | |
159 | |
160 Backend get backend => compiler.backend; | |
161 | |
162 /// Returns the [OutputUnit] where [element] belongs. | |
163 OutputUnit outputUnitForElement(Element element) { | |
164 if (!isProgramSplit) return mainOutputUnit; | |
165 | |
166 element = element.implementation; | |
167 while (!_elementToOutputUnit.containsKey(element)) { | |
168 // TODO(21051): workaround: it looks like we output annotation constants | |
169 // for classes that we don't include in the output. This seems to happen | |
170 // when we have reflection but can see that some classes are not needed. | |
171 // We still add the annotation but don't run through it below (where we | |
172 // assign every element to its output unit). | |
173 if (element.enclosingElement == null) { | |
174 _elementToOutputUnit[element] = mainOutputUnit; | |
175 break; | |
176 } | |
177 element = element.enclosingElement.implementation; | |
178 } | |
179 return _elementToOutputUnit[element]; | |
180 } | |
181 | |
182 /// Returns the [OutputUnit] where [constant] belongs. | |
183 OutputUnit outputUnitForConstant(ConstantValue constant) { | |
184 if (!isProgramSplit) return mainOutputUnit; | |
185 | |
186 return _constantToOutputUnit[constant]; | |
187 } | |
188 | |
189 bool isDeferred(Element element) { | |
190 return outputUnitForElement(element) != mainOutputUnit; | |
191 } | |
192 | |
193 /// Returns true if e1 and e2 are in the same output unit. | |
194 bool inSameOutputUnit(Element e1, Element e2) { | |
195 return outputUnitForElement(e1) == outputUnitForElement(e2); | |
196 } | |
197 | |
198 void registerConstantDeferredUse(DeferredConstantValue constant, | |
199 PrefixElement prefix) { | |
200 OutputUnit outputUnit = new OutputUnit(); | |
201 outputUnit.imports.add(prefix.deferredImport); | |
202 _constantToOutputUnit[constant] = outputUnit; | |
203 } | |
204 | |
205 /// Answers whether [element] is explicitly deferred when referred to from | |
206 /// [library]. | |
207 bool _isExplicitlyDeferred(Element element, LibraryElement library) { | |
208 Link<Import> imports = _getImports(element, library); | |
209 // If the element is not imported explicitly, it is implicitly imported | |
210 // not deferred. | |
211 if (imports.isEmpty) return false; | |
212 // An element could potentially be loaded by several imports. If all of them | |
213 // is explicitly deferred, we say the element is explicitly deferred. | |
214 // TODO(sigurdm): We might want to give a warning if the imports do not | |
215 // agree. | |
216 return imports.every((Import import) => import.isDeferred); | |
217 } | |
218 | |
219 /// Returns a [Link] of every [Import] that imports [element] into [library]. | |
220 Link<Import> _getImports(Element element, LibraryElement library) { | |
221 if (element.isClassMember) { | |
222 element = element.enclosingClass; | |
223 } | |
224 if (element.isAccessor) { | |
225 element = (element as FunctionElement).abstractField; | |
226 } | |
227 return library.getImportsFor(element); | |
228 } | |
229 | |
230 /// Finds all elements and constants that [element] depends directly on. | |
231 /// (not the transitive closure.) | |
232 /// | |
233 /// Adds the results to [elements] and [constants]. | |
234 void _collectAllElementsAndConstantsResolvedFrom( | |
235 Element element, | |
236 Set<Element> elements, | |
237 Set<ConstantValue> constants, | |
238 isMirrorUsage) { | |
239 | |
240 /// Recursively add the constant and its dependencies to [constants]. | |
241 void addConstants(ConstantValue constant) { | |
242 if (constants.contains(constant)) return; | |
243 constants.add(constant); | |
244 if (constant is ConstructedConstantValue) { | |
245 elements.add(constant.type.element); | |
246 } | |
247 constant.getDependencies().forEach(addConstants); | |
248 } | |
249 | |
250 /// Collects all direct dependencies of [element]. | |
251 /// | |
252 /// The collected dependent elements and constants are are added to | |
253 /// [elements] and [constants] respectively. | |
254 void collectDependencies(Element element) { | |
255 // TODO(johnniwinther): Remove this when [AbstractFieldElement] has been | |
256 // removed. | |
257 if (element is! AstElement) return; | |
258 AstElement astElement = element; | |
259 | |
260 // TODO(sigurdm): We want to be more specific about this - need a better | |
261 // way to query "liveness". | |
262 if (astElement is! TypedefElement && | |
263 !compiler.enqueuer.resolution.hasBeenResolved(astElement)) { | |
264 return; | |
265 } | |
266 | |
267 TreeElements treeElements = astElement.resolvedAst.elements; | |
268 | |
269 assert(treeElements != null); | |
270 | |
271 for (Element dependency in treeElements.allElements) { | |
272 if (dependency.isLocal && !dependency.isFunction) continue; | |
273 if (dependency.isErroneous) continue; | |
274 if (dependency.isTypeVariable) continue; | |
275 | |
276 elements.add(dependency); | |
277 } | |
278 treeElements.forEachConstantNode((Node node, _) { | |
279 // Explicitly depend on the backend constants. | |
280 addConstants( | |
281 backend.constants.getConstantForNode(node, treeElements).value); | |
282 }); | |
283 elements.addAll(treeElements.otherDependencies); | |
284 } | |
285 | |
286 // TODO(sigurdm): How is metadata on a patch-class handled? | |
287 for (MetadataAnnotation metadata in element.metadata) { | |
288 ConstantExpression constant = | |
289 backend.constants.getConstantForMetadata(metadata); | |
290 if (constant != null) { | |
291 addConstants(constant.value); | |
292 } | |
293 } | |
294 if (element.isClass) { | |
295 // If we see a class, add everything its live instance members refer | |
296 // to. Static members are not relevant, unless we are processing | |
297 // extra dependencies due to mirrors. | |
298 void addLiveInstanceMember(Element element) { | |
299 if (!compiler.enqueuer.resolution.hasBeenResolved(element)) return; | |
300 if (!isMirrorUsage && !element.isInstanceMember) return; | |
301 collectDependencies(element.implementation); | |
302 } | |
303 ClassElement cls = element.declaration; | |
304 cls.forEachLocalMember(addLiveInstanceMember); | |
305 if (cls.implementation != cls) { | |
306 // TODO(ahe): Why doesn't ClassElement.forEachLocalMember do this? | |
307 cls.implementation.forEachLocalMember(addLiveInstanceMember); | |
308 } | |
309 for (var type in cls.implementation.allSupertypes) { | |
310 elements.add(type.element.implementation); | |
311 } | |
312 elements.add(cls.implementation); | |
313 } else if (Elements.isStaticOrTopLevel(element) || | |
314 element.isConstructor) { | |
315 collectDependencies(element); | |
316 } | |
317 if (element.isGenerativeConstructor) { | |
318 // When instantiating a class, we record a reference to the | |
319 // constructor, not the class itself. We must add all the | |
320 // instance members of the constructor's class. | |
321 ClassElement implementation = | |
322 element.enclosingClass.implementation; | |
323 _collectAllElementsAndConstantsResolvedFrom( | |
324 implementation, elements, constants, isMirrorUsage); | |
325 } | |
326 | |
327 // Other elements, in particular instance members, are ignored as | |
328 // they are processed as part of the class. | |
329 } | |
330 | |
331 /// Returns the transitive closure of all libraries that are imported | |
332 /// from root without DeferredLibrary annotations. | |
333 Set<LibraryElement> _nonDeferredReachableLibraries(LibraryElement root) { | |
334 Set<LibraryElement> result = new Set<LibraryElement>(); | |
335 | |
336 void traverseLibrary(LibraryElement library) { | |
337 if (result.contains(library)) return; | |
338 result.add(library); | |
339 | |
340 iterateTags(LibraryElement library) { | |
341 // TODO(sigurdm): Make helper getLibraryDependencyTags when tags is | |
342 // changed to be a List instead of a Link. | |
343 for (LibraryTag tag in library.tags) { | |
344 if (tag is! LibraryDependency) continue; | |
345 LibraryDependency libraryDependency = tag; | |
346 if (!(libraryDependency is Import && libraryDependency.isDeferred)) { | |
347 LibraryElement importedLibrary = library.getLibraryFromTag(tag); | |
348 traverseLibrary(importedLibrary); | |
349 } | |
350 } | |
351 } | |
352 | |
353 iterateTags(library); | |
354 if (library.isPatched) { | |
355 iterateTags(library.implementation); | |
356 } | |
357 } | |
358 traverseLibrary(root); | |
359 result.add(compiler.coreLibrary); | |
360 return result; | |
361 } | |
362 | |
363 /// Recursively traverses the graph of dependencies from [element], mapping | |
364 /// deferred imports to each dependency it needs in the sets | |
365 /// [_importedDeferredBy] and [_constantsDeferredBy]. | |
366 void _mapDependencies(Element element, Import import, | |
367 {isMirrorUsage: false}) { | |
368 Set<Element> elements = _importedDeferredBy.putIfAbsent(import, | |
369 () => new Set<Element>()); | |
370 Set<ConstantValue> constants = _constantsDeferredBy.putIfAbsent(import, | |
371 () => new Set<ConstantValue>()); | |
372 | |
373 // Only process elements once, unless we are doing dependencies due to | |
374 // mirrors, which are added in additional traversals. | |
375 if (!isMirrorUsage && elements.contains(element)) return; | |
376 // Anything used directly by main will be loaded from the start | |
377 // We do not need to traverse it again. | |
378 if (import != _fakeMainImport && _mainElements.contains(element)) return; | |
379 | |
380 // Here we modify [_importedDeferredBy]. | |
381 elements.add(element); | |
382 | |
383 Set<Element> dependentElements = new Set<Element>(); | |
384 | |
385 // This call can modify [_importedDeferredBy] and [_constantsDeferredBy]. | |
386 _collectAllElementsAndConstantsResolvedFrom( | |
387 element, dependentElements, constants, isMirrorUsage); | |
388 | |
389 LibraryElement library = element.library; | |
390 for (Element dependency in dependentElements) { | |
391 if (_isExplicitlyDeferred(dependency, library)) { | |
392 for (Import deferredImport in _getImports(dependency, library)) { | |
393 _mapDependencies(dependency, deferredImport); | |
394 }; | |
395 } else { | |
396 _mapDependencies(dependency, import); | |
397 } | |
398 } | |
399 } | |
400 | |
401 /// Adds extra dependencies coming from mirror usage. | |
402 /// | |
403 /// The elements are added with [_mapDependencies]. | |
404 void _addMirrorElements() { | |
405 void mapDependenciesIfResolved(Element element, Import deferredImport) { | |
406 // If an element is the target of a MirrorsUsed annotation but never used | |
407 // It will not be resolved, and we should not call isNeededForReflection. | |
408 // TODO(sigurdm): Unresolved elements should just answer false when | |
409 // asked isNeededForReflection. Instead an internal error is triggered. | |
410 // So we have to filter them out here. | |
411 if (element is AnalyzableElementX && !element.hasTreeElements) return; | |
412 if (compiler.backend.isAccessibleByReflection(element)) { | |
413 _mapDependencies(element, deferredImport, isMirrorUsage: true); | |
414 } | |
415 } | |
416 | |
417 // For each deferred import we analyze all elements reachable from the | |
418 // imported library through non-deferred imports. | |
419 handleLibrary(LibraryElement library, Import deferredImport) { | |
420 library.implementation.forEachLocalMember((Element element) { | |
421 mapDependenciesIfResolved(element, deferredImport); | |
422 }); | |
423 | |
424 for (MetadataAnnotation metadata in library.metadata) { | |
425 ConstantExpression constant = | |
426 backend.constants.getConstantForMetadata(metadata); | |
427 if (constant != null) { | |
428 _mapDependencies(constant.value.computeType(compiler).element, | |
429 deferredImport); | |
430 } | |
431 } | |
432 for (LibraryTag tag in library.tags) { | |
433 for (MetadataAnnotation metadata in tag.metadata) { | |
434 ConstantExpression constant = | |
435 backend.constants.getConstantForMetadata(metadata); | |
436 if (constant != null) { | |
437 _mapDependencies(constant.value.computeType(compiler).element, | |
438 deferredImport); | |
439 } | |
440 } | |
441 } | |
442 } | |
443 | |
444 for (Import deferredImport in _allDeferredImports.keys) { | |
445 LibraryElement deferredLibrary = _allDeferredImports[deferredImport]; | |
446 for (LibraryElement library in | |
447 _nonDeferredReachableLibraries(deferredLibrary)) { | |
448 handleLibrary(library, deferredImport); | |
449 } | |
450 } | |
451 } | |
452 | |
453 /// Computes a unique string for the name field for each outputUnit. | |
454 /// | |
455 /// Also sets up the [hunksToLoad] mapping. | |
456 void _assignNamesToOutputUnits(Set<OutputUnit> allOutputUnits) { | |
457 Set<String> usedImportNames = new Set<String>(); | |
458 | |
459 // Finds the first argument to the [DeferredLibrary] annotation | |
460 void computeImportDeferName(Import import) { | |
461 String result; | |
462 if (import == _fakeMainImport) { | |
463 result = "main"; | |
464 } else if (import.isDeferred) { | |
465 result = import.prefix.toString(); | |
466 } else { | |
467 Link<MetadataAnnotation> metadatas = import.metadata; | |
468 assert(metadatas != null); | |
469 for (MetadataAnnotation metadata in metadatas) { | |
470 metadata.ensureResolved(compiler); | |
471 Element element = | |
472 metadata.constant.value.computeType(compiler).element; | |
473 if (element == deferredLibraryClass) { | |
474 ConstructedConstantValue constant = metadata.constant.value; | |
475 StringConstantValue s = constant.fields[0]; | |
476 result = s.primitiveValue.slowToString(); | |
477 break; | |
478 } | |
479 } | |
480 } | |
481 assert(result != null); | |
482 importDeferName[import] = makeUnique(result, usedImportNames);; | |
483 } | |
484 | |
485 int counter = 1; | |
486 | |
487 for (Import import in _allDeferredImports.keys) { | |
488 computeImportDeferName(import); | |
489 } | |
490 | |
491 for (OutputUnit outputUnit in allOutputUnits) { | |
492 if (outputUnit == mainOutputUnit) { | |
493 outputUnit.name = "main"; | |
494 } else { | |
495 outputUnit.name = "$counter"; | |
496 ++counter; | |
497 } | |
498 } | |
499 | |
500 List sortedOutputUnits = new List.from(allOutputUnits); | |
501 // Sort the output units in descending order of the number of imports they | |
502 // include. | |
503 | |
504 // The loading of the output units mut be ordered because a superclass needs | |
505 // to be initialized before its subclass. | |
506 // But a class can only depend on another class in an output unit shared by | |
507 // a strict superset of the imports: | |
508 // By contradiction: Assume a class C in output unit shared by imports in | |
509 // the set S1 = (lib1,.., lib_n) depends on a class D in an output unit | |
510 // shared by S2 such that S2 not a superset of S1. Let lib_s be a library in | |
511 // S1 not in S2. lib_s must depend on C, and then in turn on D therefore D | |
512 // is not in the right output unit. | |
513 sortedOutputUnits.sort((a, b) => b.imports.length - a.imports.length); | |
514 | |
515 // For each deferred import we find out which outputUnits to load. | |
516 for (Import import in _allDeferredImports.keys) { | |
517 if (import == _fakeMainImport) continue; | |
518 hunksToLoad[importDeferName[import]] = new List<OutputUnit>(); | |
519 for (OutputUnit outputUnit in sortedOutputUnits) { | |
520 if (outputUnit == mainOutputUnit) continue; | |
521 if (outputUnit.imports.contains(import)) { | |
522 hunksToLoad[importDeferName[import]].add(outputUnit); | |
523 } | |
524 } | |
525 } | |
526 } | |
527 | |
528 void onResolutionComplete(FunctionElement main) { | |
529 if (!isProgramSplit) { | |
530 allOutputUnits.add(mainOutputUnit); | |
531 return; | |
532 } | |
533 if (main == null) return; | |
534 LibraryElement mainLibrary = main.library; | |
535 _importedDeferredBy = new Map<Import, Set<Element>>(); | |
536 _constantsDeferredBy = new Map<Import, Set<ConstantValue>>(); | |
537 _importedDeferredBy[_fakeMainImport] = _mainElements; | |
538 | |
539 measureElement(mainLibrary, () { | |
540 | |
541 // Starting from main, traverse the program and find all dependencies. | |
542 _mapDependencies(compiler.mainFunction, _fakeMainImport); | |
543 | |
544 // Also add "global" dependencies to the main OutputUnit. These are | |
545 // things that the backend need but cannot associate with a particular | |
546 // element, for example, startRootIsolate. This set also contains | |
547 // elements for which we lack precise information. | |
548 for (Element element in compiler.globalDependencies.otherDependencies) { | |
549 _mapDependencies(element, _fakeMainImport); | |
550 } | |
551 | |
552 // Now check to see if we have to add more elements due to mirrors. | |
553 if (compiler.mirrorsLibrary != null) { | |
554 _addMirrorElements(); | |
555 } | |
556 | |
557 // Build the OutputUnits using these two maps. | |
558 Map<Element, OutputUnit> elementToOutputUnitBuilder = | |
559 new Map<Element, OutputUnit>(); | |
560 Map<ConstantValue, OutputUnit> constantToOutputUnitBuilder = | |
561 new Map<ConstantValue, OutputUnit>(); | |
562 | |
563 // Reverse the mappings. For each element record an OutputUnit collecting | |
564 // all deferred imports mapped to this element. Same for constants. | |
565 for (Import import in _importedDeferredBy.keys) { | |
566 for (Element element in _importedDeferredBy[import]) { | |
567 // Only one file should be loaded when the program starts, so make | |
568 // sure that only one OutputUnit is created for [fakeMainImport]. | |
569 if (import == _fakeMainImport) { | |
570 elementToOutputUnitBuilder[element] = mainOutputUnit; | |
571 } else { | |
572 elementToOutputUnitBuilder | |
573 .putIfAbsent(element, () => new OutputUnit()) | |
574 .imports.add(import); | |
575 } | |
576 } | |
577 for (ConstantValue constant in _constantsDeferredBy[import]) { | |
578 // Only one file should be loaded when the program starts, so make | |
579 // sure that only one OutputUnit is created for [fakeMainImport]. | |
580 if (import == _fakeMainImport) { | |
581 constantToOutputUnitBuilder[constant] = mainOutputUnit; | |
582 } else { | |
583 constantToOutputUnitBuilder | |
584 .putIfAbsent(constant, () => new OutputUnit()) | |
585 .imports.add(import); | |
586 } | |
587 } | |
588 } | |
589 | |
590 // Release maps; | |
591 _importedDeferredBy = null; | |
592 _constantsDeferredBy = null; | |
593 | |
594 // Find all the output units elements/constants have been mapped to, and | |
595 // canonicalize them. | |
596 elementToOutputUnitBuilder.forEach( | |
597 (Element element, OutputUnit outputUnit) { | |
598 OutputUnit representative = allOutputUnits.lookup(outputUnit); | |
599 if (representative == null) { | |
600 representative = outputUnit; | |
601 allOutputUnits.add(representative); | |
602 } | |
603 _elementToOutputUnit[element] = representative; | |
604 }); | |
605 constantToOutputUnitBuilder.forEach( | |
606 (ConstantValue constant, OutputUnit outputUnit) { | |
607 OutputUnit representative = allOutputUnits.lookup(outputUnit); | |
608 if (representative == null) { | |
609 representative = outputUnit; | |
610 allOutputUnits.add(representative); | |
611 } | |
612 _constantToOutputUnit[constant] = representative; | |
613 }); | |
614 | |
615 // Generate a unique name for each OutputUnit. | |
616 _assignNamesToOutputUnits(allOutputUnits); | |
617 }); | |
618 } | |
619 | |
620 void ensureMetadataResolved(Compiler compiler) { | |
621 if (compiler.mainApp == null) return; | |
622 _allDeferredImports[_fakeMainImport] = compiler.mainApp; | |
623 var lastDeferred; | |
624 // When detecting duplicate prefixes of deferred libraries there are 4 | |
625 // cases of duplicate prefixes: | |
626 // 1. | |
627 // import "lib.dart" deferred as a; | |
628 // import "lib2.dart" deferred as a; | |
629 // 2. | |
630 // import "lib.dart" deferred as a; | |
631 // import "lib2.dart" as a; | |
632 // 3. | |
633 // import "lib.dart" as a; | |
634 // import "lib2.dart" deferred as a; | |
635 // 4. | |
636 // import "lib.dart" as a; | |
637 // import "lib2.dart" as a; | |
638 // We must be able to signal error for case 1, 2, 3, but accept case 4. | |
639 | |
640 // The prefixes that have been used by any imports in this library. | |
641 Setlet<String> usedPrefixes = new Setlet<String>(); | |
642 // The last deferred import we saw with a given prefix (if any). | |
643 Map<String, Import> prefixDeferredImport = new Map<String, Import>(); | |
644 for (LibraryElement library in compiler.libraryLoader.libraries) { | |
645 compiler.withCurrentElement(library, () { | |
646 prefixDeferredImport.clear(); | |
647 usedPrefixes.clear(); | |
648 // TODO(sigurdm): Make helper getLibraryImportTags when tags is a List | |
649 // instead of a Link. | |
650 for (LibraryTag tag in library.tags) { | |
651 if (tag is! Import) continue; | |
652 Import import = tag; | |
653 | |
654 /// Give an error if the old annotation-based syntax has been used. | |
655 Link<MetadataAnnotation> metadataList = import.metadata; | |
656 if (metadataList != null) { | |
657 for (MetadataAnnotation metadata in metadataList) { | |
658 metadata.ensureResolved(compiler); | |
659 Element element = | |
660 metadata.constant.value.computeType(compiler).element; | |
661 if (element == deferredLibraryClass) { | |
662 compiler.reportFatalError( | |
663 import, MessageKind.DEFERRED_OLD_SYNTAX); | |
664 } | |
665 } | |
666 } | |
667 | |
668 String prefix = (import.prefix != null) | |
669 ? import.prefix.toString() | |
670 : null; | |
671 // The last import we saw with the same prefix. | |
672 Import previousDeferredImport = prefixDeferredImport[prefix]; | |
673 if (import.isDeferred) { | |
674 _allDeferredImports[import] = library.getLibraryFromTag(import); | |
675 | |
676 if (prefix == null) { | |
677 compiler.reportError(import, | |
678 MessageKind.DEFERRED_LIBRARY_WITHOUT_PREFIX); | |
679 } else { | |
680 prefixDeferredImport[prefix] = import; | |
681 } | |
682 isProgramSplit = true; | |
683 lastDeferred = import; | |
684 } | |
685 if (prefix != null) { | |
686 if (previousDeferredImport != null || | |
687 (import.isDeferred && usedPrefixes.contains(prefix))) { | |
688 Import failingImport = (previousDeferredImport != null) | |
689 ? previousDeferredImport | |
690 : import; | |
691 compiler.reportError(failingImport.prefix, | |
692 MessageKind.DEFERRED_LIBRARY_DUPLICATE_PREFIX); | |
693 } | |
694 usedPrefixes.add(prefix); | |
695 } | |
696 } | |
697 }); | |
698 } | |
699 Backend backend = compiler.backend; | |
700 if (isProgramSplit && backend is JavaScriptBackend) { | |
701 backend.registerCheckDeferredIsLoaded(compiler.globalDependencies); | |
702 } | |
703 if (isProgramSplit && backend is DartBackend) { | |
704 // TODO(sigurdm): Implement deferred loading for dart2dart. | |
705 compiler.reportWarning( | |
706 lastDeferred, | |
707 MessageKind.DEFERRED_LIBRARY_DART_2_DART); | |
708 isProgramSplit = false; | |
709 } | |
710 } | |
711 | |
712 /// If [send] is a static send with a deferred element, returns the | |
713 /// [PrefixElement] that the first prefix of the send resolves to. | |
714 /// Otherwise returns null. | |
715 /// | |
716 /// Precondition: send must be static. | |
717 /// | |
718 /// Example: | |
719 /// | |
720 /// import "a.dart" deferred as a; | |
721 /// | |
722 /// main() { | |
723 /// print(a.loadLibrary.toString()); | |
724 /// a.loadLibrary().then((_) { | |
725 /// a.run(); | |
726 /// a.foo.method(); | |
727 /// }); | |
728 /// } | |
729 /// | |
730 /// Returns null for a.loadLibrary() (the special | |
731 /// function loadLibrary is not deferred). And returns the PrefixElement for | |
732 /// a.run() and a.foo. | |
733 /// a.loadLibrary.toString() and a.foo.method() are dynamic sends - and | |
734 /// this functions should not be called on them. | |
735 PrefixElement deferredPrefixElement(ast.Send send, TreeElements elements) { | |
736 Element element = elements[send]; | |
737 // The DeferredLoaderGetter is not deferred, therefore we do not return the | |
738 // prefix. | |
739 if (element != null && element.isDeferredLoaderGetter) return null; | |
740 | |
741 ast.Node firstNode(ast.Node node) { | |
742 if (node is! ast.Send) { | |
743 return node; | |
744 } else { | |
745 ast.Send send = node; | |
746 ast.Node receiver = send.receiver; | |
747 ast.Node receiverFirst = firstNode(receiver); | |
748 if (receiverFirst != null) { | |
749 return receiverFirst; | |
750 } else { | |
751 return firstNode(send.selector); | |
752 } | |
753 } | |
754 } | |
755 ast.Node first = firstNode(send); | |
756 ast.Node identifier = first.asIdentifier(); | |
757 if (identifier == null) return null; | |
758 Element maybePrefix = elements[identifier]; | |
759 if (maybePrefix != null && maybePrefix.isPrefix) { | |
760 PrefixElement prefixElement = maybePrefix; | |
761 if (prefixElement.isDeferred) { | |
762 return prefixElement; | |
763 } | |
764 } | |
765 return null; | |
766 } | |
767 } | |
OLD | NEW |