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 'dart2jslib.dart' show | 7 import 'dart2jslib.dart' show |
| 8 Compiler, | 8 Compiler, |
| 9 CompilerTask, | 9 CompilerTask, |
| 10 Constant, | 10 Constant, |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 107 /// program. | 107 /// program. |
| 108 final Set<OutputUnit> allOutputUnits = new Set<OutputUnit>(); | 108 final Set<OutputUnit> allOutputUnits = new Set<OutputUnit>(); |
| 109 | 109 |
| 110 /// Will be `true` if the program contains deferred libraries. | 110 /// Will be `true` if the program contains deferred libraries. |
| 111 bool splitProgram = false; | 111 bool splitProgram = false; |
| 112 | 112 |
| 113 /// A mapping from the name of a [DeferredLibrary] annotation to all dependent | 113 /// A mapping from the name of a [DeferredLibrary] annotation to all dependent |
| 114 /// output units. | 114 /// output units. |
| 115 final Map<String, Set<OutputUnit>> hunksToLoad = | 115 final Map<String, Set<OutputUnit>> hunksToLoad = |
| 116 new Map<String, Set<OutputUnit>>(); | 116 new Map<String, Set<OutputUnit>>(); |
| 117 final Map<Import, String> importDeferName = new Map<Import, String>(); | |
| 117 | 118 |
| 118 /// A mapping from elements and constants to their output unit. Query this via | 119 /// A mapping from elements and constants to their output unit. Query this via |
| 119 /// [outputUnitForElement] | 120 /// [outputUnitForElement] |
| 120 final Map<Element, OutputUnit> _elementToOutputUnit = | 121 final Map<Element, OutputUnit> _elementToOutputUnit = |
| 121 new Map<Element, OutputUnit>(); | 122 new Map<Element, OutputUnit>(); |
| 122 | 123 |
| 123 /// A mapping from constants to their output unit. Query this via | 124 /// A mapping from constants to their output unit. Query this via |
| 124 /// [outputUnitForConstant] | 125 /// [outputUnitForConstant] |
| 125 final Map<Constant, OutputUnit> _constantToOutputUnit = | 126 final Map<Constant, OutputUnit> _constantToOutputUnit = |
| 126 new Map<Constant, OutputUnit>(); | 127 new Map<Constant, OutputUnit>(); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 | 191 |
| 191 /// Answers whether the [import] has a [DeferredLibrary] annotation. | 192 /// Answers whether the [import] has a [DeferredLibrary] annotation. |
| 192 bool _isImportDeferred(Import import) { | 193 bool _isImportDeferred(Import import) { |
| 193 return _allDeferredImports.containsKey(import); | 194 return _allDeferredImports.containsKey(import); |
| 194 } | 195 } |
| 195 | 196 |
| 196 /// Checks whether the [import] has a [DeferredLibrary] annotation and stores | 197 /// Checks whether the [import] has a [DeferredLibrary] annotation and stores |
| 197 /// the information in [_allDeferredImports] and on the corresponding | 198 /// the information in [_allDeferredImports] and on the corresponding |
| 198 /// prefixElement. | 199 /// prefixElement. |
| 199 void _markIfDeferred(Import import, LibraryElement library) { | 200 void _markIfDeferred(Import import, LibraryElement library) { |
| 201 // Check if the import is deferred by a keyword. | |
| 202 if (import.isDeferred) { | |
| 203 _allDeferredImports[import] = library.getLibraryFromTag(import); | |
| 204 return; | |
| 205 } | |
| 206 // Check if the import is deferred by a metadata annotation. | |
| 200 Link<MetadataAnnotation> metadataList = import.metadata; | 207 Link<MetadataAnnotation> metadataList = import.metadata; |
| 201 if (metadataList == null) return; | 208 if (metadataList == null) return; |
| 202 for (MetadataAnnotation metadata in metadataList) { | 209 for (MetadataAnnotation metadata in metadataList) { |
| 203 metadata.ensureResolved(compiler); | 210 metadata.ensureResolved(compiler); |
| 204 Element element = metadata.value.computeType(compiler).element; | 211 Element element = metadata.value.computeType(compiler).element; |
| 205 if (element == deferredLibraryClass) { | 212 if (element == deferredLibraryClass) { |
| 206 _allDeferredImports[import] = library.getLibraryFromTag(import); | 213 _allDeferredImports[import] = library.getLibraryFromTag(import); |
| 207 // On encountering a deferred library without a prefix we report an | 214 // On encountering a deferred library without a prefix we report an |
| 208 // error, but continue the compilation to possibly give more | 215 // error, but continue the compilation to possibly give more |
| 209 // information. Therefore it is neccessary to check if there is a prefix | 216 // information. Therefore it is neccessary to check if there is a prefix |
| 210 // here. | 217 // here. |
| 211 Element maybePrefix = library.find(import.prefix.toString()); | 218 Element maybePrefix = library.find(import.prefix.toString()); |
| 212 if (maybePrefix != null && maybePrefix.isPrefix()) { | 219 if (maybePrefix != null && maybePrefix.isPrefix()) { |
| 213 PrefixElement prefix = maybePrefix; | 220 PrefixElement prefix = maybePrefix; |
| 214 prefix.markAsDeferred(); | 221 prefix.markAsDeferred(import); |
| 215 } | 222 } |
| 216 } | 223 } |
| 217 } | 224 } |
| 218 } | 225 } |
| 219 | 226 |
| 220 /// Answers whether [element] is explicitly deferred when referred to from | 227 /// Answers whether [element] is explicitly deferred when referred to from |
| 221 /// [library]. | 228 /// [library]. |
| 222 bool _isExplicitlyDeferred(Element element, LibraryElement library) { | 229 bool _isExplicitlyDeferred(Element element, LibraryElement library) { |
| 223 Link<Import> imports = _getImports(element, library); | 230 Link<Import> imports = _getImports(element, library); |
| 224 // If the element is not imported explicitly, it is implicitly imported | 231 // If the element is not imported explicitly, it is implicitly imported |
| (...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 522 // unit of the constant, but we prefer separate instances. | 529 // unit of the constant, but we prefer separate instances. |
| 523 // Replace the imports of the constant to match the ones of the class. | 530 // Replace the imports of the constant to match the ones of the class. |
| 524 _replaceOutputUnitImports(constant, constantUnit, classImports); | 531 _replaceOutputUnitImports(constant, constantUnit, classImports); |
| 525 } | 532 } |
| 526 } | 533 } |
| 527 | 534 |
| 528 /// Computes a unique string for the name field for each outputUnit. | 535 /// Computes a unique string for the name field for each outputUnit. |
| 529 /// | 536 /// |
| 530 /// Also sets up the [hunksToLoad] mapping. | 537 /// Also sets up the [hunksToLoad] mapping. |
| 531 void _assignNamesToOutputUnits(Set<OutputUnit> allOutputUnits) { | 538 void _assignNamesToOutputUnits(Set<OutputUnit> allOutputUnits) { |
| 532 Map<Import, String> deferNameCache = new Map<Import, String>(); | 539 Set<String> usedImportNames = new Set<String>(); |
| 540 | |
| 541 // Returns suggestedName if it is not in usedNames. Otherwise concatenate | |
|
floitsch
2014/03/17 14:15:41
concatenates
sigurdm
2014/03/19 12:46:30
Done.
| |
| 542 // the smallest number that makes it not appear in usedNames. | |
| 543 // Adds the result to usedNames. | |
| 544 String makeUnique(String suggestedName, Set<String> usedNames) { | |
| 545 String result = suggestedName; | |
| 546 if (usedNames.contains(suggestedName)) { | |
| 547 int counter = 0; | |
| 548 while (usedNames.contains(result)) { | |
| 549 counter++; | |
| 550 result = "$suggestedName$counter"; | |
| 551 } | |
| 552 } | |
| 553 usedNames.add(result); | |
| 554 return result; | |
| 555 } | |
| 556 | |
| 533 // Finds the first argument to the [DeferredLibrary] annotation | 557 // Finds the first argument to the [DeferredLibrary] annotation |
| 534 String importDeferName(Import import) { | 558 void computeImportDeferName(Import import) { |
| 535 if (deferNameCache.containsKey(import)) return deferNameCache[import]; | |
| 536 if (import == _fakeMainImport) return "main"; | |
| 537 Link<MetadataAnnotation> metadatas = import.metadata; | |
| 538 assert(metadatas != null); | |
| 539 String result; | 559 String result; |
| 540 for (MetadataAnnotation metadata in metadatas) { | 560 if (import == _fakeMainImport) { |
| 541 metadata.ensureResolved(compiler); | 561 result = "main"; |
| 542 Element element = metadata.value.computeType(compiler).element; | 562 } else if (import.isDeferred) { |
| 543 if (metadata.value.computeType(compiler).element == | 563 result = import.prefix.toString(); |
| 544 deferredLibraryClass) { | 564 } else { |
| 545 ConstructedConstant constant = metadata.value; | 565 Link<MetadataAnnotation> metadatas = import.metadata; |
| 546 StringConstant s = constant.fields[0]; | 566 assert(metadatas != null); |
| 547 result = s.value.slowToString(); | 567 for (MetadataAnnotation metadata in metadatas) { |
| 548 break; | 568 metadata.ensureResolved(compiler); |
| 569 Element element = metadata.value.computeType(compiler).element; | |
| 570 if (metadata.value.computeType(compiler).element == | |
| 571 deferredLibraryClass) { | |
| 572 ConstructedConstant constant = metadata.value; | |
| 573 StringConstant s = constant.fields[0]; | |
| 574 result = s.value.slowToString(); | |
| 575 break; | |
| 576 } | |
| 549 } | 577 } |
| 550 } | 578 } |
| 551 assert(result != null); | 579 assert(result != null); |
| 552 deferNameCache[import] = result; | 580 importDeferName[import] = makeUnique(result, usedImportNames);; |
| 553 return result; | |
| 554 } | 581 } |
| 555 | 582 |
| 556 Set<String> usedNames = new Set<String>(); | 583 Set<String> usedOutputUnitNames = new Set<String>(); |
| 557 Map<OutputUnit, String> generatedNames = new Map<OutputUnit, String>(); | 584 Map<OutputUnit, String> generatedNames = new Map<OutputUnit, String>(); |
| 558 | 585 |
| 559 void computeOutputUnitName(OutputUnit outputUnit) { | 586 void computeOutputUnitName(OutputUnit outputUnit) { |
| 560 if (generatedNames[outputUnit] != null) return; | 587 if (generatedNames[outputUnit] != null) return; |
| 561 String suggestedName = outputUnit.imports.map((import) { | 588 String suggestedName = outputUnit.imports.map((import) { |
| 562 return importDeferName(import); | 589 return importDeferName[import]; |
| 563 }).join('_'); | 590 }).join('_'); |
| 564 if (!usedNames.contains(suggestedName)) { | 591 outputUnit.name = makeUnique(suggestedName, usedOutputUnitNames); |
| 565 outputUnit.name = suggestedName; | |
| 566 } else { | |
| 567 int counter = 0; | |
| 568 while (usedNames.contains("$suggestedName$counter")) { | |
| 569 counter++; | |
| 570 } | |
| 571 outputUnit.name = "$suggestedName$counter"; | |
| 572 } | |
| 573 generatedNames[outputUnit] = outputUnit.name; | 592 generatedNames[outputUnit] = outputUnit.name; |
| 574 } | 593 } |
| 575 | 594 |
| 595 for (Import import in _allDeferredImports.keys) { | |
| 596 computeImportDeferName(import); | |
| 597 } | |
| 598 | |
| 576 for (OutputUnit outputUnit in allOutputUnits) { | 599 for (OutputUnit outputUnit in allOutputUnits) { |
| 577 computeOutputUnitName(outputUnit); | 600 computeOutputUnitName(outputUnit); |
| 578 } | 601 } |
| 579 | 602 |
| 580 // For each deferred import we find out which outputUnits to load. | 603 // For each deferred import we find out which outputUnits to load. |
| 581 for (Import import in _allDeferredImports.keys) { | 604 for (Import import in _allDeferredImports.keys) { |
| 582 if (import == _fakeMainImport) continue; | 605 if (import == _fakeMainImport) continue; |
| 583 hunksToLoad[importDeferName(import)] = new Set<OutputUnit>(); | 606 hunksToLoad[importDeferName[import]] = new Set<OutputUnit>(); |
| 584 for (OutputUnit outputUnit in allOutputUnits) { | 607 for (OutputUnit outputUnit in allOutputUnits) { |
| 585 if (outputUnit == mainOutputUnit) continue; | 608 if (outputUnit == mainOutputUnit) continue; |
| 586 if (outputUnit.imports.contains(import)) { | 609 if (outputUnit.imports.contains(import)) { |
| 587 hunksToLoad[importDeferName(import)].add(outputUnit); | 610 hunksToLoad[importDeferName[import]].add(outputUnit); |
| 588 } | 611 } |
| 589 } | 612 } |
| 590 } | 613 } |
| 591 } | 614 } |
| 592 | 615 |
| 593 void onResolutionComplete(FunctionElement main) { | 616 void onResolutionComplete(FunctionElement main) { |
| 594 if (!splitProgram) { | 617 if (!splitProgram) { |
| 595 allOutputUnits.add(mainOutputUnit); | 618 allOutputUnits.add(mainOutputUnit); |
| 596 return; | 619 return; |
| 597 } | 620 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 650 _assignNamesToOutputUnits(allOutputUnits); | 673 _assignNamesToOutputUnits(allOutputUnits); |
| 651 }); | 674 }); |
| 652 } | 675 } |
| 653 | 676 |
| 654 void ensureMetadataResolved(Compiler compiler) { | 677 void ensureMetadataResolved(Compiler compiler) { |
| 655 _allDeferredImports[_fakeMainImport] = compiler.mainApp; | 678 _allDeferredImports[_fakeMainImport] = compiler.mainApp; |
| 656 var lastDeferred; | 679 var lastDeferred; |
| 657 // When detecting duplicate prefixes of deferred libraries there are 4 | 680 // When detecting duplicate prefixes of deferred libraries there are 4 |
| 658 // cases of duplicate prefixes: | 681 // cases of duplicate prefixes: |
| 659 // 1. | 682 // 1. |
| 660 // @DeferredLibrary("a") import "lib.dart" as a; | 683 // import "lib.dart" deferred as a; |
| 661 // @DeferredLibrary("b") import "lib2.dart" as a; | 684 // import "lib2.dart" deferred as a; |
| 662 // 2. | 685 // 2. |
| 663 // @DeferredLibrary("a") import "lib.dart" as a; | 686 // import "lib.dart" deferred as a; |
| 664 // import "lib2.dart" as a; | 687 // import "lib2.dart" as a; |
| 665 // 3. | 688 // 3. |
| 666 // import "lib.dart" as a; | 689 // import "lib.dart" as a; |
| 667 // @DeferredLibrary("a") import "lib2.dart" as a; | 690 // import "lib2.dart" deferred as a; |
| 668 // 4. | 691 // 4. |
| 669 // import "lib.dart" as a; | 692 // import "lib.dart" as a; |
| 670 // import "lib2.dart" as a; | 693 // import "lib2.dart" as a; |
| 671 // We must be able to signal error for case 1, 2, 3, but accept case 4. | 694 // We must be able to signal error for case 1, 2, 3, but accept case 4. |
| 672 | 695 |
| 673 // The prefixes that have been used by any imports in this library. | 696 // The prefixes that have been used by any imports in this library. |
| 674 Setlet<String> usedPrefixes = new Setlet<String>(); | 697 Setlet<String> usedPrefixes = new Setlet<String>(); |
| 675 // The last deferred import we saw with a given prefix (if any). | 698 // The last deferred import we saw with a given prefix (if any). |
| 676 Map<String, Import> prefixDeferredImport = new Map<String, Import>(); | 699 Map<String, Import> prefixDeferredImport = new Map<String, Import>(); |
| 677 for (LibraryElement library in compiler.libraries.values) { | 700 for (LibraryElement library in compiler.libraries.values) { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 691 Import previousDeferredImport = prefixDeferredImport[prefix]; | 714 Import previousDeferredImport = prefixDeferredImport[prefix]; |
| 692 bool isDeferred = _isImportDeferred(import); | 715 bool isDeferred = _isImportDeferred(import); |
| 693 if (isDeferred) { | 716 if (isDeferred) { |
| 694 if (prefix == null) { | 717 if (prefix == null) { |
| 695 compiler.reportError(import, | 718 compiler.reportError(import, |
| 696 MessageKind.DEFERRED_LIBRARY_WITHOUT_PREFIX); | 719 MessageKind.DEFERRED_LIBRARY_WITHOUT_PREFIX); |
| 697 } else { | 720 } else { |
| 698 prefixDeferredImport[prefix] = import; | 721 prefixDeferredImport[prefix] = import; |
| 699 } | 722 } |
| 700 splitProgram = true; | 723 splitProgram = true; |
| 701 lastDeferred = import.metadata.first; | 724 lastDeferred = import; |
| 702 } | 725 } |
| 703 if (prefix != null) { | 726 if (prefix != null) { |
| 704 if (previousDeferredImport != null || | 727 if (previousDeferredImport != null || |
| 705 (isDeferred && usedPrefixes.contains(prefix))) { | 728 (isDeferred && usedPrefixes.contains(prefix))) { |
| 706 Import failingImport = (previousDeferredImport != null) | 729 Import failingImport = (previousDeferredImport != null) |
| 707 ? previousDeferredImport | 730 ? previousDeferredImport |
| 708 : import; | 731 : import; |
| 709 compiler.reportError(failingImport.prefix, | 732 compiler.reportError(failingImport.prefix, |
| 710 MessageKind.DEFERRED_LIBRARY_DUPLICATE_PREFIX); | 733 MessageKind.DEFERRED_LIBRARY_DUPLICATE_PREFIX); |
| 711 } | 734 } |
| 712 usedPrefixes.add(prefix); | 735 usedPrefixes.add(prefix); |
| 713 } | 736 } |
| 714 } | 737 } |
| 715 }); | 738 }); |
| 716 } | 739 } |
| 717 if (splitProgram && compiler.backend is DartBackend) { | 740 if (splitProgram && compiler.backend is DartBackend) { |
| 718 // TODO(sigurdm): Implement deferred loading for dart2dart. | 741 // TODO(sigurdm): Implement deferred loading for dart2dart. |
| 719 splitProgram = false; | 742 splitProgram = false; |
| 720 compiler.reportInfo( | 743 compiler.reportInfo( |
| 721 lastDeferred, | 744 lastDeferred, |
| 722 MessageKind.DEFERRED_LIBRARY_DART_2_DART); | 745 MessageKind.DEFERRED_LIBRARY_DART_2_DART); |
| 723 } | 746 } |
| 724 } | 747 } |
| 725 } | 748 } |
| OLD | NEW |