Chromium Code Reviews| 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 abstract class 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 bool get isMainOutput; |
| 65 | 63 |
| 66 /// A unique name representing this [OutputUnit]. | 64 /// A unique name representing this [OutputUnit]. |
| 67 String name; | 65 String get name; |
| 68 | |
| 69 OutputUnit({this.isMainOutput: false}); | |
| 70 | |
| 71 String toString() => "OutputUnit($name)"; | |
| 72 | |
| 73 bool operator ==(other) { | |
| 74 return other is OutputUnit && | |
| 75 imports.length == other.imports.length && | |
| 76 imports.containsAll(other.imports); | |
| 77 } | |
| 78 | |
| 79 int get hashCode { | |
| 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 } | 66 } |
| 87 | 67 |
| 88 /// For each deferred import, find elements and constants to be loaded when that | 68 /// 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 | 69 /// import is loaded. Elements that are used by several deferred imports are in |
| 90 /// shared OutputUnits. | 70 /// shared OutputUnits. |
| 91 class DeferredLoadTask extends CompilerTask { | 71 class DeferredLoadTask extends CompilerTask { |
| 92 /// The name of this task. | 72 /// The name of this task. |
| 93 String get name => 'Deferred Loading'; | 73 String get name => 'Deferred Loading'; |
| 94 | 74 |
| 95 /// DeferredLibrary from dart:async | 75 /// DeferredLibrary from dart:async |
| 96 ClassElement get deferredLibraryClass => | 76 ClassElement get deferredLibraryClass => |
| 97 compiler.resolution.commonElements.deferredLibraryClass; | 77 compiler.resolution.commonElements.deferredLibraryClass; |
| 98 | 78 |
| 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. | 79 /// The OutputUnit that will be loaded when the program starts. |
| 103 final OutputUnit mainOutputUnit = new OutputUnit(isMainOutput: true); | 80 OutputUnit get mainOutputUnit => importSets.emptySet; |
| 104 | 81 |
| 105 /// A set containing (eventually) all output units that will result from the | 82 /// A set containing (eventually) all output units that will result from the |
| 106 /// program. | 83 /// program. |
| 107 final Set<OutputUnit> allOutputUnits = new Set<OutputUnit>(); | 84 final Set<ImportSet> allOutputUnits = new Set<ImportSet>(); |
| 108 | 85 |
| 109 /// Will be `true` if the program contains deferred libraries. | 86 /// Will be `true` if the program contains deferred libraries. |
| 110 bool isProgramSplit = false; | 87 bool isProgramSplit = false; |
| 111 | 88 |
| 112 static const ImpactUseCase IMPACT_USE = const ImpactUseCase('Deferred load'); | 89 static const ImpactUseCase IMPACT_USE = const ImpactUseCase('Deferred load'); |
| 113 | 90 |
| 114 /// A mapping from the name of a defer import to all the output units it | 91 /// 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. | 92 /// depends on in a list of lists to be loaded in the order they appear. |
| 116 /// | 93 /// |
| 117 /// For example {"lib1": [[lib1_lib2_lib3], [lib1_lib2, lib1_lib3], | 94 /// For example {"lib1": [[lib1_lib2_lib3], [lib1_lib2, lib1_lib3], |
| 118 /// [lib1]]} would mean that in order to load "lib1" first the hunk | 95 /// [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 | 96 /// 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. | 97 /// can be loaded in parallel. And finally lib1 can be loaded. |
| 121 final Map<String, List<OutputUnit>> hunksToLoad = | 98 final Map<String, List<OutputUnit>> hunksToLoad = |
| 122 new Map<String, List<OutputUnit>>(); | 99 new Map<String, List<OutputUnit>>(); |
| 123 | 100 |
| 124 /// A cache of the result of calling `computeImportDeferName` on the keys of | 101 /// A cache of the result of calling `computeImportDeferName` on the keys of |
| 125 /// this map. | 102 /// this map. |
| 126 final Map<_DeferredImport, String> importDeferName = | 103 final Map<ImportElement, String> _importDeferName = <ImportElement, String>{}; |
| 127 <_DeferredImport, String>{}; | |
| 128 | 104 |
| 129 /// A mapping from elements and constants to their output unit. Query this via | 105 /// A mapping from elements and constants to their output unit. Query this via |
| 130 /// [outputUnitForElement] | 106 /// [outputUnitForElement] |
| 131 final Map<Element, OutputUnit> _elementToOutputUnit = | 107 final Map<Entity, ImportSet> _elementToOutputUnit = |
| 132 new Map<Element, OutputUnit>(); | 108 new Map<Entity, ImportSet>(); |
| 133 | 109 |
| 134 /// A mapping from constants to their output unit. Query this via | 110 /// A mapping from constants to their output unit. Query this via |
| 135 /// [outputUnitForConstant] | 111 /// [outputUnitForConstant] |
| 136 final Map<ConstantValue, OutputUnit> _constantToOutputUnit = | 112 final Map<ConstantValue, ImportSet> _constantToOutputUnit = |
| 137 new Map<ConstantValue, OutputUnit>(); | 113 new Map<ConstantValue, ImportSet>(); |
| 138 | |
| 139 /// All the imports with a [DeferredLibrary] annotation, mapped to the | |
| 140 /// [LibraryElement] they import. | |
| 141 /// The main library is included in this set for convenience. | |
| 142 final Map<_DeferredImport, LibraryElement> _allDeferredImports = | |
| 143 new Map<_DeferredImport, LibraryElement>(); | |
| 144 | 114 |
| 145 /// Because the token-stream is forgotten later in the program, we cache a | 115 /// Because the token-stream is forgotten later in the program, we cache a |
| 146 /// description of each deferred import. | 116 /// description of each deferred import. |
| 147 final Map<_DeferredImport, ImportDescription> _deferredImportDescriptions = | 117 final Map<ImportElement, ImportDescription> _deferredImportDescriptions = |
| 148 <_DeferredImport, ImportDescription>{}; | 118 <ImportElement, ImportDescription>{}; |
| 149 | 119 |
| 150 // For each deferred import we want to know exactly what elements have to | 120 /// A lattice to compactly represent multiple subsets of imports. |
| 151 // be loaded. | 121 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 | 122 |
| 157 final Compiler compiler; | 123 final Compiler compiler; |
| 158 DeferredLoadTask(Compiler compiler) | 124 DeferredLoadTask(Compiler compiler) |
| 159 : compiler = compiler, | 125 : compiler = compiler, |
| 160 super(compiler.measurer) { | 126 super(compiler.measurer) { |
| 161 mainOutputUnit.imports.add(_fakeMainImport); | 127 allOutputUnits.add(mainOutputUnit); |
| 162 } | 128 } |
| 163 | 129 |
| 164 JavaScriptBackend get backend => compiler.backend; | 130 JavaScriptBackend get backend => compiler.backend; |
| 165 DiagnosticReporter get reporter => compiler.reporter; | 131 DiagnosticReporter get reporter => compiler.reporter; |
| 166 | 132 |
| 167 /// Returns the [OutputUnit] where [element] belongs. | 133 /// Returns the [OutputUnit] where [element] belongs. |
| 168 OutputUnit outputUnitForElement(Entity entity) { | 134 OutputUnit outputUnitForElement(Entity entity) { |
| 169 // TODO(johnniwinther): Support use of entities by splitting maps by | 135 // TODO(johnniwinther): Support use of entities by splitting maps by |
| 170 // entity kind. | 136 // entity kind. |
| 171 if (!isProgramSplit) return mainOutputUnit; | 137 if (!isProgramSplit) return mainOutputUnit; |
| 172 Element element = entity; | 138 Element element = entity; |
| 173 element = element.implementation; | 139 element = element.implementation; |
| 174 while (!_elementToOutputUnit.containsKey(element)) { | 140 while (!_elementToOutputUnit.containsKey(element)) { |
| 175 // TODO(21051): workaround: it looks like we output annotation constants | 141 // 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 | 142 // 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. | 143 // 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 | 144 // We still add the annotation but don't run through it below (where we |
| 179 // assign every element to its output unit). | 145 // assign every element to its output unit). |
| 180 if (element.enclosingElement == null) { | 146 if (element.enclosingElement == null) { |
| 181 _elementToOutputUnit[element] = mainOutputUnit; | 147 _elementToOutputUnit[element] = mainOutputUnit; |
| 182 break; | 148 break; |
| 183 } | 149 } |
| 184 element = element.enclosingElement.implementation; | 150 element = element.enclosingElement.implementation; |
| 185 } | 151 } |
| 186 return _elementToOutputUnit[element]; | 152 return _elementToOutputUnit[element]; |
| 187 } | 153 } |
| 188 | 154 |
| 189 /// Returns the [OutputUnit] where [element] belongs. | 155 /// Returns the [OutputUnit] where [element] belongs. |
| 190 OutputUnit outputUnitForClass(ClassEntity element) { | 156 OutputUnit outputUnitForClass(ClassElement element) { |
| 191 return outputUnitForElement(element); | 157 return outputUnitForElement(element); |
| 192 } | 158 } |
| 193 | 159 |
| 194 /// Returns the [OutputUnit] where [element] belongs. | 160 /// Returns the [OutputUnit] where [element] belongs. |
| 195 OutputUnit outputUnitForMember(MemberEntity element) { | 161 OutputUnit outputUnitForMember(MemberEntity element) { |
| 196 return outputUnitForElement(element); | 162 return outputUnitForElement(element); |
| 197 } | 163 } |
| 198 | 164 |
| 199 /// Direct access to the output-unit to element relation used for testing. | 165 /// Direct access to the output-unit to element relation used for testing. |
| 200 OutputUnit getOutputUnitForElementForTesting(Element element) { | 166 OutputUnit getOutputUnitForElementForTesting(Entity element) { |
| 201 return _elementToOutputUnit[element]; | 167 return _elementToOutputUnit[element]; |
| 202 } | 168 } |
| 203 | 169 |
| 204 /// Returns the [OutputUnit] where [constant] belongs. | 170 /// Returns the [OutputUnit] where [constant] belongs. |
| 205 OutputUnit outputUnitForConstant(ConstantValue constant) { | 171 OutputUnit outputUnitForConstant(ConstantValue constant) { |
| 206 if (!isProgramSplit) return mainOutputUnit; | 172 if (!isProgramSplit) return mainOutputUnit; |
| 207 return _constantToOutputUnit[constant]; | 173 return _constantToOutputUnit[constant]; |
| 208 } | 174 } |
| 209 | 175 |
| 210 /// Direct access to the output-unit to constants map used for testing. | 176 /// Direct access to the output-unit to constants map used for testing. |
| 211 Map<ConstantValue, OutputUnit> get outputUnitForConstantsForTesting { | 177 Map<ConstantValue, OutputUnit> get outputUnitForConstantsForTesting { |
| 212 return _constantToOutputUnit; | 178 return _constantToOutputUnit; |
| 213 } | 179 } |
| 214 | 180 |
| 215 bool isDeferred(Entity element) { | 181 bool isDeferred(Entity element) { |
| 216 return outputUnitForElement(element) != mainOutputUnit; | 182 return outputUnitForElement(element) != mainOutputUnit; |
| 217 } | 183 } |
| 218 | 184 |
| 219 bool isDeferredClass(ClassEntity element) { | 185 bool isDeferredClass(ClassEntity element) { |
| 220 return outputUnitForElement(element) != mainOutputUnit; | 186 return outputUnitForElement(element) != mainOutputUnit; |
| 221 } | 187 } |
| 222 | 188 |
| 223 /// Returns the unique name for the deferred import of [prefix]. | 189 /// Returns the unique name for the deferred import of [prefix]. |
| 224 String getImportDeferName(Spannable node, PrefixElement prefix) { | 190 String getImportDeferName(Spannable node, PrefixElement prefix) { |
| 225 String name = | 191 String name = _importDeferName[prefix.deferredImport]; |
| 226 importDeferName[new _DeclaredDeferredImport(prefix.deferredImport)]; | |
| 227 if (name == null) { | 192 if (name == null) { |
| 228 reporter.internalError(node, "No deferred name for $prefix."); | 193 reporter.internalError(node, "No deferred name for $prefix."); |
| 229 } | 194 } |
| 230 return name; | 195 return name; |
| 231 } | 196 } |
| 232 | 197 |
| 198 /// Returns the names associated with each deferred import in [unit]. | |
| 199 Iterable<String> getImportNames(OutputUnit unit) { | |
| 200 ImportSet importSet = unit; | |
| 201 return importSet._imports.map((i) => _importDeferName[i]); | |
| 202 } | |
| 203 | |
| 233 /// Returns `true` if element [to] is reachable from element [from] without | 204 /// Returns `true` if element [to] is reachable from element [from] without |
| 234 /// crossing a deferred import. | 205 /// crossing a deferred import. |
| 235 /// | 206 /// |
| 236 /// For example, if we have two deferred libraries `A` and `B` that both | 207 /// 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 | 208 /// 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`. | 209 /// different output units, there is a non-deferred path between `A` and `C`. |
| 239 bool hasOnlyNonDeferredImportPaths(Element from, Element to) { | 210 bool hasOnlyNonDeferredImportPaths(Entity from, Entity to) { |
| 240 OutputUnit outputUnitFrom = outputUnitForElement(from); | 211 ImportSet outputUnitFrom = outputUnitForElement(from); |
| 241 OutputUnit outputUnitTo = outputUnitForElement(to); | 212 ImportSet outputUnitTo = outputUnitForElement(to); |
| 242 return outputUnitTo.imports.containsAll(outputUnitFrom.imports); | 213 return outputUnitTo._imports.containsAll(outputUnitFrom._imports); |
| 243 } | |
| 244 | |
| 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 } | 214 } |
| 254 | 215 |
| 255 void registerConstantDeferredUse( | 216 void registerConstantDeferredUse( |
| 256 DeferredConstantValue constant, PrefixElement prefix) { | 217 DeferredConstantValue constant, PrefixElement prefix) { |
| 257 OutputUnit outputUnit = new OutputUnit(); | 218 var importSet = importSets.singleton(prefix.deferredImport); |
| 258 outputUnit.imports.add(new _DeclaredDeferredImport(prefix.deferredImport)); | 219 assert(_constantToOutputUnit[constant] == null || |
| 259 | 220 _constantToOutputUnit[constant] == importSet); |
| 260 // Check to see if there is already a canonical output unit registered. | 221 _constantToOutputUnit[constant] = importSet; |
| 261 _constantToOutputUnit[constant] = _getCanonicalUnit(outputUnit); | |
| 262 } | 222 } |
| 263 | 223 |
| 264 /// Given [imports] that refer to an element from a library, determine whether | 224 /// Given [imports] that refer to an element from a library, determine whether |
| 265 /// the element is explicitly deferred. | 225 /// the element is explicitly deferred. |
| 266 static bool _isExplicitlyDeferred(Iterable<ImportElement> imports) { | 226 static bool _isExplicitlyDeferred(Iterable<ImportElement> imports) { |
| 267 // If the element is not imported explicitly, it is implicitly imported | 227 // If the element is not imported explicitly, it is implicitly imported |
| 268 // not deferred. | 228 // not deferred. |
| 269 if (imports.isEmpty) return false; | 229 if (imports.isEmpty) return false; |
| 270 // An element could potentially be loaded by several imports. If all of them | 230 // An element could potentially be loaded by several imports. If all of them |
| 271 // is explicitly deferred, we say the element is explicitly deferred. | 231 // is explicitly deferred, we say the element is explicitly deferred. |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 337 } else { | 297 } else { |
| 338 // TODO(sigurdm): We want to be more specific about this - need a better | 298 // TODO(sigurdm): We want to be more specific about this - need a better |
| 339 // way to query "liveness". | 299 // way to query "liveness". |
| 340 MemberElement analyzableElement = element.analyzableElement.declaration; | 300 MemberElement analyzableElement = element.analyzableElement.declaration; |
| 341 if (!compiler.resolutionWorldBuilder.isMemberUsed(analyzableElement)) { | 301 if (!compiler.resolutionWorldBuilder.isMemberUsed(analyzableElement)) { |
| 342 return; | 302 return; |
| 343 } | 303 } |
| 344 | 304 |
| 345 WorldImpact worldImpact = | 305 WorldImpact worldImpact = |
| 346 compiler.resolution.getWorldImpact(analyzableElement); | 306 compiler.resolution.getWorldImpact(analyzableElement); |
| 307 | |
| 347 compiler.impactStrategy.visitImpact( | 308 compiler.impactStrategy.visitImpact( |
| 348 analyzableElement, | 309 analyzableElement, |
| 349 worldImpact, | 310 worldImpact, |
| 350 new WorldImpactVisitorImpl(visitStaticUse: (StaticUse staticUse) { | 311 new WorldImpactVisitorImpl(visitStaticUse: (StaticUse staticUse) { |
| 351 elements.add(staticUse.element); | 312 elements.add(staticUse.element); |
| 352 switch (staticUse.kind) { | 313 switch (staticUse.kind) { |
| 353 case StaticUseKind.CONSTRUCTOR_INVOKE: | 314 case StaticUseKind.CONSTRUCTOR_INVOKE: |
| 354 case StaticUseKind.CONST_CONSTRUCTOR_INVOKE: | 315 case StaticUseKind.CONST_CONSTRUCTOR_INVOKE: |
| 355 collectTypeDependencies(staticUse.type); | 316 collectTypeDependencies(staticUse.type); |
| 356 break; | 317 break; |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 503 if (library.isPatched) { | 464 if (library.isPatched) { |
| 504 iterateTags(library.implementation); | 465 iterateTags(library.implementation); |
| 505 } | 466 } |
| 506 } | 467 } |
| 507 | 468 |
| 508 traverseLibrary(root); | 469 traverseLibrary(root); |
| 509 result.add(compiler.resolution.commonElements.coreLibrary); | 470 result.add(compiler.resolution.commonElements.coreLibrary); |
| 510 return result; | 471 return result; |
| 511 } | 472 } |
| 512 | 473 |
| 513 /// Add all dependencies of [constant] to the mapping of [import]. | 474 /// Update the import set of all constants reachable from [constant], as long |
| 514 void _mapConstantDependencies( | 475 /// as they had the [oldSet]. As soon as we see a constant with a different |
| 515 ConstantValue constant, _DeferredImport import) { | 476 /// import set, we stop and enqueue a new recursive update in [queue]. |
| 516 Set<ConstantValue> constants = _constantsDeferredBy.putIfAbsent( | 477 /// |
| 517 import, () => new Set<ConstantValue>()); | 478 /// Invariants: oldSet is either null or a subset of newSet. |
| 518 if (constants.contains(constant)) return; | 479 void _updateConstantRecursive(ConstantValue constant, ImportSet oldSet, |
| 519 constants.add(constant); | 480 ImportSet newSet, WorkQueue queue) { |
| 520 if (constant is ConstructedConstantValue) { | 481 if (constant == null) return; |
| 521 ClassElement cls = constant.type.element; | 482 var importSet = _constantToOutputUnit[constant]; |
| 522 _mapDependencies(element: cls, import: import); | 483 |
| 484 // Already visited. | |
| 485 if (importSet == newSet) return; | |
| 486 | |
| 487 // Elements in the main output unit always remain there. | |
| 488 if (importSet == importSets.emptySet) return; | |
| 489 | |
| 490 if (importSet == oldSet) { | |
| 491 _constantToOutputUnit[constant] = newSet; | |
| 492 if (constant is ConstructedConstantValue) { | |
| 493 ClassElement cls = constant.type.element; | |
| 494 _updateElementRecursive(cls, oldSet, newSet, queue); | |
| 495 } | |
| 496 constant.getDependencies().forEach((ConstantValue dependency) { | |
| 497 if (dependency is DeferredConstantValue) { | |
| 498 /// New deferred-imports are only discovered when we are visiting the | |
| 499 /// main output unit (size == 0) or code reachable from a deferred | |
| 500 /// import (size == 1). After that, we are rediscovering the | |
| 501 /// same nodes we have already seen. | |
| 502 if (newSet.size <= 1) { | |
| 503 PrefixElement prefix = dependency.prefix; | |
| 504 queue.addConstant( | |
| 505 dependency, importSets.singleton(prefix.deferredImport)); | |
| 506 } | |
| 507 } else { | |
| 508 _updateConstantRecursive(dependency, oldSet, newSet, queue); | |
| 509 } | |
| 510 }); | |
| 511 } else { | |
| 512 assert( | |
| 513 newSet != importSets.emptySet || oldSet != null, | |
| 514 failedAt( | |
| 515 NO_LOCATION_SPANNABLE, | |
| 516 "Tried to assign ${constant.toDartText()} to the main output " | |
| 517 "unit, but it was assigned to $importSet.")); | |
| 518 queue.addConstant(constant, newSet); | |
| 523 } | 519 } |
| 524 constant.getDependencies().forEach((ConstantValue dependency) { | |
| 525 _mapConstantDependencies(dependency, import); | |
| 526 }); | |
| 527 } | 520 } |
| 528 | 521 |
| 529 /// Recursively traverses the graph of dependencies from [element], mapping | 522 /// Update the import set of all elements reachable from [element], as long as |
| 530 /// deferred imports to each dependency it needs in the sets | 523 /// they had the [oldSet]. As soon as we see an element with a different |
| 531 /// [_importedDeferredBy] and [_constantsDeferredBy]. | 524 /// import set, we stop and enqueue a new recursive update in [queue]. |
| 532 void _mapDependencies( | 525 void _updateElementRecursive( |
| 533 {Element element, _DeferredImport import, isMirrorUsage: false}) { | 526 Element element, ImportSet oldSet, ImportSet newSet, WorkQueue queue, |
| 534 Set<Element> elements = _importedDeferredBy[import] ??= new Set<Element>(); | 527 {bool isMirrorUsage: false}) { |
| 528 if (element == null) return; | |
| 529 var importSet = _elementToOutputUnit[element]; | |
| 535 | 530 |
| 536 Set<Element> dependentElements = new Set<Element>(); | 531 // Already visited. We may visit some root nodes a second time with |
| 537 Set<ConstantValue> dependentConstants = new Set<ConstantValue>(); | 532 // [isMirrorUsage] so we embed static members as well. |
| 533 if (importSet == newSet && !isMirrorUsage) return; | |
| 538 | 534 |
| 539 LibraryElement library; | 535 // Elements in the main output unit always remain there. |
| 536 if (importSet == importSets.emptySet) return; | |
| 540 | 537 |
| 541 if (element != null) { | 538 if (importSet == oldSet) { |
| 542 // Only process elements once, unless we are doing dependencies due to | 539 // Continue recursively updating from [oldSet] to [newSet]. |
| 543 // mirrors, which are added in additional traversals. | 540 _elementToOutputUnit[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 | 541 |
| 552 // This call can modify [dependentElements] and [dependentConstants]. | 542 Set<Element> dependentElements = new Set<Element>(); |
| 543 Set<ConstantValue> dependentConstants = new Set<ConstantValue>(); | |
| 553 _collectAllElementsAndConstantsResolvedFrom( | 544 _collectAllElementsAndConstantsResolvedFrom( |
| 554 element, dependentElements, dependentConstants, isMirrorUsage); | 545 element, dependentElements, dependentConstants, isMirrorUsage); |
| 555 | 546 |
| 556 library = element.library; | 547 LibraryElement library = element.library; |
| 557 } | 548 for (Element dependency in dependentElements) { |
| 549 Iterable<ImportElement> imports = _getImports(dependency, library); | |
| 550 if (_isExplicitlyDeferred(imports)) { | |
| 551 /// New deferred-imports are only discovered when we are visiting the | |
| 552 /// main output unit (size == 0) or code reachable from a deferred | |
| 553 /// import (size == 1). After that, we are rediscovering the | |
| 554 /// same nodes we have already seen. | |
| 555 if (newSet.size <= 1) { | |
| 556 for (ImportElement deferredImport in imports) { | |
| 557 queue.addElement( | |
| 558 dependency, importSets.singleton(deferredImport)); | |
| 559 } | |
| 560 } | |
| 561 } else { | |
| 562 _updateElementRecursive(dependency, oldSet, newSet, queue); | |
| 563 } | |
| 564 } | |
| 558 | 565 |
| 559 for (Element dependency in dependentElements) { | 566 for (ConstantValue dependency in dependentConstants) { |
| 560 Iterable<ImportElement> imports = _getImports(dependency, library); | 567 if (dependency is DeferredConstantValue) { |
| 561 if (_isExplicitlyDeferred(imports)) { | 568 if (newSet.size <= 1) { |
| 562 for (ImportElement deferredImport in imports) { | 569 PrefixElement prefix = dependency.prefix; |
| 563 _mapDependencies( | 570 queue.addConstant( |
| 564 element: dependency, | 571 dependency, importSets.singleton(prefix.deferredImport)); |
| 565 import: new _DeclaredDeferredImport(deferredImport)); | 572 } |
| 573 } else { | |
| 574 _updateConstantRecursive(dependency, oldSet, newSet, queue); | |
| 566 } | 575 } |
| 567 } else { | |
| 568 _mapDependencies(element: dependency, import: import); | |
| 569 } | 576 } |
| 570 } | 577 } else { |
| 571 | 578 // elements in the emptySet will never be updated, so we don't |
| 572 for (ConstantValue dependency in dependentConstants) { | 579 // enqueue any work on them. |
| 573 if (dependency is DeferredConstantValue) { | 580 queue.addElement(element, newSet); |
| 574 PrefixElement prefix = dependency.prefix; | |
| 575 _mapConstantDependencies( | |
| 576 dependency, new _DeclaredDeferredImport(prefix.deferredImport)); | |
| 577 } else { | |
| 578 _mapConstantDependencies(dependency, import); | |
| 579 } | |
| 580 } | 581 } |
| 581 } | 582 } |
| 582 | 583 |
| 583 /// Adds extra dependencies coming from mirror usage. | 584 /// Adds extra dependencies coming from mirror usage. |
| 584 /// | 585 void _addDeferredMirrorElements(WorkQueue queue) { |
| 585 /// The elements are added with [_mapDependencies]. | 586 for (ImportElement deferredImport in importSets._allDeferredImports) { |
| 586 void _addMirrorElements() { | 587 _addMirrorElementsForLibrary(queue, deferredImport.importedLibrary, |
| 587 void mapDependenciesIfResolved( | 588 importSets.singleton(deferredImport)); |
| 588 Element element, _DeferredImport deferredImport) { | 589 } |
| 590 } | |
| 591 | |
| 592 void _addMirrorElementsForLibrary( | |
| 593 WorkQueue queue, LibraryElement root, ImportSet importSet) { | |
| 594 void handleElementIfResolved(Element element, ImportSet importSet) { | |
| 589 // If an element is the target of a MirrorsUsed annotation but never used | 595 // 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. | 596 // It will not be resolved, and we should not call isNeededForReflection. |
| 591 // TODO(sigurdm): Unresolved elements should just answer false when | 597 // TODO(sigurdm): Unresolved elements should just answer false when |
| 592 // asked isNeededForReflection. Instead an internal error is triggered. | 598 // asked isNeededForReflection. Instead an internal error is triggered. |
| 593 // So we have to filter them out here. | 599 // So we have to filter them out here. |
| 594 if (element is AnalyzableElementX && !element.hasTreeElements) return; | 600 if (element is AnalyzableElementX && !element.hasTreeElements) return; |
| 595 | 601 |
| 596 bool isAccessibleByReflection(Element element) { | 602 bool isAccessibleByReflection(Element element) { |
| 597 if (element.isLibrary) { | 603 if (element.isLibrary) { |
| 598 return false; | 604 return false; |
| 599 } else if (element.isClass) { | 605 } else if (element.isClass) { |
| 600 ClassElement cls = element; | 606 ClassElement cls = element; |
| 601 return compiler.backend.mirrorsData | 607 return compiler.backend.mirrorsData |
| 602 .isClassAccessibleByReflection(cls); | 608 .isClassAccessibleByReflection(cls); |
| 603 } else if (element.isTypedef) { | 609 } else if (element.isTypedef) { |
| 604 TypedefElement typedef = element; | 610 TypedefElement typedef = element; |
| 605 return compiler.backend.mirrorsData | 611 return compiler.backend.mirrorsData |
| 606 .isTypedefAccessibleByReflection(typedef); | 612 .isTypedefAccessibleByReflection(typedef); |
| 607 } else { | 613 } else { |
| 608 MemberElement member = element; | 614 MemberElement member = element; |
| 609 return compiler.backend.mirrorsData | 615 return compiler.backend.mirrorsData |
| 610 .isMemberAccessibleByReflection(member); | 616 .isMemberAccessibleByReflection(member); |
| 611 } | 617 } |
| 612 } | 618 } |
| 613 | 619 |
| 614 if (isAccessibleByReflection(element)) { | 620 if (isAccessibleByReflection(element)) { |
| 615 _mapDependencies( | 621 queue.addElement(element, importSet, isMirrorsUsage: true); |
| 616 element: element, import: deferredImport, isMirrorUsage: true); | |
| 617 } | 622 } |
| 618 } | 623 } |
| 619 | 624 |
| 620 // For each deferred import we analyze all elements reachable from the | 625 // For each deferred import we analyze all elements reachable from the |
| 621 // imported library through non-deferred imports. | 626 // imported library through non-deferred imports. |
| 622 void handleLibrary(LibraryElement library, _DeferredImport deferredImport) { | 627 void handleLibrary(LibraryElement library, ImportSet importSet) { |
| 623 library.implementation.forEachLocalMember((Element element) { | 628 library.implementation.forEachLocalMember((Element element) { |
| 624 mapDependenciesIfResolved(element, deferredImport); | 629 handleElementIfResolved(element, importSet); |
| 625 }); | 630 }); |
| 626 | 631 |
| 627 void processMetadata(Element element) { | 632 void processMetadata(Element element) { |
| 628 for (MetadataAnnotation metadata in element.metadata) { | 633 for (MetadataAnnotation metadata in element.metadata) { |
| 629 ConstantValue constant = | 634 ConstantValue constant = |
| 630 backend.constants.getConstantValueForMetadata(metadata); | 635 backend.constants.getConstantValueForMetadata(metadata); |
| 631 if (constant != null) { | 636 if (constant != null) { |
| 632 _mapConstantDependencies(constant, deferredImport); | 637 queue.addConstant(constant, importSet); |
| 633 } | 638 } |
| 634 } | 639 } |
| 635 } | 640 } |
| 636 | 641 |
| 637 processMetadata(library); | 642 processMetadata(library); |
| 638 library.imports.forEach(processMetadata); | 643 library.imports.forEach(processMetadata); |
| 639 library.exports.forEach(processMetadata); | 644 library.exports.forEach(processMetadata); |
| 640 } | 645 } |
| 641 | 646 |
| 642 for (_DeferredImport deferredImport in _allDeferredImports.keys) { | 647 for (LibraryElement library in _nonDeferredReachableLibraries(root)) { |
| 643 LibraryElement deferredLibrary = _allDeferredImports[deferredImport]; | 648 handleLibrary(library, importSet); |
| 644 for (LibraryElement library | |
| 645 in _nonDeferredReachableLibraries(deferredLibrary)) { | |
| 646 handleLibrary(library, deferredImport); | |
| 647 } | |
| 648 } | 649 } |
| 649 } | 650 } |
| 650 | 651 |
| 651 /// Computes a unique string for the name field for each outputUnit. | 652 /// Computes a unique string for the name field for each outputUnit. |
| 652 /// | 653 /// |
| 653 /// Also sets up the [hunksToLoad] mapping. | 654 /// Also sets up the [hunksToLoad] mapping. |
| 654 void _assignNamesToOutputUnits(Set<OutputUnit> allOutputUnits) { | 655 void _assignNamesToOutputUnits() { |
| 655 Set<String> usedImportNames = new Set<String>(); | 656 Set<String> usedImportNames = new Set<String>(); |
| 656 | 657 |
| 657 void computeImportDeferName(_DeferredImport import) { | 658 void computeImportDeferName(ImportElement import) { |
| 658 String result = import.computeImportDeferName(compiler); | 659 String result = _computeImportDeferName(import, compiler); |
| 659 assert(result != null); | 660 assert(result != null); |
| 660 importDeferName[import] = makeUnique(result, usedImportNames); | 661 _importDeferName[import] = makeUnique(result, usedImportNames); |
| 661 } | 662 } |
| 662 | 663 |
| 663 int counter = 1; | 664 int counter = 1; |
| 664 | 665 |
| 665 for (_DeferredImport import in _allDeferredImports.keys) { | 666 for (ImportElement import in _deferredImportDescriptions.keys) { |
| 666 computeImportDeferName(import); | 667 computeImportDeferName(import); |
| 667 } | 668 } |
| 668 | 669 |
| 669 for (OutputUnit outputUnit in allOutputUnits) { | 670 for (ImportSet outputUnit in allOutputUnits) { |
| 670 if (outputUnit == mainOutputUnit) { | 671 if (outputUnit == mainOutputUnit) { |
| 671 outputUnit.name = "main"; | 672 outputUnit.name = "main"; |
| 672 } else { | 673 } else { |
| 673 outputUnit.name = "$counter"; | 674 outputUnit.name = "$counter"; |
| 674 ++counter; | 675 ++counter; |
| 675 } | 676 } |
| 676 } | 677 } |
| 677 | 678 |
| 678 List sortedOutputUnits = new List.from(allOutputUnits); | 679 List sortedOutputUnits = new List.from(allOutputUnits); |
| 679 // Sort the output units in descending order of the number of imports they | 680 // Sort the output units in descending order of the number of imports they |
| 680 // include. | 681 // include. |
| 681 | 682 |
| 682 // The loading of the output units must be ordered because a superclass | 683 // The loading of the output units must be ordered because a superclass |
| 683 // needs to be initialized before its subclass. | 684 // needs to be initialized before its subclass. |
| 684 // But a class can only depend on another class in an output unit shared by | 685 // But a class can only depend on another class in an output unit shared by |
| 685 // a strict superset of the imports: | 686 // a strict superset of the imports: |
| 686 // By contradiction: Assume a class C in output unit shared by imports in | 687 // 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 | 688 // 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 | 689 // 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 | 690 // 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. | 691 // is not in the right output unit. |
| 691 sortedOutputUnits.sort((a, b) => b.imports.length - a.imports.length); | 692 sortedOutputUnits.sort((a, b) => b.size - a.size); |
| 692 | 693 |
| 693 // For each deferred import we find out which outputUnits to load. | 694 // For each deferred import we find out which outputUnits to load. |
| 694 for (_DeferredImport import in _allDeferredImports.keys) { | 695 for (ImportElement import in importSets._allDeferredImports) { |
| 695 if (import == _fakeMainImport) continue; | 696 hunksToLoad[_importDeferName[import]] = new List<OutputUnit>(); |
| 696 hunksToLoad[importDeferName[import]] = new List<OutputUnit>(); | 697 for (ImportSet outputUnit in sortedOutputUnits) { |
| 697 for (OutputUnit outputUnit in sortedOutputUnits) { | |
| 698 if (outputUnit == mainOutputUnit) continue; | 698 if (outputUnit == mainOutputUnit) continue; |
| 699 if (outputUnit.imports.contains(import)) { | 699 if (outputUnit._imports.contains(import)) { |
| 700 hunksToLoad[importDeferName[import]].add(outputUnit); | 700 hunksToLoad[_importDeferName[import]].add(outputUnit); |
| 701 } | 701 } |
| 702 } | 702 } |
| 703 } | 703 } |
| 704 } | 704 } |
| 705 | 705 |
| 706 /// Performs the deferred loading algorithm. | |
| 707 /// | |
| 708 /// The deferred loading algorithm maps elements and constants to an output | |
| 709 /// unit. Each output unit is identified by a subset of deferred imports (an | |
| 710 /// [ImportSet]), and they will contain the elements that are inheretly used | |
| 711 /// by all those deferred imports. An element is used by a deferred import if | |
| 712 /// it is either loaded by that import or transitively accessed by an element | |
| 713 /// that the import loads. An empty set represents the main output unit, | |
| 714 /// which contains any elements that are accessed directly and are not | |
| 715 /// deferred. | |
| 716 /// | |
| 717 /// The algorithm traverses the element model recursively looking for | |
| 718 /// dependencies between elements. These dependencies may be deferred or | |
| 719 /// non-deferred. Deferred dependencies are mainly used to discover the root | |
| 720 /// elements that are loaded from deferred imports, while non-deferred | |
| 721 /// dependencies are used to recursively associate more elements to output | |
| 722 /// units. | |
| 723 /// | |
| 724 /// Naively, the algorithm traverses each root of a deferred import and marks | |
| 725 /// everything it can reach as being used by that import. To reduce how many | |
| 726 /// times we visit an element, we use an algorithm that works in segments: it | |
| 727 /// marks elements with a subset of deferred imports at a time, until it | |
| 728 /// detects a merge point where more deferred imports could be considered at | |
| 729 /// once. | |
| 730 /// | |
| 731 /// For example, consider this dependency graph (ignoring elements in the main | |
| 732 /// output unit): | |
| 733 /// | |
| 734 /// deferred import A: a1 ---> s1 ---> s2 -> s3 | |
| 735 /// ^ ^ | |
| 736 /// | | | |
| 737 /// deferred import B: b1 -----+ | | |
| 738 /// | | |
| 739 /// deferred import C: c1 ---> c2 ---> c3 | |
| 740 /// | |
| 741 /// The algorithm will compute a result with 5 deferred output units: | |
| 742 // | |
| 743 /// * unit {A}: contains a1 | |
| 744 /// * unit {B}: contains b1 | |
| 745 /// * unit {C}: contains c1, c2, and c3 | |
| 746 /// * unit {A, B}: contains s1 | |
| 747 /// * unit {A, B, C}: contains s2, and s3 | |
| 748 /// | |
| 749 /// After marking everything reachable from main as part of the main output | |
| 750 /// unit, our algorithm will work as follows: | |
| 751 /// | |
| 752 /// * Initially all deferred elements have no mapping. | |
| 753 /// * We make note of work to do, initially to mark the root of each | |
| 754 /// deferred import: | |
| 755 /// * a1 with A, and recurse from there. | |
| 756 /// * b1 with B, and recurse from there. | |
| 757 /// * c1 with C, and recurse from there. | |
| 758 /// * we update a1, s1, s2, s3 from no mapping to {A} | |
| 759 /// * we update b1 from no mapping to {B}, and when we find s1 we notice | |
| 760 /// that s1 is already associated with another import set {A}, so we make | |
| 761 /// note of additional work for later to mark s1 with {A, B} | |
| 762 /// * we update c1, c2, c3 to {C}, and make a note to update s2 with {A, C} | |
| 763 /// * we update s1 to {A, B}, and update the existing note to update s2, now | |
| 764 /// with {A, B, C} | |
| 765 /// * finally we update s2 and s3 with {A, B, C} in one go, without ever | |
| 766 /// updating them to the intermediate state {A, C}. | |
| 767 /// | |
| 768 /// The implementation below does atomic updates from one import-set to | |
| 769 /// another. At first we add one deferred import at a time, but as the | |
| 770 /// algorithm progesses it may update a small import-set with a larger | |
| 771 /// import-set in one go. The key of this algorithm is to detect when sharing | |
| 772 /// begins, so we can update those elements more efficently. | |
| 773 /// | |
| 774 /// To detect these merge points where sharing begins, the implementation | |
| 775 /// below uses `a swap operation`: we first compare what the old import-set | |
| 776 /// is, and if it matches our expectation, the swap is done and we recurse, | |
| 777 /// otherwise a merge root was detected and we enqueue a new segment of | |
| 778 /// updates for later. | |
| 779 /// | |
| 780 /// TODO(sigmund): investigate different heuristics for how to select the next | |
| 781 /// work item (e.g. we might converge faster if we pick first the update that | |
| 782 /// contains a bigger delta.) | |
| 706 void onResolutionComplete(FunctionEntity main, ClosedWorld closedWorld) { | 783 void onResolutionComplete(FunctionEntity main, ClosedWorld closedWorld) { |
| 707 if (!isProgramSplit) { | 784 if (!isProgramSplit) return; |
| 708 allOutputUnits.add(mainOutputUnit); | |
| 709 return; | |
| 710 } | |
| 711 if (main == null) return; | 785 if (main == null) return; |
| 712 MethodElement mainMethod = main; | 786 work() { |
| 713 LibraryElement mainLibrary = mainMethod.library; | 787 var queue = new WorkQueue(this.importSets); |
| 714 _importedDeferredBy = new Map<_DeferredImport, Set<Element>>(); | |
| 715 _constantsDeferredBy = new Map<_DeferredImport, Set<ConstantValue>>(); | |
| 716 _importedDeferredBy[_fakeMainImport] = _mainElements; | |
| 717 | 788 |
| 718 work() { | 789 // Add `main` and their recursive dependencies to the main output unit. |
|
sra1
2017/08/18 23:42:28
Explain somewhere why the main unit is completely
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Done.
| |
| 719 // Starting from main, traverse the program and find all dependencies. | 790 queue.addElement(main, importSets.emptySet); |
| 720 _mapDependencies(element: mainMethod, import: _fakeMainImport); | |
| 721 | 791 |
| 722 // Also add "global" dependencies to the main OutputUnit. These are | 792 // Also add "global" dependencies to the main output unit. These are |
| 723 // things that the backend needs but cannot associate with a particular | 793 // things that the backend needs but cannot associate with a particular |
| 724 // element, for example, startRootIsolate. This set also contains | 794 // element, for example, startRootIsolate. This set also contains |
| 725 // elements for which we lack precise information. | 795 // elements for which we lack precise information. |
| 726 for (MethodElement element | 796 for (MethodElement element |
| 727 in closedWorld.backendUsage.globalFunctionDependencies) { | 797 in closedWorld.backendUsage.globalFunctionDependencies) { |
| 728 _mapDependencies( | 798 queue.addElement(element.implementation, importSets.emptySet); |
| 729 element: element.implementation, import: _fakeMainImport); | |
| 730 } | 799 } |
| 731 for (ClassElement element | 800 for (ClassElement element |
| 732 in closedWorld.backendUsage.globalClassDependencies) { | 801 in closedWorld.backendUsage.globalClassDependencies) { |
| 733 _mapDependencies( | 802 queue.addElement(element.implementation, importSets.emptySet); |
| 734 element: element.implementation, import: _fakeMainImport); | 803 } |
| 804 if (closedWorld.backendUsage.isMirrorsUsed) { | |
| 805 _addMirrorElementsForLibrary(queue, main.library, importSets.emptySet); | |
| 735 } | 806 } |
| 736 | 807 |
| 737 // Now check to see if we have to add more elements due to mirrors. | 808 void emptyQueue() { |
| 738 if (closedWorld.backendUsage.isMirrorsUsed) { | 809 while (!queue.isEmpty) { |
|
sra1
2017/08/18 23:42:28
nit: queue.isNotEmpty
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Done.
| |
| 739 _addMirrorElements(); | 810 var item = queue.nextItem(); |
| 740 } | 811 if (item.element != null) { |
| 741 | 812 var oldSet = _elementToOutputUnit[item.element]; |
| 742 // Build the OutputUnits using these two maps. | 813 var newSet = importSets.union(oldSet, item.newSet); |
| 743 Map<Element, OutputUnit> elementToOutputUnitBuilder = | 814 _updateElementRecursive(item.element, oldSet, newSet, queue); |
| 744 new Map<Element, OutputUnit>(); | 815 } else if (item.value != null) { |
| 745 Map<ConstantValue, OutputUnit> constantToOutputUnitBuilder = | 816 var oldSet = _constantToOutputUnit[item.value]; |
| 746 new Map<ConstantValue, OutputUnit>(); | 817 var newSet = importSets.union(oldSet, item.newSet); |
| 747 | 818 _updateConstantRecursive(item.value, oldSet, newSet, queue); |
| 748 // Add all constants that may have been registered during resolution with | |
| 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 } | 819 } |
| 781 } | 820 } |
| 782 } | 821 } |
| 783 | 822 |
| 784 // Release maps; | 823 emptyQueue(); |
| 785 _importedDeferredBy = null; | 824 if (closedWorld.backendUsage.isMirrorsUsed) { |
| 786 _constantsDeferredBy = null; | 825 _addDeferredMirrorElements(queue); |
| 826 emptyQueue(); | |
| 827 } | |
| 787 | 828 |
| 788 // Find all the output units elements/constants have been mapped | 829 allOutputUnits.addAll(_elementToOutputUnit.values); |
| 789 // to, and canonicalize them. | 830 allOutputUnits.addAll(_constantToOutputUnit.values); |
| 790 elementToOutputUnitBuilder | 831 |
| 791 .forEach((Element element, OutputUnit outputUnit) { | 832 /// We generate an output unit for each defer import, even if all their |
| 792 _elementToOutputUnit[element] = _getCanonicalUnit(outputUnit); | 833 /// elements are shared with other deferred imports. |
| 793 }); | 834 allOutputUnits |
| 794 constantToOutputUnitBuilder | 835 .addAll(_deferredImportDescriptions.keys.map(importSets.singleton)); |
| 795 .forEach((ConstantValue constant, OutputUnit outputUnit) { | |
| 796 _constantToOutputUnit[constant] = _getCanonicalUnit(outputUnit); | |
| 797 }); | |
| 798 | 836 |
| 799 // Generate a unique name for each OutputUnit. | 837 // Generate a unique name for each OutputUnit. |
| 800 _assignNamesToOutputUnits(allOutputUnits); | 838 _assignNamesToOutputUnits(); |
| 801 } | 839 } |
| 802 | 840 |
| 803 reporter.withCurrentElement(mainLibrary, () => measure(work)); | 841 reporter.withCurrentElement(main.library, () => measure(work)); |
| 804 | 842 |
| 805 // Notify the impact strategy impacts are no longer needed for deferred | 843 // Notify that we no longer need impacts for deferred load, so they can be |
| 806 // load. | 844 // discarded at this time. |
| 807 compiler.impactStrategy.onImpactUsed(IMPACT_USE); | 845 compiler.impactStrategy.onImpactUsed(IMPACT_USE); |
| 808 } | 846 } |
| 809 | 847 |
| 810 void beforeResolution(LibraryEntity mainLibrary) { | 848 void beforeResolution(LibraryEntity mainLibrary) { |
| 811 if (mainLibrary == null) return; | 849 if (mainLibrary == null) return; |
| 812 // TODO(johnniwinther): Support deferred load for kernel based elements. | 850 // TODO(johnniwinther): Support deferred load for kernel based elements. |
| 813 if (compiler.options.useKernel) return; | 851 if (compiler.options.useKernel) return; |
| 814 _allDeferredImports[_fakeMainImport] = mainLibrary; | |
| 815 var lastDeferred; | 852 var lastDeferred; |
| 816 // When detecting duplicate prefixes of deferred libraries there are 4 | 853 // When detecting duplicate prefixes of deferred libraries there are 4 |
| 817 // cases of duplicate prefixes: | 854 // cases of duplicate prefixes: |
| 818 // 1. | 855 // 1. |
| 819 // import "lib.dart" deferred as a; | 856 // import "lib.dart" deferred as a; |
| 820 // import "lib2.dart" deferred as a; | 857 // import "lib2.dart" deferred as a; |
| 821 // 2. | 858 // 2. |
| 822 // import "lib.dart" deferred as a; | 859 // import "lib.dart" deferred as a; |
| 823 // import "lib2.dart" as a; | 860 // import "lib2.dart" as a; |
| 824 // 3. | 861 // 3. |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 855 reporter.reportErrorMessage( | 892 reporter.reportErrorMessage( |
| 856 import, MessageKind.DEFERRED_OLD_SYNTAX); | 893 import, MessageKind.DEFERRED_OLD_SYNTAX); |
| 857 } | 894 } |
| 858 } | 895 } |
| 859 } | 896 } |
| 860 | 897 |
| 861 String prefix = (import.prefix != null) ? import.prefix.name : null; | 898 String prefix = (import.prefix != null) ? import.prefix.name : null; |
| 862 // The last import we saw with the same prefix. | 899 // The last import we saw with the same prefix. |
| 863 ImportElement previousDeferredImport = prefixDeferredImport[prefix]; | 900 ImportElement previousDeferredImport = prefixDeferredImport[prefix]; |
| 864 if (import.isDeferred) { | 901 if (import.isDeferred) { |
| 865 _DeferredImport key = new _DeclaredDeferredImport(import); | |
| 866 LibraryElement importedLibrary = import.importedLibrary; | |
| 867 _allDeferredImports[key] = importedLibrary; | |
| 868 | |
| 869 if (prefix == null) { | 902 if (prefix == null) { |
| 870 reporter.reportErrorMessage( | 903 reporter.reportErrorMessage( |
| 871 import, MessageKind.DEFERRED_LIBRARY_WITHOUT_PREFIX); | 904 import, MessageKind.DEFERRED_LIBRARY_WITHOUT_PREFIX); |
| 872 } else { | 905 } else { |
| 873 prefixDeferredImport[prefix] = import; | 906 prefixDeferredImport[prefix] = import; |
| 874 Uri mainLibraryUri = compiler.mainLibraryUri; | 907 Uri mainLibraryUri = compiler.mainLibraryUri; |
| 875 _deferredImportDescriptions[key] = | 908 _deferredImportDescriptions[import] = |
| 876 new ImportDescription(import, library, mainLibraryUri); | 909 new ImportDescription(import, library, mainLibraryUri); |
| 877 } | 910 } |
| 878 isProgramSplit = true; | 911 isProgramSplit = true; |
| 879 lastDeferred = import; | 912 lastDeferred = import; |
| 880 } | 913 } |
| 881 if (prefix != null) { | 914 if (prefix != null) { |
| 882 if (previousDeferredImport != null || | 915 if (previousDeferredImport != null || |
| 883 (import.isDeferred && usedPrefixes.contains(prefix))) { | 916 (import.isDeferred && usedPrefixes.contains(prefix))) { |
| 884 ImportElement failingImport = (previousDeferredImport != null) | 917 ImportElement failingImport = (previousDeferredImport != null) |
| 885 ? previousDeferredImport | 918 ? 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 | 997 /// - <library uri> is the relative uri of the library making a deferred |
| 965 /// import. | 998 /// import. |
| 966 /// - <library name> is the name of the library, and "<unnamed>" if it is | 999 /// - <library name> is the name of the library, and "<unnamed>" if it is |
| 967 /// unnamed. | 1000 /// unnamed. |
| 968 /// - <prefix> is the `as` prefix used for a given deferred import. | 1001 /// - <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 | 1002 /// - <list of files> is a list of the filenames the must be loaded when that |
| 970 /// import is loaded. | 1003 /// import is loaded. |
| 971 Map<String, Map<String, dynamic>> computeDeferredMap() { | 1004 Map<String, Map<String, dynamic>> computeDeferredMap() { |
| 972 Map<String, Map<String, dynamic>> mapping = | 1005 Map<String, Map<String, dynamic>> mapping = |
| 973 new Map<String, Map<String, dynamic>>(); | 1006 new Map<String, Map<String, dynamic>>(); |
| 974 _deferredImportDescriptions.keys.forEach((_DeferredImport import) { | 1007 _deferredImportDescriptions.keys.forEach((ImportElement import) { |
| 975 List<OutputUnit> outputUnits = hunksToLoad[importDeferName[import]]; | 1008 List<OutputUnit> outputUnits = hunksToLoad[_importDeferName[import]]; |
| 976 ImportDescription description = _deferredImportDescriptions[import]; | 1009 ImportDescription description = _deferredImportDescriptions[import]; |
| 977 Map<String, dynamic> libraryMap = mapping.putIfAbsent( | 1010 Map<String, dynamic> libraryMap = mapping.putIfAbsent( |
| 978 description.importingUri, | 1011 description.importingUri, |
| 979 () => <String, dynamic>{ | 1012 () => <String, dynamic>{ |
| 980 "name": description.importingLibraryName, | 1013 "name": description.importingLibraryName, |
| 981 "imports": <String, List<String>>{} | 1014 "imports": <String, List<String>>{} |
| 982 }); | 1015 }); |
| 983 | 1016 |
| 984 libraryMap["imports"][importDeferName[import]] = | 1017 libraryMap["imports"][_importDeferName[import]] = |
| 985 outputUnits.map((OutputUnit outputUnit) { | 1018 outputUnits.map((OutputUnit outputUnit) { |
| 986 return deferredPartFileName(outputUnit.name); | 1019 return deferredPartFileName(outputUnit.name); |
| 987 }).toList(); | 1020 }).toList(); |
| 988 }); | 1021 }); |
| 989 return mapping; | 1022 return mapping; |
| 990 } | 1023 } |
| 991 | 1024 |
| 992 /// Returns the filename for the output-unit named [name]. | 1025 /// Returns the filename for the output-unit named [name]. |
| 993 /// | 1026 /// |
| 994 /// The filename is of the form "<main output file>_<name>.part.js". | 1027 /// The filename is of the form "<main output file>_<name>.part.js". |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1008 Map<OutputUnit, List<String>> elementMap = <OutputUnit, List<String>>{}; | 1041 Map<OutputUnit, List<String>> elementMap = <OutputUnit, List<String>>{}; |
| 1009 Map<OutputUnit, List<String>> constantMap = <OutputUnit, List<String>>{}; | 1042 Map<OutputUnit, List<String>> constantMap = <OutputUnit, List<String>>{}; |
| 1010 _elementToOutputUnit.forEach((Element element, OutputUnit output) { | 1043 _elementToOutputUnit.forEach((Element element, OutputUnit output) { |
| 1011 elementMap.putIfAbsent(output, () => <String>[]).add('$element'); | 1044 elementMap.putIfAbsent(output, () => <String>[]).add('$element'); |
| 1012 }); | 1045 }); |
| 1013 _constantToOutputUnit.forEach((ConstantValue value, OutputUnit output) { | 1046 _constantToOutputUnit.forEach((ConstantValue value, OutputUnit output) { |
| 1014 constantMap | 1047 constantMap |
| 1015 .putIfAbsent(output, () => <String>[]) | 1048 .putIfAbsent(output, () => <String>[]) |
| 1016 .add(value.toStructuredText()); | 1049 .add(value.toStructuredText()); |
| 1017 }); | 1050 }); |
| 1051 elementMap.forEach((k, v) => v.sort()); | |
| 1052 constantMap.forEach((k, v) => v.sort()); | |
| 1018 | 1053 |
| 1019 StringBuffer sb = new StringBuffer(); | 1054 StringBuffer sb = new StringBuffer(); |
| 1020 for (OutputUnit outputUnit in allOutputUnits) { | 1055 var list = allOutputUnits.toList(); |
| 1056 list.sort((a, b) { | |
| 1057 if (elementMap[a] != null && elementMap[b] == null) return -1; | |
| 1058 if (elementMap[a] == null && elementMap[b] != null) return 1; | |
| 1059 if (elementMap[a] == null && elementMap[b] == null) { | |
| 1060 if (constantMap[a] != null && constantMap[b] == null) return -1; | |
| 1061 if (constantMap[a] == null && constantMap[b] != null) return 1; | |
| 1062 for (int i = 0; i < constantMap[a].length; i++) { | |
| 1063 if (i >= constantMap[b].length) return 1; | |
| 1064 var fa = constantMap[a][i]; | |
| 1065 var fb = constantMap[b][i]; | |
| 1066 if (fa != fb) return fa.compareTo(fb); | |
| 1067 } | |
| 1068 return -1; | |
| 1069 } | |
| 1070 for (int i = 0; i < elementMap[a].length; i++) { | |
| 1071 if (i >= elementMap[b].length) return 1; | |
| 1072 var fa = elementMap[a][i]; | |
| 1073 var fb = elementMap[b][i]; | |
| 1074 if (fa != fb) return fa.compareTo(fb); | |
| 1075 } | |
| 1076 return -1; | |
| 1077 }); | |
| 1078 for (OutputUnit outputUnit in list) { | |
| 1021 sb.write('\n-------------------------------\n'); | 1079 sb.write('\n-------------------------------\n'); |
| 1022 sb.write('Output unit: ${outputUnit.name}'); | 1080 sb.write('Output unit: ${outputUnit.name}'); |
| 1023 List<String> elements = elementMap[outputUnit]; | 1081 List<String> elements = elementMap[outputUnit]; |
| 1024 if (elements != null) { | 1082 if (elements != null) { |
| 1025 sb.write('\n elements:'); | 1083 sb.write('\n elements:'); |
| 1026 for (String element in elements..sort()) { | 1084 for (String element in elements..sort()) { |
| 1027 sb.write('\n $element'); | 1085 sb.write('\n $element'); |
| 1028 } | 1086 } |
| 1029 } | 1087 } |
| 1030 List<String> constants = constantMap[outputUnit]; | 1088 List<String> constants = constantMap[outputUnit]; |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 1054 prefix = import.prefix.name, | 1112 prefix = import.prefix.name, |
| 1055 _importingLibrary = importingLibrary; | 1113 _importingLibrary = importingLibrary; |
| 1056 | 1114 |
| 1057 String get importingLibraryName { | 1115 String get importingLibraryName { |
| 1058 return _importingLibrary.hasLibraryName | 1116 return _importingLibrary.hasLibraryName |
| 1059 ? _importingLibrary.libraryName | 1117 ? _importingLibrary.libraryName |
| 1060 : "<unnamed>"; | 1118 : "<unnamed>"; |
| 1061 } | 1119 } |
| 1062 } | 1120 } |
| 1063 | 1121 |
| 1064 /// A node in the deferred import graph. | 1122 /// A compact lattice representation of import sets and subsets. |
| 1065 /// | 1123 /// |
| 1066 /// This class serves as the root node; the 'import' of the main library. | 1124 /// We use a graph of nodes to represent elements of the lattice, but only |
| 1067 class _DeferredImport { | 1125 /// create new nodes on-demand as they are needed by the deferred loading |
| 1068 const _DeferredImport(); | 1126 /// algorithm. |
| 1069 | 1127 /// |
| 1070 /// Computes a suggestive name for this import. | 1128 /// The constructions of nodes is carefully done by storing imports in a |
| 1071 String computeImportDeferName(Compiler compiler) => 'main'; | 1129 /// specific order. This ensures that we have a unique and canonical |
| 1072 | 1130 /// representation for each subset. |
| 1073 ImportElement get declaration => null; | 1131 class ImportSetLattice { |
| 1074 | 1132 /// The set of all deferred imports seen in the program, in a canonical order. |
| 1075 String toString() => 'main'; | 1133 List<ImportElement> _allDeferredImports = []; |
| 1076 } | 1134 |
| 1077 | 1135 /// The index corresponding to a deferred import according to the canonical |
| 1078 /// A node in the deferred import graph defined by a deferred import directive. | 1136 /// order. |
| 1079 class _DeclaredDeferredImport implements _DeferredImport { | 1137 /// Invariant: `_allDeferredImports[_importIndex[i]] == i`. |
| 1080 final ImportElement declaration; | 1138 Map<ImportElement, int> _importIndex = {}; |
| 1081 | 1139 |
| 1082 _DeclaredDeferredImport(this.declaration); | 1140 /// The empty import set. This corresponds to the main output unit. |
| 1141 ImportSet emptySet = new ImportSet(); | |
| 1142 | |
| 1143 /// Get the singleton import set that only contains [import]. | |
| 1144 ImportSet singleton(ImportElement import) => _add(emptySet, import); | |
| 1145 | |
| 1146 /// Get the import set that includes all imports in [importSet] and [import]. | |
| 1147 ImportSet _add(ImportSet importSet, ImportElement import) { | |
| 1148 if (importSet._imports.contains(import)) return importSet; | |
| 1149 var index = _indexOf(import); | |
| 1150 var otherImportAdded = false; | |
| 1151 var result = emptySet; | |
| 1152 for (var existing in importSet._imports) { | |
|
sra1
2017/08/18 23:42:28
This loop inside the merge loop in union makes uni
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Doh! - and good catch!
I meant to use result._add
| |
| 1153 if (!otherImportAdded && _indexOf(existing) > index) { | |
| 1154 result = result._add(import); | |
| 1155 otherImportAdded = true; | |
| 1156 } | |
| 1157 result = result._add(existing); | |
| 1158 } | |
| 1159 if (!otherImportAdded) { | |
| 1160 result = result._add(import); | |
| 1161 } | |
| 1162 // TODO(sigmund): use _neighbors to cache transitions | |
| 1163 // importSet._neighbors[import] = result; | |
| 1164 return result; | |
| 1165 } | |
| 1166 | |
| 1167 /// Get the import set that includes the union of [a] and [b]. | |
| 1168 ImportSet union(ImportSet a, ImportSet b) { | |
| 1169 if (a == null || a == emptySet) return b; | |
| 1170 if (b == null || b == emptySet) return a; | |
| 1171 | |
| 1172 // We create the union by merging the imports in canonical order first, and | |
| 1173 // then getting (or creating) the canonical sets by adding an import at a | |
| 1174 // time. | |
| 1175 List<ImportElement> aImports = a._imports.toList(); | |
|
sra1
2017/08/18 23:42:28
Could also use iterators over the sets.
Siggi Cherem (dart-lang)
2017/08/28 16:57:15
thanks nice idea. After splitting import-set and o
| |
| 1176 List<ImportElement> bImports = b._imports.toList(); | |
| 1177 int i = 0, j = 0; | |
| 1178 int lastAIndex = 0, lastBIndex = 0; | |
| 1179 var result = emptySet; | |
| 1180 while (i < aImports.length && j < bImports.length) { | |
| 1181 var importA = aImports[i]; | |
| 1182 var importB = bImports[j]; | |
| 1183 var aIndex = _indexOf(importA); | |
| 1184 var bIndex = _indexOf(importB); | |
| 1185 assert(lastAIndex <= aIndex); | |
| 1186 assert(lastBIndex <= bIndex); | |
| 1187 if (aIndex == bIndex) { | |
|
sra1
2017/08/18 23:42:29
This case can be avoided with a self-edge (see bel
Siggi Cherem (dart-lang)
2017/08/28 16:57:15
Done.
| |
| 1188 result = _add(result, importA); | |
| 1189 i++; | |
| 1190 j++; | |
| 1191 } else if (aIndex < bIndex) { | |
| 1192 result = _add(result, importA); | |
|
sra1
2017/08/18 23:42:29
Why is this not the following?
result = result
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
yes! indeed. good catch
| |
| 1193 i++; | |
| 1194 } else { | |
| 1195 result = _add(result, importB); | |
| 1196 j++; | |
| 1197 } | |
| 1198 } | |
| 1199 for (; i < aImports.length; i++) { | |
| 1200 result = _add(result, aImports[i]); | |
| 1201 } | |
| 1202 for (; j < bImports.length; j++) { | |
| 1203 result = _add(result, bImports[j]); | |
| 1204 } | |
| 1205 return result; | |
| 1206 } | |
| 1207 | |
| 1208 /// Get the index for an [import] according to the canonical order. | |
| 1209 int _indexOf(ImportElement import) { | |
| 1210 var index = _importIndex[import]; | |
|
sra1
2017/08/18 23:42:29
I think is is worth keeping _DeferredImport as a p
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Thanks, I like how this simplified things.
I chan
| |
| 1211 if (index == null) { | |
| 1212 index = _importIndex[import] = _allDeferredImports.length; | |
| 1213 _allDeferredImports.add(import); | |
| 1214 } | |
| 1215 return index; | |
| 1216 } | |
| 1217 } | |
| 1218 | |
| 1219 /// A canonical set of deferred imports. | |
| 1220 class ImportSet implements OutputUnit { | |
|
sra1
2017/08/18 23:42:29
I'd rather ImportSet and OutputUnit be separate, w
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Done.
However, I don't have the edge from OutputU
| |
| 1221 /// Imports that are part of this set. | |
| 1222 /// | |
| 1223 /// Invariant: this is a LinkedHashSet and the order in which elements are | |
| 1224 /// added must respect the canonical order. | |
| 1225 final Set<ImportElement> _imports = new Set(); | |
|
sra1
2017/08/18 23:42:28
<ImportElement>
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Done.
| |
| 1226 final Map<ImportElement, ImportSet> _neighbors = {}; | |
| 1083 | 1227 |
| 1084 @override | 1228 @override |
| 1085 String computeImportDeferName(Compiler compiler) { | 1229 bool get isMainOutput => _imports.isEmpty; |
|
sra1
2017/08/18 23:42:28
This is called a lot.
I wonder if the old way of h
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Done with the split of ImportSet and output unit
| |
| 1086 String result; | 1230 |
| 1087 if (declaration.isDeferred) { | 1231 @override |
| 1088 if (declaration.prefix != null) { | 1232 String name; |
| 1089 result = declaration.prefix.name; | 1233 |
| 1090 } else { | 1234 int get size => _imports.length; |
|
sra1
2017/08/18 23:42:28
This name (or 'length') makes sense for an ImportS
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Done.
| |
| 1091 // This happens when the deferred import isn't declared with a prefix. | 1235 |
| 1092 assert(compiler.compilationFailed); | 1236 /// Create an import set that adds [import] to all the imports on this set. |
| 1093 result = ''; | 1237 /// This assumes that import's canonical order comes after all imports in |
| 1238 /// this current set. This should only be called from [ImportSetLattice], | |
| 1239 /// since it is where we preserve this invariant. | |
| 1240 ImportSet _add(ImportElement import) { | |
| 1241 return _neighbors.putIfAbsent(import, () { | |
| 1242 var result = new ImportSet(); | |
| 1243 result._imports.addAll(_imports); | |
| 1244 result._imports.add(import); | |
|
sra1
2017/08/18 23:42:28
Adding a self edge allows the 'already-a-member' c
Siggi Cherem (dart-lang)
2017/08/28 16:57:15
Done.
| |
| 1245 return result; | |
| 1246 }); | |
| 1247 } | |
| 1248 | |
| 1249 String toString() { | |
| 1250 StringBuffer sb = new StringBuffer(); | |
| 1251 sb.write('ImportSet(size: $size, '); | |
| 1252 for (var import in _imports) { | |
| 1253 sb.write('${import.prefix} '); | |
| 1254 } | |
| 1255 sb.write(')'); | |
| 1256 return '$sb'; | |
| 1257 } | |
| 1258 } | |
| 1259 | |
| 1260 /// The algorithm work queue. | |
| 1261 class WorkQueue { | |
| 1262 Map<Entity, WorkItem> pendingElements = {}; | |
|
sra1
2017/08/18 23:42:29
Types on map literals.
sra1
2017/08/18 23:42:29
final?
Siggi Cherem (dart-lang)
2017/08/28 16:57:14
Added, but note they are not needed in the short f
Siggi Cherem (dart-lang)
2017/08/28 16:57:15
Done.
| |
| 1263 Map<ConstantValue, WorkItem> pendingConstants = {}; | |
| 1264 Queue<WorkItem> queue = new Queue<WorkItem>(); | |
| 1265 | |
| 1266 ImportSetLattice importSets; | |
| 1267 | |
| 1268 WorkQueue(this.importSets); | |
| 1269 | |
| 1270 bool get isEmpty => queue.isEmpty; | |
| 1271 | |
| 1272 WorkItem nextItem() { | |
| 1273 var item = queue.removeFirst(); | |
| 1274 if (item.element != null) pendingElements.remove(item.element); | |
| 1275 if (item.value != null) pendingConstants.remove(item.value); | |
| 1276 return item; | |
| 1277 } | |
| 1278 | |
| 1279 addElement(Entity element, ImportSet importSet, {isMirrorsUsage: false}) { | |
| 1280 var item = pendingElements[element]; | |
| 1281 if (item == null) { | |
| 1282 item = new WorkItem(element, importSet); | |
| 1283 pendingElements[element] = item; | |
| 1284 queue.add(item); | |
| 1285 } else { | |
| 1286 item.newSet = importSets.union(item.newSet, importSet); | |
| 1287 } | |
| 1288 if (isMirrorsUsage) item.isMirrorsUsage = true; | |
| 1289 } | |
| 1290 | |
| 1291 addConstant(ConstantValue constant, ImportSet importSet) { | |
| 1292 var item = pendingConstants[constant]; | |
| 1293 if (item == null) { | |
| 1294 item = new WorkItem.constant(constant, importSet); | |
| 1295 pendingConstants[constant] = item; | |
| 1296 queue.add(item); | |
| 1297 } else { | |
| 1298 item.newSet = importSets.union(item.newSet, importSet); | |
| 1299 } | |
| 1300 } | |
| 1301 } | |
| 1302 | |
| 1303 class WorkItem { | |
| 1304 final Entity element; | |
| 1305 final ConstantValue value; | |
| 1306 | |
| 1307 ImportSet newSet; | |
| 1308 bool isMirrorsUsage = false; | |
| 1309 | |
| 1310 WorkItem(this.element, this.newSet) : value = null; | |
| 1311 WorkItem.constant(this.value, this.newSet) : element = null; | |
| 1312 } | |
| 1313 | |
| 1314 /// Returns a name for a deferred import. | |
| 1315 // TODO(sigmund): delete support for the old annotation-style syntax. | |
| 1316 String _computeImportDeferName(ImportElement declaration, Compiler compiler) { | |
| 1317 String result; | |
| 1318 if (declaration.isDeferred) { | |
| 1319 if (declaration.prefix != null) { | |
| 1320 result = declaration.prefix.name; | |
| 1321 } else { | |
| 1322 // This happens when the deferred import isn't declared with a prefix. | |
| 1323 assert(compiler.compilationFailed); | |
| 1324 result = ''; | |
| 1325 } | |
| 1326 } else { | |
| 1327 // Finds the first argument to the [DeferredLibrary] annotation | |
| 1328 List<MetadataAnnotation> metadatas = declaration.metadata; | |
| 1329 assert(metadatas != null); | |
| 1330 for (MetadataAnnotation metadata in metadatas) { | |
| 1331 metadata.ensureResolved(compiler.resolution); | |
| 1332 ConstantValue value = | |
| 1333 compiler.constants.getConstantValue(metadata.constant); | |
| 1334 ResolutionDartType type = | |
| 1335 value.getType(compiler.resolution.commonElements); | |
| 1336 Element element = type.element; | |
| 1337 if (element == compiler.resolution.commonElements.deferredLibraryClass) { | |
| 1338 ConstructedConstantValue constant = value; | |
| 1339 StringConstantValue s = constant.fields.values.single; | |
| 1340 result = s.primitiveValue; | |
| 1341 break; | |
| 1094 } | 1342 } |
| 1095 } else { | 1343 } |
| 1096 // Finds the first argument to the [DeferredLibrary] annotation | 1344 } |
| 1097 List<MetadataAnnotation> metadatas = declaration.metadata; | 1345 assert(result != null); |
| 1098 assert(metadatas != null); | 1346 return result; |
| 1099 for (MetadataAnnotation metadata in metadatas) { | 1347 } |
| 1100 metadata.ensureResolved(compiler.resolution); | |
| 1101 ConstantValue value = | |
| 1102 compiler.constants.getConstantValue(metadata.constant); | |
| 1103 ResolutionDartType type = | |
| 1104 value.getType(compiler.resolution.commonElements); | |
| 1105 Element element = type.element; | |
| 1106 if (element == | |
| 1107 compiler.resolution.commonElements.deferredLibraryClass) { | |
| 1108 ConstructedConstantValue constant = value; | |
| 1109 StringConstantValue s = constant.fields.values.single; | |
| 1110 result = s.primitiveValue; | |
| 1111 break; | |
| 1112 } | |
| 1113 } | |
| 1114 } | |
| 1115 assert(result != null); | |
| 1116 return result; | |
| 1117 } | |
| 1118 | |
| 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 |