OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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:collection' show Queue; |
| 8 |
7 import 'common/tasks.dart' show CompilerTask; | 9 import 'common/tasks.dart' show CompilerTask; |
8 import 'common.dart'; | 10 import 'common.dart'; |
9 import 'compiler.dart' show Compiler; | 11 import 'compiler.dart' show Compiler; |
10 import 'constants/expressions.dart' show ConstantExpression; | 12 import 'constants/expressions.dart' show ConstantExpression; |
11 import 'constants/values.dart' | 13 import 'constants/values.dart' |
12 show | 14 show |
13 ConstantValue, | 15 ConstantValue, |
14 ConstructedConstantValue, | 16 ConstructedConstantValue, |
15 DeferredConstantValue, | 17 DeferredConstantValue, |
16 StringConstantValue; | 18 StringConstantValue; |
(...skipping 30 matching lines...) Expand all Loading... |
47 | 49 |
48 /// A "hunk" of the program that will be loaded whenever one of its [imports] | 50 /// A "hunk" of the program that will be loaded whenever one of its [imports] |
49 /// are loaded. | 51 /// are loaded. |
50 /// | 52 /// |
51 /// Elements that are only used in one deferred import, is in an OutputUnit with | 53 /// Elements that are only used in one deferred import, is in an OutputUnit with |
52 /// the deferred import as single element in the [imports] set. | 54 /// the deferred import as single element in the [imports] set. |
53 /// | 55 /// |
54 /// Whenever a deferred Element is shared between several deferred imports it is | 56 /// Whenever a deferred Element is shared between several deferred imports it is |
55 /// in an output unit with those imports in the [imports] Set. | 57 /// in an output unit with those imports in the [imports] Set. |
56 /// | 58 /// |
57 /// OutputUnits are equal if their [imports] are equal. | 59 /// We never create two OutputUnits sharing the same set of [imports]. |
58 class OutputUnit { | 60 class OutputUnit implements Comparable<OutputUnit> { |
59 /// The deferred imports that will load this output unit when one of them is | |
60 /// loaded. | |
61 final Setlet<_DeferredImport> imports = new Setlet<_DeferredImport>(); | |
62 | |
63 /// `true` if this output unit is for the main output file. | 61 /// `true` if this output unit is for the main output file. |
64 final bool isMainOutput; | 62 final bool isMainOutput; |
65 | 63 |
66 /// A unique name representing this [OutputUnit]. | 64 /// A unique name representing this [OutputUnit]. |
67 String name; | 65 final String name; |
68 | 66 |
69 OutputUnit({this.isMainOutput: false}); | 67 /// The deferred imports that use the elements in this output unit. |
| 68 final Set<ImportElement> _imports; |
70 | 69 |
71 String toString() => "OutputUnit($name)"; | 70 OutputUnit(this.isMainOutput, this.name, this._imports); |
72 | 71 |
73 bool operator ==(other) { | 72 int compareTo(OutputUnit other) { |
74 return other is OutputUnit && | 73 if (identical(this, other)) return 0; |
75 imports.length == other.imports.length && | 74 if (isMainOutput && !other.isMainOutput) return -1; |
76 imports.containsAll(other.imports); | 75 if (!isMainOutput && other.isMainOutput) return 1; |
| 76 var size = _imports.length; |
| 77 var otherSize = other._imports.length; |
| 78 if (size != otherSize) return size.compareTo(otherSize); |
| 79 var imports = _imports.toList(); |
| 80 var otherImports = other._imports.toList(); |
| 81 for (var i = 0; i < size; i++) { |
| 82 if (imports[i] == otherImports[i]) continue; |
| 83 var a = imports[i].uri.path; |
| 84 var b = otherImports[i].uri.path; |
| 85 var cmp = a.compareTo(b); |
| 86 if (cmp != 0) return cmp; |
| 87 } |
| 88 // TODO(sigmund): make compare stable. If we hit this point, all imported |
| 89 // libraries are the same, however [this] and [other] use different deferred |
| 90 // imports in the program. We can make this stable if we sort based on the |
| 91 // deferred imports themselves (e.g. their declaration location). |
| 92 return name.compareTo(other.name); |
77 } | 93 } |
78 | 94 |
79 int get hashCode { | 95 String toString() => "OutputUnit($name, $_imports)"; |
80 int sum = 0; | |
81 for (_DeferredImport import in imports) { | |
82 sum = (sum + import.hashCode) & 0x3FFFFFFF; // Stay in 30 bit range. | |
83 } | |
84 return sum; | |
85 } | |
86 } | 96 } |
87 | 97 |
88 /// For each deferred import, find elements and constants to be loaded when that | 98 /// For each deferred import, find elements and constants to be loaded when that |
89 /// import is loaded. Elements that are used by several deferred imports are in | 99 /// import is loaded. Elements that are used by several deferred imports are in |
90 /// shared OutputUnits. | 100 /// shared OutputUnits. |
91 class DeferredLoadTask extends CompilerTask { | 101 class DeferredLoadTask extends CompilerTask { |
92 /// The name of this task. | 102 /// The name of this task. |
93 String get name => 'Deferred Loading'; | 103 String get name => 'Deferred Loading'; |
94 | 104 |
95 /// DeferredLibrary from dart:async | 105 /// DeferredLibrary from dart:async |
96 ClassElement get deferredLibraryClass => | 106 ClassElement get deferredLibraryClass => |
97 compiler.resolution.commonElements.deferredLibraryClass; | 107 compiler.resolution.commonElements.deferredLibraryClass; |
98 | 108 |
99 /// A synthetic import representing the loading of the main program. | |
100 final _DeferredImport _fakeMainImport = const _DeferredImport(); | |
101 | |
102 /// The OutputUnit that will be loaded when the program starts. | 109 /// The OutputUnit that will be loaded when the program starts. |
103 final OutputUnit mainOutputUnit = new OutputUnit(isMainOutput: true); | 110 OutputUnit mainOutputUnit; |
104 | 111 |
105 /// A set containing (eventually) all output units that will result from the | 112 /// A set containing (eventually) all output units that will result from the |
106 /// program. | 113 /// program. |
107 final Set<OutputUnit> allOutputUnits = new Set<OutputUnit>(); | 114 final List<OutputUnit> allOutputUnits = new List<OutputUnit>(); |
108 | 115 |
109 /// Will be `true` if the program contains deferred libraries. | 116 /// Will be `true` if the program contains deferred libraries. |
110 bool isProgramSplit = false; | 117 bool isProgramSplit = false; |
111 | 118 |
112 static const ImpactUseCase IMPACT_USE = const ImpactUseCase('Deferred load'); | 119 static const ImpactUseCase IMPACT_USE = const ImpactUseCase('Deferred load'); |
113 | 120 |
114 /// A mapping from the name of a defer import to all the output units it | 121 /// A mapping from the name of a defer import to all the output units it |
115 /// depends on in a list of lists to be loaded in the order they appear. | 122 /// depends on in a list of lists to be loaded in the order they appear. |
116 /// | 123 /// |
117 /// For example {"lib1": [[lib1_lib2_lib3], [lib1_lib2, lib1_lib3], | 124 /// For example {"lib1": [[lib1_lib2_lib3], [lib1_lib2, lib1_lib3], |
118 /// [lib1]]} would mean that in order to load "lib1" first the hunk | 125 /// [lib1]]} would mean that in order to load "lib1" first the hunk |
119 /// lib1_lib2_lib2 should be loaded, then the hunks lib1_lib2 and lib1_lib3 | 126 /// lib1_lib2_lib2 should be loaded, then the hunks lib1_lib2 and lib1_lib3 |
120 /// can be loaded in parallel. And finally lib1 can be loaded. | 127 /// can be loaded in parallel. And finally lib1 can be loaded. |
121 final Map<String, List<OutputUnit>> hunksToLoad = | 128 final Map<String, List<OutputUnit>> hunksToLoad = |
122 new Map<String, List<OutputUnit>>(); | 129 new Map<String, List<OutputUnit>>(); |
123 | 130 |
124 /// A cache of the result of calling `computeImportDeferName` on the keys of | 131 /// A cache of the result of calling `computeImportDeferName` on the keys of |
125 /// this map. | 132 /// this map. |
126 final Map<_DeferredImport, String> importDeferName = | 133 final Map<ImportElement, String> _importDeferName = <ImportElement, String>{}; |
127 <_DeferredImport, String>{}; | |
128 | 134 |
129 /// A mapping from elements and constants to their output unit. Query this via | 135 /// A mapping from elements and constants to their output unit. Query this via |
130 /// [outputUnitForElement] | 136 /// [outputUnitForElement] |
131 final Map<Element, OutputUnit> _elementToOutputUnit = | 137 final Map<Entity, ImportSet> _elementToSet = new Map<Entity, ImportSet>(); |
132 new Map<Element, OutputUnit>(); | |
133 | 138 |
134 /// A mapping from constants to their output unit. Query this via | 139 /// A mapping from constants to their output unit. Query this via |
135 /// [outputUnitForConstant] | 140 /// [outputUnitForConstant] |
136 final Map<ConstantValue, OutputUnit> _constantToOutputUnit = | 141 final Map<ConstantValue, ImportSet> _constantToSet = |
137 new Map<ConstantValue, OutputUnit>(); | 142 new Map<ConstantValue, ImportSet>(); |
138 | 143 |
139 /// All the imports with a [DeferredLibrary] annotation, mapped to the | 144 Iterable<ImportElement> get _allDeferredImports => |
140 /// [LibraryElement] they import. | 145 _deferredImportDescriptions.keys; |
141 /// The main library is included in this set for convenience. | |
142 final Map<_DeferredImport, LibraryElement> _allDeferredImports = | |
143 new Map<_DeferredImport, LibraryElement>(); | |
144 | 146 |
145 /// Because the token-stream is forgotten later in the program, we cache a | 147 /// Because the token-stream is forgotten later in the program, we cache a |
146 /// description of each deferred import. | 148 /// description of each deferred import. |
147 final Map<_DeferredImport, ImportDescription> _deferredImportDescriptions = | 149 final Map<ImportElement, ImportDescription> _deferredImportDescriptions = |
148 <_DeferredImport, ImportDescription>{}; | 150 <ImportElement, ImportDescription>{}; |
149 | 151 |
150 // For each deferred import we want to know exactly what elements have to | 152 /// A lattice to compactly represent multiple subsets of imports. |
151 // be loaded. | 153 final ImportSetLattice importSets = new ImportSetLattice(); |
152 Map<_DeferredImport, Set<Element>> _importedDeferredBy = null; | |
153 Map<_DeferredImport, Set<ConstantValue>> _constantsDeferredBy = null; | |
154 | |
155 Set<Element> _mainElements = new Set<Element>(); | |
156 | 154 |
157 final Compiler compiler; | 155 final Compiler compiler; |
158 DeferredLoadTask(Compiler compiler) | 156 DeferredLoadTask(Compiler compiler) |
159 : compiler = compiler, | 157 : compiler = compiler, |
160 super(compiler.measurer) { | 158 super(compiler.measurer) { |
161 mainOutputUnit.imports.add(_fakeMainImport); | 159 mainOutputUnit = new OutputUnit(true, 'main', new Set<ImportElement>()); |
| 160 importSets.mainSet.unit = mainOutputUnit; |
| 161 allOutputUnits.add(mainOutputUnit); |
162 } | 162 } |
163 | 163 |
164 JavaScriptBackend get backend => compiler.backend; | 164 JavaScriptBackend get backend => compiler.backend; |
165 DiagnosticReporter get reporter => compiler.reporter; | 165 DiagnosticReporter get reporter => compiler.reporter; |
166 | 166 |
167 /// Returns the [OutputUnit] where [element] belongs. | 167 /// Returns the [OutputUnit] where [element] belongs. |
168 OutputUnit outputUnitForElement(Entity entity) { | 168 OutputUnit outputUnitForElement(Entity entity) { |
169 // TODO(johnniwinther): Support use of entities by splitting maps by | 169 // TODO(johnniwinther): Support use of entities by splitting maps by |
170 // entity kind. | 170 // entity kind. |
171 if (!isProgramSplit) return mainOutputUnit; | 171 if (!isProgramSplit) return mainOutputUnit; |
172 Element element = entity; | 172 Element element = entity; |
173 element = element.implementation; | 173 element = element.implementation; |
174 while (!_elementToOutputUnit.containsKey(element)) { | 174 while (!_elementToSet.containsKey(element)) { |
175 // TODO(21051): workaround: it looks like we output annotation constants | 175 // TODO(21051): workaround: it looks like we output annotation constants |
176 // for classes that we don't include in the output. This seems to happen | 176 // for classes that we don't include in the output. This seems to happen |
177 // when we have reflection but can see that some classes are not needed. | 177 // when we have reflection but can see that some classes are not needed. |
178 // We still add the annotation but don't run through it below (where we | 178 // We still add the annotation but don't run through it below (where we |
179 // assign every element to its output unit). | 179 // assign every element to its output unit). |
180 if (element.enclosingElement == null) { | 180 if (element.enclosingElement == null) { |
181 _elementToOutputUnit[element] = mainOutputUnit; | 181 _elementToSet[element] = importSets.mainSet; |
182 break; | 182 break; |
183 } | 183 } |
184 element = element.enclosingElement.implementation; | 184 element = element.enclosingElement.implementation; |
185 } | 185 } |
186 return _elementToOutputUnit[element]; | 186 return _elementToSet[element].unit; |
187 } | 187 } |
188 | 188 |
189 /// Returns the [OutputUnit] where [element] belongs. | 189 /// Returns the [OutputUnit] where [element] belongs. |
190 OutputUnit outputUnitForClass(ClassEntity element) { | 190 OutputUnit outputUnitForClass(ClassEntity element) { |
191 return outputUnitForElement(element); | 191 return outputUnitForElement(element); |
192 } | 192 } |
193 | 193 |
194 /// Returns the [OutputUnit] where [element] belongs. | 194 /// Returns the [OutputUnit] where [element] belongs. |
195 OutputUnit outputUnitForMember(MemberEntity element) { | 195 OutputUnit outputUnitForMember(MemberEntity element) { |
196 return outputUnitForElement(element); | 196 return outputUnitForElement(element); |
197 } | 197 } |
198 | 198 |
199 /// Direct access to the output-unit to element relation used for testing. | 199 /// Direct access to the output-unit to element relation used for testing. |
200 OutputUnit getOutputUnitForElementForTesting(Element element) { | 200 OutputUnit getOutputUnitForElementForTesting(Entity element) { |
201 return _elementToOutputUnit[element]; | 201 return _elementToSet[element]?.unit; |
202 } | 202 } |
203 | 203 |
204 /// Returns the [OutputUnit] where [constant] belongs. | 204 /// Returns the [OutputUnit] where [constant] belongs. |
205 OutputUnit outputUnitForConstant(ConstantValue constant) { | 205 OutputUnit outputUnitForConstant(ConstantValue constant) { |
206 if (!isProgramSplit) return mainOutputUnit; | 206 if (!isProgramSplit) return mainOutputUnit; |
207 return _constantToOutputUnit[constant]; | 207 return _constantToSet[constant]?.unit; |
208 } | 208 } |
209 | 209 |
210 /// Direct access to the output-unit to constants map used for testing. | 210 /// Direct access to the output-unit to constants map used for testing. |
211 Map<ConstantValue, OutputUnit> get outputUnitForConstantsForTesting { | 211 Iterable<ConstantValue> get constantsForTesting => _constantToSet.keys; |
212 return _constantToOutputUnit; | |
213 } | |
214 | 212 |
215 bool isDeferred(Entity element) { | 213 bool isDeferred(Entity element) { |
216 return outputUnitForElement(element) != mainOutputUnit; | 214 return outputUnitForElement(element) != mainOutputUnit; |
217 } | 215 } |
218 | 216 |
219 bool isDeferredClass(ClassEntity element) { | 217 bool isDeferredClass(ClassEntity element) { |
220 return outputUnitForElement(element) != mainOutputUnit; | 218 return outputUnitForElement(element) != mainOutputUnit; |
221 } | 219 } |
222 | 220 |
223 /// Returns the unique name for the deferred import of [prefix]. | 221 /// Returns the unique name for the deferred import of [prefix]. |
224 String getImportDeferName(Spannable node, PrefixElement prefix) { | 222 String getImportDeferName(Spannable node, PrefixElement prefix) { |
225 String name = | 223 String name = _importDeferName[prefix.deferredImport]; |
226 importDeferName[new _DeclaredDeferredImport(prefix.deferredImport)]; | |
227 if (name == null) { | 224 if (name == null) { |
228 reporter.internalError(node, "No deferred name for $prefix."); | 225 reporter.internalError(node, "No deferred name for $prefix."); |
229 } | 226 } |
230 return name; | 227 return name; |
231 } | 228 } |
232 | 229 |
| 230 /// Returns the names associated with each deferred import in [unit]. |
| 231 Iterable<String> getImportNames(OutputUnit unit) { |
| 232 return unit._imports.map((i) => _importDeferName[i]); |
| 233 } |
| 234 |
233 /// Returns `true` if element [to] is reachable from element [from] without | 235 /// Returns `true` if element [to] is reachable from element [from] without |
234 /// crossing a deferred import. | 236 /// crossing a deferred import. |
235 /// | 237 /// |
236 /// For example, if we have two deferred libraries `A` and `B` that both | 238 /// For example, if we have two deferred libraries `A` and `B` that both |
237 /// import a library `C`, then even though elements from `A` and `C` end up in | 239 /// import a library `C`, then even though elements from `A` and `C` end up in |
238 /// different output units, there is a non-deferred path between `A` and `C`. | 240 /// different output units, there is a non-deferred path between `A` and `C`. |
239 bool hasOnlyNonDeferredImportPaths(Element from, Element to) { | 241 bool hasOnlyNonDeferredImportPaths(Entity from, Entity to) { |
240 OutputUnit outputUnitFrom = outputUnitForElement(from); | 242 OutputUnit outputUnitFrom = outputUnitForElement(from); |
241 OutputUnit outputUnitTo = outputUnitForElement(to); | 243 OutputUnit outputUnitTo = outputUnitForElement(to); |
242 return outputUnitTo.imports.containsAll(outputUnitFrom.imports); | 244 if (outputUnitTo == mainOutputUnit) return true; |
243 } | 245 if (outputUnitFrom == mainOutputUnit) return false; |
244 | 246 return outputUnitTo._imports.containsAll(outputUnitFrom._imports); |
245 // TODO(het): use a union-find to canonicalize output units | |
246 OutputUnit _getCanonicalUnit(OutputUnit outputUnit) { | |
247 OutputUnit representative = allOutputUnits.lookup(outputUnit); | |
248 if (representative == null) { | |
249 representative = outputUnit; | |
250 allOutputUnits.add(representative); | |
251 } | |
252 return representative; | |
253 } | 247 } |
254 | 248 |
255 void registerConstantDeferredUse( | 249 void registerConstantDeferredUse( |
256 DeferredConstantValue constant, PrefixElement prefix) { | 250 DeferredConstantValue constant, PrefixElement prefix) { |
257 OutputUnit outputUnit = new OutputUnit(); | 251 var newSet = importSets.singleton(prefix.deferredImport); |
258 outputUnit.imports.add(new _DeclaredDeferredImport(prefix.deferredImport)); | 252 assert( |
259 | 253 _constantToSet[constant] == null || _constantToSet[constant] == newSet); |
260 // Check to see if there is already a canonical output unit registered. | 254 _constantToSet[constant] = newSet; |
261 _constantToOutputUnit[constant] = _getCanonicalUnit(outputUnit); | |
262 } | 255 } |
263 | 256 |
264 /// Given [imports] that refer to an element from a library, determine whether | 257 /// Given [imports] that refer to an element from a library, determine whether |
265 /// the element is explicitly deferred. | 258 /// the element is explicitly deferred. |
266 static bool _isExplicitlyDeferred(Iterable<ImportElement> imports) { | 259 static bool _isExplicitlyDeferred(Iterable<ImportElement> imports) { |
267 // If the element is not imported explicitly, it is implicitly imported | 260 // If the element is not imported explicitly, it is implicitly imported |
268 // not deferred. | 261 // not deferred. |
269 if (imports.isEmpty) return false; | 262 if (imports.isEmpty) return false; |
270 // An element could potentially be loaded by several imports. If all of them | 263 // An element could potentially be loaded by several imports. If all of them |
271 // is explicitly deferred, we say the element is explicitly deferred. | 264 // is explicitly deferred, we say the element is explicitly deferred. |
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
503 if (library.isPatched) { | 496 if (library.isPatched) { |
504 iterateTags(library.implementation); | 497 iterateTags(library.implementation); |
505 } | 498 } |
506 } | 499 } |
507 | 500 |
508 traverseLibrary(root); | 501 traverseLibrary(root); |
509 result.add(compiler.resolution.commonElements.coreLibrary); | 502 result.add(compiler.resolution.commonElements.coreLibrary); |
510 return result; | 503 return result; |
511 } | 504 } |
512 | 505 |
513 /// Add all dependencies of [constant] to the mapping of [import]. | 506 /// Update the import set of all constants reachable from [constant], as long |
514 void _mapConstantDependencies( | 507 /// as they had the [oldSet]. As soon as we see a constant with a different |
515 ConstantValue constant, _DeferredImport import) { | 508 /// import set, we stop and enqueue a new recursive update in [queue]. |
516 Set<ConstantValue> constants = _constantsDeferredBy.putIfAbsent( | 509 /// |
517 import, () => new Set<ConstantValue>()); | 510 /// Invariants: oldSet is either null or a subset of newSet. |
518 if (constants.contains(constant)) return; | 511 void _updateConstantRecursive(ConstantValue constant, ImportSet oldSet, |
519 constants.add(constant); | 512 ImportSet newSet, WorkQueue queue) { |
520 if (constant is ConstructedConstantValue) { | 513 if (constant == null) return; |
521 ClassElement cls = constant.type.element; | 514 var currentSet = _constantToSet[constant]; |
522 _mapDependencies(element: cls, import: import); | 515 |
| 516 // Already visited. |
| 517 if (currentSet == newSet) return; |
| 518 |
| 519 // Elements in the main output unit always remain there. |
| 520 if (currentSet == importSets.mainSet) return; |
| 521 |
| 522 if (currentSet == oldSet) { |
| 523 _constantToSet[constant] = newSet; |
| 524 if (constant is ConstructedConstantValue) { |
| 525 ClassElement cls = constant.type.element; |
| 526 _updateElementRecursive(cls, oldSet, newSet, queue); |
| 527 } |
| 528 constant.getDependencies().forEach((ConstantValue dependency) { |
| 529 if (dependency is DeferredConstantValue) { |
| 530 /// New deferred-imports are only discovered when we are visiting the |
| 531 /// main output unit (size == 0) or code reachable from a deferred |
| 532 /// import (size == 1). After that, we are rediscovering the |
| 533 /// same nodes we have already seen. |
| 534 if (newSet.length <= 1) { |
| 535 PrefixElement prefix = dependency.prefix; |
| 536 queue.addConstant( |
| 537 dependency, importSets.singleton(prefix.deferredImport)); |
| 538 } |
| 539 } else { |
| 540 _updateConstantRecursive(dependency, oldSet, newSet, queue); |
| 541 } |
| 542 }); |
| 543 } else { |
| 544 assert( |
| 545 // Invariant: we must mark main before we mark any deferred import. |
| 546 newSet != importSets.mainSet || oldSet != null, |
| 547 failedAt( |
| 548 NO_LOCATION_SPANNABLE, |
| 549 "Tried to assign ${constant.toDartText()} to the main output " |
| 550 "unit, but it was assigned to $currentSet.")); |
| 551 queue.addConstant(constant, newSet); |
523 } | 552 } |
524 constant.getDependencies().forEach((ConstantValue dependency) { | |
525 _mapConstantDependencies(dependency, import); | |
526 }); | |
527 } | 553 } |
528 | 554 |
529 /// Recursively traverses the graph of dependencies from [element], mapping | 555 /// Update the import set of all elements reachable from [element], as long as |
530 /// deferred imports to each dependency it needs in the sets | 556 /// they had the [oldSet]. As soon as we see an element with a different |
531 /// [_importedDeferredBy] and [_constantsDeferredBy]. | 557 /// import set, we stop and enqueue a new recursive update in [queue]. |
532 void _mapDependencies( | 558 void _updateElementRecursive( |
533 {Element element, _DeferredImport import, isMirrorUsage: false}) { | 559 Element element, ImportSet oldSet, ImportSet newSet, WorkQueue queue, |
534 Set<Element> elements = _importedDeferredBy[import] ??= new Set<Element>(); | 560 {bool isMirrorUsage: false}) { |
| 561 if (element == null) return; |
| 562 var currentSet = _elementToSet[element]; |
535 | 563 |
536 Set<Element> dependentElements = new Set<Element>(); | 564 // Already visited. We may visit some root nodes a second time with |
537 Set<ConstantValue> dependentConstants = new Set<ConstantValue>(); | 565 // [isMirrorUsage] in order to mark static members used reflectively. |
| 566 if (currentSet == newSet && !isMirrorUsage) return; |
538 | 567 |
539 LibraryElement library; | 568 // Elements in the main output unit always remain there. |
| 569 if (currentSet == importSets.mainSet) return; |
540 | 570 |
541 if (element != null) { | 571 if (currentSet == oldSet) { |
542 // Only process elements once, unless we are doing dependencies due to | 572 // Continue recursively updating from [oldSet] to [newSet]. |
543 // mirrors, which are added in additional traversals. | 573 _elementToSet[element] = newSet; |
544 if (!isMirrorUsage && elements.contains(element)) return; | |
545 // Anything used directly by main will be loaded from the start | |
546 // We do not need to traverse it again. | |
547 if (import != _fakeMainImport && _mainElements.contains(element)) return; | |
548 // This adds [element] to [_mainElements] because [_mainElements] is | |
549 // aliased with `_importedDeferredBy[_fakeMainImport]]`. | |
550 elements.add(element); | |
551 | 574 |
552 // This call can modify [dependentElements] and [dependentConstants]. | 575 Set<Element> dependentElements = new Set<Element>(); |
| 576 Set<ConstantValue> dependentConstants = new Set<ConstantValue>(); |
553 _collectAllElementsAndConstantsResolvedFrom( | 577 _collectAllElementsAndConstantsResolvedFrom( |
554 element, dependentElements, dependentConstants, isMirrorUsage); | 578 element, dependentElements, dependentConstants, isMirrorUsage); |
555 | 579 |
556 library = element.library; | 580 LibraryElement library = element.library; |
557 } | 581 for (Element dependency in dependentElements) { |
| 582 Iterable<ImportElement> imports = _getImports(dependency, library); |
| 583 if (_isExplicitlyDeferred(imports)) { |
| 584 /// New deferred-imports are only discovered when we are visiting the |
| 585 /// main output unit (size == 0) or code reachable from a deferred |
| 586 /// import (size == 1). After that, we are rediscovering the |
| 587 /// same nodes we have already seen. |
| 588 if (newSet.length <= 1) { |
| 589 for (ImportElement deferredImport in imports) { |
| 590 queue.addElement( |
| 591 dependency, importSets.singleton(deferredImport)); |
| 592 } |
| 593 } |
| 594 } else { |
| 595 _updateElementRecursive(dependency, oldSet, newSet, queue); |
| 596 } |
| 597 } |
558 | 598 |
559 for (Element dependency in dependentElements) { | 599 for (ConstantValue dependency in dependentConstants) { |
560 Iterable<ImportElement> imports = _getImports(dependency, library); | 600 if (dependency is DeferredConstantValue) { |
561 if (_isExplicitlyDeferred(imports)) { | 601 if (newSet.length <= 1) { |
562 for (ImportElement deferredImport in imports) { | 602 PrefixElement prefix = dependency.prefix; |
563 _mapDependencies( | 603 queue.addConstant( |
564 element: dependency, | 604 dependency, importSets.singleton(prefix.deferredImport)); |
565 import: new _DeclaredDeferredImport(deferredImport)); | 605 } |
| 606 } else { |
| 607 _updateConstantRecursive(dependency, oldSet, newSet, queue); |
566 } | 608 } |
567 } else { | |
568 _mapDependencies(element: dependency, import: import); | |
569 } | 609 } |
570 } | 610 } else { |
571 | 611 queue.addElement(element, newSet); |
572 for (ConstantValue dependency in dependentConstants) { | |
573 if (dependency is DeferredConstantValue) { | |
574 PrefixElement prefix = dependency.prefix; | |
575 _mapConstantDependencies( | |
576 dependency, new _DeclaredDeferredImport(prefix.deferredImport)); | |
577 } else { | |
578 _mapConstantDependencies(dependency, import); | |
579 } | |
580 } | 612 } |
581 } | 613 } |
582 | 614 |
583 /// Adds extra dependencies coming from mirror usage. | 615 /// Adds extra dependencies coming from mirror usage. |
584 /// | 616 void _addDeferredMirrorElements(WorkQueue queue) { |
585 /// The elements are added with [_mapDependencies]. | 617 for (ImportElement deferredImport in _allDeferredImports) { |
586 void _addMirrorElements() { | 618 _addMirrorElementsForLibrary(queue, deferredImport.importedLibrary, |
587 void mapDependenciesIfResolved( | 619 importSets.singleton(deferredImport)); |
588 Element element, _DeferredImport deferredImport) { | 620 } |
| 621 } |
| 622 |
| 623 void _addMirrorElementsForLibrary( |
| 624 WorkQueue queue, LibraryElement root, ImportSet newSet) { |
| 625 void handleElementIfResolved(Element element) { |
589 // If an element is the target of a MirrorsUsed annotation but never used | 626 // If an element is the target of a MirrorsUsed annotation but never used |
590 // It will not be resolved, and we should not call isNeededForReflection. | 627 // It will not be resolved, and we should not call isNeededForReflection. |
591 // TODO(sigurdm): Unresolved elements should just answer false when | 628 // TODO(sigurdm): Unresolved elements should just answer false when |
592 // asked isNeededForReflection. Instead an internal error is triggered. | 629 // asked isNeededForReflection. Instead an internal error is triggered. |
593 // So we have to filter them out here. | 630 // So we have to filter them out here. |
594 if (element is AnalyzableElementX && !element.hasTreeElements) return; | 631 if (element is AnalyzableElementX && !element.hasTreeElements) return; |
595 | 632 |
596 bool isAccessibleByReflection(Element element) { | 633 bool isAccessibleByReflection(Element element) { |
597 if (element.isLibrary) { | 634 if (element.isLibrary) { |
598 return false; | 635 return false; |
599 } else if (element.isClass) { | 636 } else if (element.isClass) { |
600 ClassElement cls = element; | 637 ClassElement cls = element; |
601 return compiler.backend.mirrorsData | 638 return compiler.backend.mirrorsData |
602 .isClassAccessibleByReflection(cls); | 639 .isClassAccessibleByReflection(cls); |
603 } else if (element.isTypedef) { | 640 } else if (element.isTypedef) { |
604 TypedefElement typedef = element; | 641 TypedefElement typedef = element; |
605 return compiler.backend.mirrorsData | 642 return compiler.backend.mirrorsData |
606 .isTypedefAccessibleByReflection(typedef); | 643 .isTypedefAccessibleByReflection(typedef); |
607 } else { | 644 } else { |
608 MemberElement member = element; | 645 MemberElement member = element; |
609 return compiler.backend.mirrorsData | 646 return compiler.backend.mirrorsData |
610 .isMemberAccessibleByReflection(member); | 647 .isMemberAccessibleByReflection(member); |
611 } | 648 } |
612 } | 649 } |
613 | 650 |
614 if (isAccessibleByReflection(element)) { | 651 if (isAccessibleByReflection(element)) { |
615 _mapDependencies( | 652 queue.addElement(element, newSet, isMirrorUsage: true); |
616 element: element, import: deferredImport, isMirrorUsage: true); | |
617 } | 653 } |
618 } | 654 } |
619 | 655 |
620 // For each deferred import we analyze all elements reachable from the | 656 // For each deferred import we analyze all elements reachable from the |
621 // imported library through non-deferred imports. | 657 // imported library through non-deferred imports. |
622 void handleLibrary(LibraryElement library, _DeferredImport deferredImport) { | 658 void handleLibrary(LibraryElement library) { |
623 library.implementation.forEachLocalMember((Element element) { | 659 library.implementation.forEachLocalMember((Element element) { |
624 mapDependenciesIfResolved(element, deferredImport); | 660 handleElementIfResolved(element); |
625 }); | 661 }); |
626 | 662 |
627 void processMetadata(Element element) { | 663 void processMetadata(Element element) { |
628 for (MetadataAnnotation metadata in element.metadata) { | 664 for (MetadataAnnotation metadata in element.metadata) { |
629 ConstantValue constant = | 665 ConstantValue constant = |
630 backend.constants.getConstantValueForMetadata(metadata); | 666 backend.constants.getConstantValueForMetadata(metadata); |
631 if (constant != null) { | 667 if (constant != null) { |
632 _mapConstantDependencies(constant, deferredImport); | 668 queue.addConstant(constant, newSet); |
633 } | 669 } |
634 } | 670 } |
635 } | 671 } |
636 | 672 |
637 processMetadata(library); | 673 processMetadata(library); |
638 library.imports.forEach(processMetadata); | 674 library.imports.forEach(processMetadata); |
639 library.exports.forEach(processMetadata); | 675 library.exports.forEach(processMetadata); |
640 } | 676 } |
641 | 677 |
642 for (_DeferredImport deferredImport in _allDeferredImports.keys) { | 678 _nonDeferredReachableLibraries(root).forEach(handleLibrary); |
643 LibraryElement deferredLibrary = _allDeferredImports[deferredImport]; | |
644 for (LibraryElement library | |
645 in _nonDeferredReachableLibraries(deferredLibrary)) { | |
646 handleLibrary(library, deferredImport); | |
647 } | |
648 } | |
649 } | 679 } |
650 | 680 |
651 /// Computes a unique string for the name field for each outputUnit. | 681 /// Computes a unique string for the name field for each outputUnit. |
652 /// | 682 void _createOutputUnits() { |
653 /// Also sets up the [hunksToLoad] mapping. | 683 int counter = 1; |
654 void _assignNamesToOutputUnits(Set<OutputUnit> allOutputUnits) { | 684 void addUnit(ImportSet importSet) { |
| 685 if (importSet.unit != null) return; |
| 686 var unit = new OutputUnit(false, '$counter', |
| 687 importSet._imports.map((i) => i.declaration).toSet()); |
| 688 counter++; |
| 689 importSet.unit = unit; |
| 690 allOutputUnits.add(unit); |
| 691 } |
| 692 |
| 693 // Generate an output unit for all import sets that are associated with an |
| 694 // element or constant. |
| 695 _elementToSet.values.forEach(addUnit); |
| 696 _constantToSet.values.forEach(addUnit); |
| 697 |
| 698 // Sort output units to make the output of the compiler more stable. |
| 699 allOutputUnits.sort(); |
| 700 } |
| 701 |
| 702 void _setupHunksToLoad() { |
655 Set<String> usedImportNames = new Set<String>(); | 703 Set<String> usedImportNames = new Set<String>(); |
656 | 704 |
657 void computeImportDeferName(_DeferredImport import) { | 705 void computeImportDeferName(ImportElement import) { |
658 String result = import.computeImportDeferName(compiler); | 706 String result = _computeImportDeferName(import, compiler); |
659 assert(result != null); | 707 assert(result != null); |
660 importDeferName[import] = makeUnique(result, usedImportNames); | 708 _importDeferName[import] = makeUnique(result, usedImportNames); |
661 } | 709 } |
662 | 710 |
663 int counter = 1; | 711 for (ImportElement import in _allDeferredImports) { |
664 | |
665 for (_DeferredImport import in _allDeferredImports.keys) { | |
666 computeImportDeferName(import); | 712 computeImportDeferName(import); |
667 } | 713 } |
668 | 714 |
669 for (OutputUnit outputUnit in allOutputUnits) { | |
670 if (outputUnit == mainOutputUnit) { | |
671 outputUnit.name = "main"; | |
672 } else { | |
673 outputUnit.name = "$counter"; | |
674 ++counter; | |
675 } | |
676 } | |
677 | |
678 List sortedOutputUnits = new List.from(allOutputUnits); | |
679 // Sort the output units in descending order of the number of imports they | 715 // Sort the output units in descending order of the number of imports they |
680 // include. | 716 // include. |
681 | 717 |
682 // The loading of the output units must be ordered because a superclass | 718 // The loading of the output units must be ordered because a superclass |
683 // needs to be initialized before its subclass. | 719 // needs to be initialized before its subclass. |
684 // But a class can only depend on another class in an output unit shared by | 720 // But a class can only depend on another class in an output unit shared by |
685 // a strict superset of the imports: | 721 // a strict superset of the imports: |
686 // By contradiction: Assume a class C in output unit shared by imports in | 722 // By contradiction: Assume a class C in output unit shared by imports in |
687 // the set S1 = (lib1,.., lib_n) depends on a class D in an output unit | 723 // the set S1 = (lib1,.., lib_n) depends on a class D in an output unit |
688 // shared by S2 such that S2 not a superset of S1. Let lib_s be a library in | 724 // shared by S2 such that S2 not a superset of S1. Let lib_s be a library in |
689 // S1 not in S2. lib_s must depend on C, and then in turn on D. Therefore D | 725 // S1 not in S2. lib_s must depend on C, and then in turn on D. Therefore D |
690 // is not in the right output unit. | 726 // is not in the right output unit. |
691 sortedOutputUnits.sort((a, b) => b.imports.length - a.imports.length); | 727 List sortedOutputUnits = allOutputUnits.reversed.toList(); |
692 | 728 |
693 // For each deferred import we find out which outputUnits to load. | 729 // For each deferred import we find out which outputUnits to load. |
694 for (_DeferredImport import in _allDeferredImports.keys) { | 730 for (ImportElement import in _allDeferredImports) { |
695 if (import == _fakeMainImport) continue; | 731 // We expect to find an entry for any call to `loadLibrary`, even if |
696 hunksToLoad[importDeferName[import]] = new List<OutputUnit>(); | 732 // there is no code to load. In that case, the entry will be an empty |
| 733 // list. |
| 734 hunksToLoad[_importDeferName[import]] = new List<OutputUnit>(); |
697 for (OutputUnit outputUnit in sortedOutputUnits) { | 735 for (OutputUnit outputUnit in sortedOutputUnits) { |
698 if (outputUnit == mainOutputUnit) continue; | 736 if (outputUnit == mainOutputUnit) continue; |
699 if (outputUnit.imports.contains(import)) { | 737 if (outputUnit._imports.contains(import)) { |
700 hunksToLoad[importDeferName[import]].add(outputUnit); | 738 hunksToLoad[_importDeferName[import]].add(outputUnit); |
701 } | 739 } |
702 } | 740 } |
703 } | 741 } |
704 } | 742 } |
705 | 743 |
| 744 /// Performs the deferred loading algorithm. |
| 745 /// |
| 746 /// The deferred loading algorithm maps elements and constants to an output |
| 747 /// unit. Each output unit is identified by a subset of deferred imports (an |
| 748 /// [ImportSet]), and they will contain the elements that are inheretly used |
| 749 /// by all those deferred imports. An element is used by a deferred import if |
| 750 /// it is either loaded by that import or transitively accessed by an element |
| 751 /// that the import loads. An empty set represents the main output unit, |
| 752 /// which contains any elements that are accessed directly and are not |
| 753 /// deferred. |
| 754 /// |
| 755 /// The algorithm traverses the element model recursively looking for |
| 756 /// dependencies between elements. These dependencies may be deferred or |
| 757 /// non-deferred. Deferred dependencies are mainly used to discover the root |
| 758 /// elements that are loaded from deferred imports, while non-deferred |
| 759 /// dependencies are used to recursively associate more elements to output |
| 760 /// units. |
| 761 /// |
| 762 /// Naively, the algorithm traverses each root of a deferred import and marks |
| 763 /// everything it can reach as being used by that import. To reduce how many |
| 764 /// times we visit an element, we use an algorithm that works in segments: it |
| 765 /// marks elements with a subset of deferred imports at a time, until it |
| 766 /// detects a merge point where more deferred imports could be considered at |
| 767 /// once. |
| 768 /// |
| 769 /// For example, consider this dependency graph (ignoring elements in the main |
| 770 /// output unit): |
| 771 /// |
| 772 /// deferred import A: a1 ---> s1 ---> s2 -> s3 |
| 773 /// ^ ^ |
| 774 /// | | |
| 775 /// deferred import B: b1 -----+ | |
| 776 /// | |
| 777 /// deferred import C: c1 ---> c2 ---> c3 |
| 778 /// |
| 779 /// The algorithm will compute a result with 5 deferred output units: |
| 780 // |
| 781 /// * unit {A}: contains a1 |
| 782 /// * unit {B}: contains b1 |
| 783 /// * unit {C}: contains c1, c2, and c3 |
| 784 /// * unit {A, B}: contains s1 |
| 785 /// * unit {A, B, C}: contains s2, and s3 |
| 786 /// |
| 787 /// After marking everything reachable from main as part of the main output |
| 788 /// unit, our algorithm will work as follows: |
| 789 /// |
| 790 /// * Initially all deferred elements have no mapping. |
| 791 /// * We make note of work to do, initially to mark the root of each |
| 792 /// deferred import: |
| 793 /// * a1 with A, and recurse from there. |
| 794 /// * b1 with B, and recurse from there. |
| 795 /// * c1 with C, and recurse from there. |
| 796 /// * we update a1, s1, s2, s3 from no mapping to {A} |
| 797 /// * we update b1 from no mapping to {B}, and when we find s1 we notice |
| 798 /// that s1 is already associated with another import set {A}, so we make |
| 799 /// note of additional work for later to mark s1 with {A, B} |
| 800 /// * we update c1, c2, c3 to {C}, and make a note to update s2 with {A, C} |
| 801 /// * we update s1 to {A, B}, and update the existing note to update s2, now |
| 802 /// with {A, B, C} |
| 803 /// * finally we update s2 and s3 with {A, B, C} in one go, without ever |
| 804 /// updating them to the intermediate state {A, C}. |
| 805 /// |
| 806 /// The implementation below does atomic updates from one import-set to |
| 807 /// another. At first we add one deferred import at a time, but as the |
| 808 /// algorithm progesses it may update a small import-set with a larger |
| 809 /// import-set in one go. The key of this algorithm is to detect when sharing |
| 810 /// begins, so we can update those elements more efficently. |
| 811 /// |
| 812 /// To detect these merge points where sharing begins, the implementation |
| 813 /// below uses `a swap operation`: we first compare what the old import-set |
| 814 /// is, and if it matches our expectation, the swap is done and we recurse, |
| 815 /// otherwise a merge root was detected and we enqueue a new segment of |
| 816 /// updates for later. |
| 817 /// |
| 818 /// TODO(sigmund): investigate different heuristics for how to select the next |
| 819 /// work item (e.g. we might converge faster if we pick first the update that |
| 820 /// contains a bigger delta.) |
706 void onResolutionComplete(FunctionEntity main, ClosedWorld closedWorld) { | 821 void onResolutionComplete(FunctionEntity main, ClosedWorld closedWorld) { |
707 if (!isProgramSplit) { | 822 if (!isProgramSplit) return; |
708 allOutputUnits.add(mainOutputUnit); | |
709 return; | |
710 } | |
711 if (main == null) return; | 823 if (main == null) return; |
712 MethodElement mainMethod = main; | |
713 LibraryElement mainLibrary = mainMethod.library; | |
714 _importedDeferredBy = new Map<_DeferredImport, Set<Element>>(); | |
715 _constantsDeferredBy = new Map<_DeferredImport, Set<ConstantValue>>(); | |
716 _importedDeferredBy[_fakeMainImport] = _mainElements; | |
717 | 824 |
718 work() { | 825 work() { |
719 // Starting from main, traverse the program and find all dependencies. | 826 var queue = new WorkQueue(this.importSets); |
720 _mapDependencies(element: mainMethod, import: _fakeMainImport); | |
721 | 827 |
722 // Also add "global" dependencies to the main OutputUnit. These are | 828 // Add `main` and their recursive dependencies to the main output unit. |
| 829 // We do this upfront to avoid wasting time visiting these elements when |
| 830 // analyzing deferred imports. |
| 831 queue.addElement(main, importSets.mainSet); |
| 832 |
| 833 // Also add "global" dependencies to the main output unit. These are |
723 // things that the backend needs but cannot associate with a particular | 834 // things that the backend needs but cannot associate with a particular |
724 // element, for example, startRootIsolate. This set also contains | 835 // element, for example, startRootIsolate. This set also contains |
725 // elements for which we lack precise information. | 836 // elements for which we lack precise information. |
726 for (MethodElement element | 837 for (MethodElement element |
727 in closedWorld.backendUsage.globalFunctionDependencies) { | 838 in closedWorld.backendUsage.globalFunctionDependencies) { |
728 _mapDependencies( | 839 queue.addElement(element.implementation, importSets.mainSet); |
729 element: element.implementation, import: _fakeMainImport); | |
730 } | 840 } |
731 for (ClassElement element | 841 for (ClassElement element |
732 in closedWorld.backendUsage.globalClassDependencies) { | 842 in closedWorld.backendUsage.globalClassDependencies) { |
733 _mapDependencies( | 843 queue.addElement(element.implementation, importSets.mainSet); |
734 element: element.implementation, import: _fakeMainImport); | 844 } |
| 845 if (closedWorld.backendUsage.isMirrorsUsed) { |
| 846 _addMirrorElementsForLibrary(queue, main.library, importSets.mainSet); |
735 } | 847 } |
736 | 848 |
737 // Now check to see if we have to add more elements due to mirrors. | 849 void emptyQueue() { |
738 if (closedWorld.backendUsage.isMirrorsUsed) { | 850 while (queue.isNotEmpty) { |
739 _addMirrorElements(); | 851 var item = queue.nextItem(); |
740 } | 852 if (item.element != null) { |
741 | 853 var oldSet = _elementToSet[item.element]; |
742 // Build the OutputUnits using these two maps. | 854 var newSet = importSets.union(oldSet, item.newSet); |
743 Map<Element, OutputUnit> elementToOutputUnitBuilder = | 855 _updateElementRecursive(item.element, oldSet, newSet, queue, |
744 new Map<Element, OutputUnit>(); | 856 isMirrorUsage: item.isMirrorUsage); |
745 Map<ConstantValue, OutputUnit> constantToOutputUnitBuilder = | 857 } else if (item.value != null) { |
746 new Map<ConstantValue, OutputUnit>(); | 858 var oldSet = _constantToSet[item.value]; |
747 | 859 var newSet = importSets.union(oldSet, item.newSet); |
748 // Add all constants that may have been registered during resolution with | 860 _updateConstantRecursive(item.value, oldSet, newSet, queue); |
749 // [registerConstantDeferredUse]. | |
750 constantToOutputUnitBuilder.addAll(_constantToOutputUnit); | |
751 _constantToOutputUnit.clear(); | |
752 | |
753 // Reverse the mappings. For each element record an OutputUnit collecting | |
754 // all deferred imports mapped to this element. Same for constants. | |
755 for (_DeferredImport import in _importedDeferredBy.keys) { | |
756 for (Element element in _importedDeferredBy[import]) { | |
757 // Only one file should be loaded when the program starts, so make | |
758 // sure that only one OutputUnit is created for [fakeMainImport]. | |
759 if (import == _fakeMainImport) { | |
760 elementToOutputUnitBuilder[element] = mainOutputUnit; | |
761 } else { | |
762 elementToOutputUnitBuilder | |
763 .putIfAbsent(element, () => new OutputUnit()) | |
764 .imports | |
765 .add(import); | |
766 } | |
767 } | |
768 } | |
769 for (_DeferredImport import in _constantsDeferredBy.keys) { | |
770 for (ConstantValue constant in _constantsDeferredBy[import]) { | |
771 // Only one file should be loaded when the program starts, so make | |
772 // sure that only one OutputUnit is created for [fakeMainImport]. | |
773 if (import == _fakeMainImport) { | |
774 constantToOutputUnitBuilder[constant] = mainOutputUnit; | |
775 } else { | |
776 constantToOutputUnitBuilder | |
777 .putIfAbsent(constant, () => new OutputUnit()) | |
778 .imports | |
779 .add(import); | |
780 } | 861 } |
781 } | 862 } |
782 } | 863 } |
783 | 864 |
784 // Release maps; | 865 emptyQueue(); |
785 _importedDeferredBy = null; | 866 if (closedWorld.backendUsage.isMirrorsUsed) { |
786 _constantsDeferredBy = null; | 867 _addDeferredMirrorElements(queue); |
| 868 emptyQueue(); |
| 869 } |
787 | 870 |
788 // Find all the output units elements/constants have been mapped | 871 _createOutputUnits(); |
789 // to, and canonicalize them. | 872 _setupHunksToLoad(); |
790 elementToOutputUnitBuilder | |
791 .forEach((Element element, OutputUnit outputUnit) { | |
792 _elementToOutputUnit[element] = _getCanonicalUnit(outputUnit); | |
793 }); | |
794 constantToOutputUnitBuilder | |
795 .forEach((ConstantValue constant, OutputUnit outputUnit) { | |
796 _constantToOutputUnit[constant] = _getCanonicalUnit(outputUnit); | |
797 }); | |
798 | |
799 // Generate a unique name for each OutputUnit. | |
800 _assignNamesToOutputUnits(allOutputUnits); | |
801 } | 873 } |
802 | 874 |
803 reporter.withCurrentElement(mainLibrary, () => measure(work)); | 875 reporter.withCurrentElement(main.library, () => measure(work)); |
804 | 876 |
805 // Notify the impact strategy impacts are no longer needed for deferred | 877 // Notify that we no longer need impacts for deferred load, so they can be |
806 // load. | 878 // discarded at this time. |
807 compiler.impactStrategy.onImpactUsed(IMPACT_USE); | 879 compiler.impactStrategy.onImpactUsed(IMPACT_USE); |
808 } | 880 } |
809 | 881 |
810 void beforeResolution(LibraryEntity mainLibrary) { | 882 void beforeResolution(LibraryEntity mainLibrary) { |
811 if (mainLibrary == null) return; | 883 if (mainLibrary == null) return; |
812 // TODO(johnniwinther): Support deferred load for kernel based elements. | 884 // TODO(johnniwinther): Support deferred load for kernel based elements. |
813 if (compiler.options.useKernel) return; | 885 if (compiler.options.useKernel) return; |
814 _allDeferredImports[_fakeMainImport] = mainLibrary; | |
815 var lastDeferred; | 886 var lastDeferred; |
816 // When detecting duplicate prefixes of deferred libraries there are 4 | 887 // When detecting duplicate prefixes of deferred libraries there are 4 |
817 // cases of duplicate prefixes: | 888 // cases of duplicate prefixes: |
818 // 1. | 889 // 1. |
819 // import "lib.dart" deferred as a; | 890 // import "lib.dart" deferred as a; |
820 // import "lib2.dart" deferred as a; | 891 // import "lib2.dart" deferred as a; |
821 // 2. | 892 // 2. |
822 // import "lib.dart" deferred as a; | 893 // import "lib.dart" deferred as a; |
823 // import "lib2.dart" as a; | 894 // import "lib2.dart" as a; |
824 // 3. | 895 // 3. |
(...skipping 30 matching lines...) Expand all Loading... |
855 reporter.reportErrorMessage( | 926 reporter.reportErrorMessage( |
856 import, MessageKind.DEFERRED_OLD_SYNTAX); | 927 import, MessageKind.DEFERRED_OLD_SYNTAX); |
857 } | 928 } |
858 } | 929 } |
859 } | 930 } |
860 | 931 |
861 String prefix = (import.prefix != null) ? import.prefix.name : null; | 932 String prefix = (import.prefix != null) ? import.prefix.name : null; |
862 // The last import we saw with the same prefix. | 933 // The last import we saw with the same prefix. |
863 ImportElement previousDeferredImport = prefixDeferredImport[prefix]; | 934 ImportElement previousDeferredImport = prefixDeferredImport[prefix]; |
864 if (import.isDeferred) { | 935 if (import.isDeferred) { |
865 _DeferredImport key = new _DeclaredDeferredImport(import); | |
866 LibraryElement importedLibrary = import.importedLibrary; | |
867 _allDeferredImports[key] = importedLibrary; | |
868 | |
869 if (prefix == null) { | 936 if (prefix == null) { |
870 reporter.reportErrorMessage( | 937 reporter.reportErrorMessage( |
871 import, MessageKind.DEFERRED_LIBRARY_WITHOUT_PREFIX); | 938 import, MessageKind.DEFERRED_LIBRARY_WITHOUT_PREFIX); |
872 } else { | 939 } else { |
873 prefixDeferredImport[prefix] = import; | 940 prefixDeferredImport[prefix] = import; |
874 Uri mainLibraryUri = compiler.mainLibraryUri; | 941 Uri mainLibraryUri = compiler.mainLibraryUri; |
875 _deferredImportDescriptions[key] = | 942 _deferredImportDescriptions[import] = |
876 new ImportDescription(import, library, mainLibraryUri); | 943 new ImportDescription(import, library, mainLibraryUri); |
877 } | 944 } |
878 isProgramSplit = true; | 945 isProgramSplit = true; |
879 lastDeferred = import; | 946 lastDeferred = import; |
880 } | 947 } |
881 if (prefix != null) { | 948 if (prefix != null) { |
882 if (previousDeferredImport != null || | 949 if (previousDeferredImport != null || |
883 (import.isDeferred && usedPrefixes.contains(prefix))) { | 950 (import.isDeferred && usedPrefixes.contains(prefix))) { |
884 ImportElement failingImport = (previousDeferredImport != null) | 951 ImportElement failingImport = (previousDeferredImport != null) |
885 ? previousDeferredImport | 952 ? previousDeferredImport |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
964 /// - <library uri> is the relative uri of the library making a deferred | 1031 /// - <library uri> is the relative uri of the library making a deferred |
965 /// import. | 1032 /// import. |
966 /// - <library name> is the name of the library, and "<unnamed>" if it is | 1033 /// - <library name> is the name of the library, and "<unnamed>" if it is |
967 /// unnamed. | 1034 /// unnamed. |
968 /// - <prefix> is the `as` prefix used for a given deferred import. | 1035 /// - <prefix> is the `as` prefix used for a given deferred import. |
969 /// - <list of files> is a list of the filenames the must be loaded when that | 1036 /// - <list of files> is a list of the filenames the must be loaded when that |
970 /// import is loaded. | 1037 /// import is loaded. |
971 Map<String, Map<String, dynamic>> computeDeferredMap() { | 1038 Map<String, Map<String, dynamic>> computeDeferredMap() { |
972 Map<String, Map<String, dynamic>> mapping = | 1039 Map<String, Map<String, dynamic>> mapping = |
973 new Map<String, Map<String, dynamic>>(); | 1040 new Map<String, Map<String, dynamic>>(); |
974 _deferredImportDescriptions.keys.forEach((_DeferredImport import) { | 1041 _deferredImportDescriptions.keys.forEach((ImportElement import) { |
975 List<OutputUnit> outputUnits = hunksToLoad[importDeferName[import]]; | 1042 List<OutputUnit> outputUnits = hunksToLoad[_importDeferName[import]]; |
976 ImportDescription description = _deferredImportDescriptions[import]; | 1043 ImportDescription description = _deferredImportDescriptions[import]; |
977 Map<String, dynamic> libraryMap = mapping.putIfAbsent( | 1044 Map<String, dynamic> libraryMap = mapping.putIfAbsent( |
978 description.importingUri, | 1045 description.importingUri, |
979 () => <String, dynamic>{ | 1046 () => <String, dynamic>{ |
980 "name": description.importingLibraryName, | 1047 "name": description.importingLibraryName, |
981 "imports": <String, List<String>>{} | 1048 "imports": <String, List<String>>{} |
982 }); | 1049 }); |
983 | 1050 |
984 libraryMap["imports"][importDeferName[import]] = | 1051 libraryMap["imports"][_importDeferName[import]] = |
985 outputUnits.map((OutputUnit outputUnit) { | 1052 outputUnits.map((OutputUnit outputUnit) { |
986 return deferredPartFileName(outputUnit.name); | 1053 return deferredPartFileName(outputUnit.name); |
987 }).toList(); | 1054 }).toList(); |
988 }); | 1055 }); |
989 return mapping; | 1056 return mapping; |
990 } | 1057 } |
991 | 1058 |
992 /// Returns the filename for the output-unit named [name]. | 1059 /// Returns the filename for the output-unit named [name]. |
993 /// | 1060 /// |
994 /// The filename is of the form "<main output file>_<name>.part.js". | 1061 /// The filename is of the form "<main output file>_<name>.part.js". |
995 /// If [addExtension] is false, the ".part.js" suffix is left out. | 1062 /// If [addExtension] is false, the ".part.js" suffix is left out. |
996 String deferredPartFileName(String name, {bool addExtension: true}) { | 1063 String deferredPartFileName(String name, {bool addExtension: true}) { |
997 assert(name != ""); | 1064 assert(name != ""); |
998 String outPath = compiler.options.outputUri != null | 1065 String outPath = compiler.options.outputUri != null |
999 ? compiler.options.outputUri.path | 1066 ? compiler.options.outputUri.path |
1000 : "out"; | 1067 : "out"; |
1001 String outName = outPath.substring(outPath.lastIndexOf('/') + 1); | 1068 String outName = outPath.substring(outPath.lastIndexOf('/') + 1); |
1002 String extension = addExtension ? ".part.js" : ""; | 1069 String extension = addExtension ? ".part.js" : ""; |
1003 return "${outName}_$name$extension"; | 1070 return "${outName}_$name$extension"; |
1004 } | 1071 } |
1005 | 1072 |
1006 /// Creates a textual representation of the output unit content. | 1073 /// Creates a textual representation of the output unit content. |
1007 String dump() { | 1074 String dump() { |
1008 Map<OutputUnit, List<String>> elementMap = <OutputUnit, List<String>>{}; | 1075 Map<OutputUnit, List<String>> elementMap = <OutputUnit, List<String>>{}; |
1009 Map<OutputUnit, List<String>> constantMap = <OutputUnit, List<String>>{}; | 1076 Map<OutputUnit, List<String>> constantMap = <OutputUnit, List<String>>{}; |
1010 _elementToOutputUnit.forEach((Element element, OutputUnit output) { | 1077 _elementToSet.forEach((Entity element, ImportSet importSet) { |
1011 elementMap.putIfAbsent(output, () => <String>[]).add('$element'); | 1078 elementMap.putIfAbsent(importSet.unit, () => <String>[]).add('$element'); |
1012 }); | 1079 }); |
1013 _constantToOutputUnit.forEach((ConstantValue value, OutputUnit output) { | 1080 _constantToSet.forEach((ConstantValue value, ImportSet importSet) { |
1014 constantMap | 1081 constantMap |
1015 .putIfAbsent(output, () => <String>[]) | 1082 .putIfAbsent(importSet.unit, () => <String>[]) |
1016 .add(value.toStructuredText()); | 1083 .add(value.toStructuredText()); |
1017 }); | 1084 }); |
1018 | 1085 |
1019 StringBuffer sb = new StringBuffer(); | 1086 Map<OutputUnit, String> text = {}; |
1020 for (OutputUnit outputUnit in allOutputUnits) { | 1087 for (OutputUnit outputUnit in allOutputUnits) { |
1021 sb.write('\n-------------------------------\n'); | 1088 StringBuffer unitText = new StringBuffer(); |
1022 sb.write('Output unit: ${outputUnit.name}'); | 1089 if (outputUnit.isMainOutput) { |
| 1090 unitText.write(' <MAIN UNIT>'); |
| 1091 } else { |
| 1092 unitText.write(' imports:'); |
| 1093 var imports = outputUnit._imports.map((i) => '${i.uri}').toList() |
| 1094 ..sort(); |
| 1095 for (var i in imports) { |
| 1096 unitText.write('\n $i:'); |
| 1097 } |
| 1098 } |
1023 List<String> elements = elementMap[outputUnit]; | 1099 List<String> elements = elementMap[outputUnit]; |
1024 if (elements != null) { | 1100 if (elements != null) { |
1025 sb.write('\n elements:'); | 1101 unitText.write('\n elements:'); |
1026 for (String element in elements..sort()) { | 1102 for (String element in elements..sort()) { |
1027 sb.write('\n $element'); | 1103 unitText.write('\n $element'); |
1028 } | 1104 } |
1029 } | 1105 } |
1030 List<String> constants = constantMap[outputUnit]; | 1106 List<String> constants = constantMap[outputUnit]; |
1031 if (constants != null) { | 1107 if (constants != null) { |
1032 sb.write('\n constants:'); | 1108 unitText.write('\n constants:'); |
1033 for (String value in constants..sort()) { | 1109 for (String value in constants..sort()) { |
1034 sb.write('\n $value'); | 1110 unitText.write('\n $value'); |
1035 } | 1111 } |
1036 } | 1112 } |
| 1113 text[outputUnit] = '$unitText'; |
| 1114 } |
| 1115 |
| 1116 StringBuffer sb = new StringBuffer(); |
| 1117 for (OutputUnit outputUnit in allOutputUnits.toList() |
| 1118 ..sort((a, b) => text[a].compareTo(text[b]))) { |
| 1119 sb.write('\n\n-------------------------------\n'); |
| 1120 sb.write('Output unit: ${outputUnit.name}'); |
| 1121 sb.write('\n ${text[outputUnit]}'); |
1037 } | 1122 } |
1038 return sb.toString(); | 1123 return sb.toString(); |
1039 } | 1124 } |
1040 } | 1125 } |
1041 | 1126 |
1042 class ImportDescription { | 1127 class ImportDescription { |
1043 /// Relative uri to the importing library. | 1128 /// Relative uri to the importing library. |
1044 final String importingUri; | 1129 final String importingUri; |
1045 | 1130 |
1046 /// The prefix this import is imported as. | 1131 /// The prefix this import is imported as. |
1047 final String prefix; | 1132 final String prefix; |
1048 final LibraryElement _importingLibrary; | 1133 final LibraryElement _importingLibrary; |
1049 | 1134 |
1050 ImportDescription( | 1135 ImportDescription( |
1051 ImportElement import, LibraryElement importingLibrary, Uri mainLibraryUri) | 1136 ImportElement import, LibraryElement importingLibrary, Uri mainLibraryUri) |
1052 : importingUri = uri_extras.relativize( | 1137 : importingUri = uri_extras.relativize( |
1053 mainLibraryUri, importingLibrary.canonicalUri, false), | 1138 mainLibraryUri, importingLibrary.canonicalUri, false), |
1054 prefix = import.prefix.name, | 1139 prefix = import.prefix.name, |
1055 _importingLibrary = importingLibrary; | 1140 _importingLibrary = importingLibrary; |
1056 | 1141 |
1057 String get importingLibraryName { | 1142 String get importingLibraryName { |
1058 return _importingLibrary.hasLibraryName | 1143 return _importingLibrary.hasLibraryName |
1059 ? _importingLibrary.libraryName | 1144 ? _importingLibrary.libraryName |
1060 : "<unnamed>"; | 1145 : "<unnamed>"; |
1061 } | 1146 } |
1062 } | 1147 } |
1063 | 1148 |
1064 /// A node in the deferred import graph. | 1149 /// Indirectly represents a deferred import in an [ImportSet]. |
1065 /// | 1150 /// |
1066 /// This class serves as the root node; the 'import' of the main library. | 1151 /// We could directly store the [declaration] in [ImportSet], but adding this |
| 1152 /// class makes some of the import set operations more efficient. |
1067 class _DeferredImport { | 1153 class _DeferredImport { |
1068 const _DeferredImport(); | |
1069 | |
1070 /// Computes a suggestive name for this import. | |
1071 String computeImportDeferName(Compiler compiler) => 'main'; | |
1072 | |
1073 ImportElement get declaration => null; | |
1074 | |
1075 String toString() => 'main'; | |
1076 } | |
1077 | |
1078 /// A node in the deferred import graph defined by a deferred import directive. | |
1079 class _DeclaredDeferredImport implements _DeferredImport { | |
1080 final ImportElement declaration; | 1154 final ImportElement declaration; |
1081 | 1155 |
1082 _DeclaredDeferredImport(this.declaration); | 1156 /// Canonical index associated with [declaration]. This is used to efficiently |
1083 | 1157 /// implement [ImportSetLattice.union]. |
1084 @override | 1158 final int index; |
1085 String computeImportDeferName(Compiler compiler) { | 1159 |
1086 String result; | 1160 _DeferredImport(this.declaration, this.index); |
1087 if (declaration.isDeferred) { | 1161 } |
1088 if (declaration.prefix != null) { | 1162 |
1089 result = declaration.prefix.name; | 1163 /// A compact lattice representation of import sets and subsets. |
| 1164 /// |
| 1165 /// We use a graph of nodes to represent elements of the lattice, but only |
| 1166 /// create new nodes on-demand as they are needed by the deferred loading |
| 1167 /// algorithm. |
| 1168 /// |
| 1169 /// The constructions of nodes is carefully done by storing imports in a |
| 1170 /// specific order. This ensures that we have a unique and canonical |
| 1171 /// representation for each subset. |
| 1172 class ImportSetLattice { |
| 1173 /// Index of deferred imports that defines the canonical order used by the |
| 1174 /// operations below. |
| 1175 Map<ImportElement, _DeferredImport> _importIndex = {}; |
| 1176 |
| 1177 /// The canonical instance representing the empty import set. |
| 1178 ImportSet _emptySet = new ImportSet(); |
| 1179 |
| 1180 /// The import set representing the main output unit, which happens to be |
| 1181 /// implemented as an empty set in our algorithm. |
| 1182 ImportSet get mainSet => _emptySet; |
| 1183 |
| 1184 /// Get the singleton import set that only contains [import]. |
| 1185 ImportSet singleton(ImportElement import) { |
| 1186 // Ensure we have import in the index. |
| 1187 return _emptySet._add(_wrap(import)); |
| 1188 } |
| 1189 |
| 1190 /// Get the import set that includes the union of [a] and [b]. |
| 1191 ImportSet union(ImportSet a, ImportSet b) { |
| 1192 if (a == null || a == _emptySet) return b; |
| 1193 if (b == null || b == _emptySet) return a; |
| 1194 |
| 1195 // We create the union by merging the imports in canonical order first, and |
| 1196 // then getting (or creating) the canonical sets by adding an import at a |
| 1197 // time. |
| 1198 List<_DeferredImport> aImports = a._imports; |
| 1199 List<_DeferredImport> bImports = b._imports; |
| 1200 int i = 0, j = 0, lastAIndex = 0, lastBIndex = 0; |
| 1201 var result = _emptySet; |
| 1202 while (i < aImports.length && j < bImports.length) { |
| 1203 var importA = aImports[i]; |
| 1204 var importB = bImports[j]; |
| 1205 assert(lastAIndex <= importA.index); |
| 1206 assert(lastBIndex <= importB.index); |
| 1207 if (importA.index < importB.index) { |
| 1208 result = result._add(importA); |
| 1209 i++; |
1090 } else { | 1210 } else { |
1091 // This happens when the deferred import isn't declared with a prefix. | 1211 result = result._add(importB); |
1092 assert(compiler.compilationFailed); | 1212 j++; |
1093 result = ''; | |
1094 } | 1213 } |
| 1214 } |
| 1215 for (; i < aImports.length; i++) { |
| 1216 result = result._add(aImports[i]); |
| 1217 } |
| 1218 for (; j < bImports.length; j++) { |
| 1219 result = result._add(bImports[j]); |
| 1220 } |
| 1221 |
| 1222 return result; |
| 1223 } |
| 1224 |
| 1225 /// Get the index for an [import] according to the canonical order. |
| 1226 _DeferredImport _wrap(ImportElement import) { |
| 1227 return _importIndex.putIfAbsent( |
| 1228 import, () => new _DeferredImport(import, _importIndex.length)); |
| 1229 } |
| 1230 } |
| 1231 |
| 1232 /// A canonical set of deferred imports. |
| 1233 class ImportSet { |
| 1234 /// Imports that are part of this set. |
| 1235 /// |
| 1236 /// Invariant: the order in which elements are added must respect the |
| 1237 /// canonical order of all imports in [ImportSetLattice]. |
| 1238 final List<_DeferredImport> _imports; |
| 1239 |
| 1240 /// Links to other import sets in the lattice by adding one import. |
| 1241 final Map<_DeferredImport, ImportSet> _transitions = |
| 1242 <_DeferredImport, ImportSet>{}; |
| 1243 |
| 1244 ImportSet([this._imports = const <_DeferredImport>[]]); |
| 1245 |
| 1246 /// The output unit corresponding to this set of imports, if any. |
| 1247 OutputUnit unit; |
| 1248 |
| 1249 int get length => _imports.length; |
| 1250 |
| 1251 /// Create an import set that adds [import] to all the imports on this set. |
| 1252 /// This assumes that import's canonical order comes after all imports in |
| 1253 /// this current set. This should only be called from [ImportSetLattice], |
| 1254 /// since it is where we preserve this invariant. |
| 1255 ImportSet _add(_DeferredImport import) { |
| 1256 return _transitions.putIfAbsent(import, () { |
| 1257 var result = new ImportSet(new List.from(_imports)..add(import)); |
| 1258 result._transitions[import] = result; |
| 1259 return result; |
| 1260 }); |
| 1261 } |
| 1262 |
| 1263 String toString() { |
| 1264 StringBuffer sb = new StringBuffer(); |
| 1265 sb.write('ImportSet(size: $length, '); |
| 1266 for (var import in _imports) { |
| 1267 sb.write('${import.declaration.prefix} '); |
| 1268 } |
| 1269 sb.write(')'); |
| 1270 return '$sb'; |
| 1271 } |
| 1272 } |
| 1273 |
| 1274 /// The algorithm work queue. |
| 1275 class WorkQueue { |
| 1276 /// The actual queue of work that needs to be done. |
| 1277 final Queue<WorkItem> queue = new Queue<WorkItem>(); |
| 1278 |
| 1279 /// An index to find work items in the queue corresponding to an entity. |
| 1280 final Map<Entity, WorkItem> pendingElements = <Entity, WorkItem>{}; |
| 1281 |
| 1282 /// An index to find work items in the queue corresponding to a constant. |
| 1283 final Map<ConstantValue, WorkItem> pendingConstants = |
| 1284 <ConstantValue, WorkItem>{}; |
| 1285 |
| 1286 /// Lattice used to compute unions of [ImportSet]s. |
| 1287 final ImportSetLattice _importSets; |
| 1288 |
| 1289 WorkQueue(this._importSets); |
| 1290 |
| 1291 /// Whether there are no more work items in the queue. |
| 1292 bool get isNotEmpty => queue.isNotEmpty; |
| 1293 |
| 1294 /// Pop the next element in the queue. |
| 1295 WorkItem nextItem() { |
| 1296 assert(isNotEmpty); |
| 1297 var item = queue.removeFirst(); |
| 1298 if (item.element != null) pendingElements.remove(item.element); |
| 1299 if (item.value != null) pendingConstants.remove(item.value); |
| 1300 return item; |
| 1301 } |
| 1302 |
| 1303 /// Add to the queue that [element] should be updated to include all imports |
| 1304 /// in [importSet]. If there is already a work item in the queue for |
| 1305 /// [element], this makes sure that the work item now includes the union of |
| 1306 /// [importSet] and the existing work item's import set. |
| 1307 void addElement(Entity element, ImportSet importSet, {isMirrorUsage: false}) { |
| 1308 var item = pendingElements[element]; |
| 1309 if (item == null) { |
| 1310 item = new WorkItem(element, importSet); |
| 1311 pendingElements[element] = item; |
| 1312 queue.add(item); |
1095 } else { | 1313 } else { |
1096 // Finds the first argument to the [DeferredLibrary] annotation | 1314 item.newSet = _importSets.union(item.newSet, importSet); |
1097 List<MetadataAnnotation> metadatas = declaration.metadata; | 1315 } |
1098 assert(metadatas != null); | 1316 if (isMirrorUsage) item.isMirrorUsage = true; |
1099 for (MetadataAnnotation metadata in metadatas) { | 1317 } |
1100 metadata.ensureResolved(compiler.resolution); | 1318 |
1101 ConstantValue value = | 1319 /// Add to the queue that [constant] should be updated to include all imports |
1102 compiler.constants.getConstantValue(metadata.constant); | 1320 /// in [importSet]. If there is already a work item in the queue for |
1103 ResolutionDartType type = | 1321 /// [constant], this makes sure that the work item now includes the union of |
1104 value.getType(compiler.resolution.commonElements); | 1322 /// [importSet] and the existing work item's import set. |
1105 Element element = type.element; | 1323 void addConstant(ConstantValue constant, ImportSet importSet) { |
1106 if (element == | 1324 var item = pendingConstants[constant]; |
1107 compiler.resolution.commonElements.deferredLibraryClass) { | 1325 if (item == null) { |
1108 ConstructedConstantValue constant = value; | 1326 item = new WorkItem.constant(constant, importSet); |
1109 StringConstantValue s = constant.fields.values.single; | 1327 pendingConstants[constant] = item; |
1110 result = s.primitiveValue; | 1328 queue.add(item); |
1111 break; | 1329 } else { |
1112 } | 1330 item.newSet = _importSets.union(item.newSet, importSet); |
| 1331 } |
| 1332 } |
| 1333 } |
| 1334 |
| 1335 /// Summary of the work that needs to be done on an entity or constant. |
| 1336 class WorkItem { |
| 1337 /// Entity to be recursively updated. |
| 1338 final Entity element; |
| 1339 |
| 1340 /// Constant to be recursively updated. |
| 1341 final ConstantValue value; |
| 1342 |
| 1343 /// Additional imports that use [element] or [value] and need to be added by |
| 1344 /// the algorithm. |
| 1345 /// |
| 1346 /// This is non-final in case we add more deferred imports to the set before |
| 1347 /// the work item is applied (see [WorkQueue.addElement] and |
| 1348 /// [WorkQueue.addConstant]). |
| 1349 ImportSet newSet; |
| 1350 |
| 1351 /// Whether [element] is used via mirrors. |
| 1352 /// |
| 1353 /// This is non-final in case we later discover that the same [element] is |
| 1354 /// used via mirrors (but before the work item is applied). |
| 1355 bool isMirrorUsage = false; |
| 1356 |
| 1357 WorkItem(this.element, this.newSet) : value = null; |
| 1358 WorkItem.constant(this.value, this.newSet) : element = null; |
| 1359 } |
| 1360 |
| 1361 /// Returns a name for a deferred import. |
| 1362 // TODO(sigmund): delete support for the old annotation-style syntax. |
| 1363 String _computeImportDeferName(ImportElement declaration, Compiler compiler) { |
| 1364 String result; |
| 1365 if (declaration.isDeferred) { |
| 1366 if (declaration.prefix != null) { |
| 1367 result = declaration.prefix.name; |
| 1368 } else { |
| 1369 // This happens when the deferred import isn't declared with a prefix. |
| 1370 assert(compiler.compilationFailed); |
| 1371 result = ''; |
| 1372 } |
| 1373 } else { |
| 1374 // Finds the first argument to the [DeferredLibrary] annotation |
| 1375 List<MetadataAnnotation> metadatas = declaration.metadata; |
| 1376 assert(metadatas != null); |
| 1377 for (MetadataAnnotation metadata in metadatas) { |
| 1378 metadata.ensureResolved(compiler.resolution); |
| 1379 ConstantValue value = |
| 1380 compiler.constants.getConstantValue(metadata.constant); |
| 1381 ResolutionDartType type = |
| 1382 value.getType(compiler.resolution.commonElements); |
| 1383 Element element = type.element; |
| 1384 if (element == compiler.resolution.commonElements.deferredLibraryClass) { |
| 1385 ConstructedConstantValue constant = value; |
| 1386 StringConstantValue s = constant.fields.values.single; |
| 1387 result = s.primitiveValue; |
| 1388 break; |
1113 } | 1389 } |
1114 } | 1390 } |
1115 assert(result != null); | 1391 } |
1116 return result; | 1392 assert(result != null); |
1117 } | 1393 return result; |
1118 | 1394 } |
1119 bool operator ==(other) { | |
1120 if (other is! _DeclaredDeferredImport) return false; | |
1121 return declaration == other.declaration; | |
1122 } | |
1123 | |
1124 int get hashCode => declaration.hashCode * 17; | |
1125 | |
1126 String toString() => '$declaration'; | |
1127 } | |
OLD | NEW |