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