| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 part of dart2js; | 5 part of dart2js; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * [CompilerTask] for loading libraries and setting up the import/export scopes. | 8 * [CompilerTask] for loading libraries and setting up the import/export scopes. |
| 9 * | 9 * |
| 10 * The library loader uses four different kinds of URIs in different parts of | 10 * The library loader uses four different kinds of URIs in different parts of |
| (...skipping 551 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 562 } | 562 } |
| 563 } | 563 } |
| 564 } | 564 } |
| 565 | 565 |
| 566 /** | 566 /** |
| 567 * The combinator filter computed from an export tag and the library dependency | 567 * The combinator filter computed from an export tag and the library dependency |
| 568 * node for the library that declared the export tag. This represents an edge in | 568 * node for the library that declared the export tag. This represents an edge in |
| 569 * the library dependency graph. | 569 * the library dependency graph. |
| 570 */ | 570 */ |
| 571 class ExportLink { | 571 class ExportLink { |
| 572 final Export export; |
| 572 final CombinatorFilter combinatorFilter; | 573 final CombinatorFilter combinatorFilter; |
| 573 final LibraryDependencyNode exportNode; | 574 final LibraryDependencyNode exportNode; |
| 574 | 575 |
| 575 ExportLink(Export export, LibraryDependencyNode this.exportNode) | 576 ExportLink(Export export, LibraryDependencyNode this.exportNode) |
| 576 : this.combinatorFilter = new CombinatorFilter.fromTag(export); | 577 : this.export = export, |
| 578 this.combinatorFilter = new CombinatorFilter.fromTag(export); |
| 577 | 579 |
| 578 /** | 580 /** |
| 579 * Exports [element] to the dependent library unless [element] is filtered by | 581 * Exports [element] to the dependent library unless [element] is filtered by |
| 580 * the export combinators. Returns [:true:] if the set pending exports of the | 582 * the export combinators. Returns [:true:] if the set pending exports of the |
| 581 * dependent library was modified. | 583 * dependent library was modified. |
| 582 */ | 584 */ |
| 583 bool exportElement(Element element) { | 585 bool exportElement(Element element) { |
| 584 if (combinatorFilter.exclude(element)) return false; | 586 if (combinatorFilter.exclude(element)) return false; |
| 585 return exportNode.addElementToPendingExports(element); | 587 return exportNode.addElementToPendingExports(element, export); |
| 586 } | 588 } |
| 587 } | 589 } |
| 588 | 590 |
| 589 /** | 591 /** |
| 590 * A node in the library dependency graph. | 592 * A node in the library dependency graph. |
| 591 * | 593 * |
| 592 * This class is used to collect the library dependencies expressed through | 594 * This class is used to collect the library dependencies expressed through |
| 593 * import and export tags, and as the work-list entry in computations of library | 595 * import and export tags, and as the work-list entry in computations of library |
| 594 * exports performed in [LibraryDependencyHandler.computeExports]. | 596 * exports performed in [LibraryDependencyHandler.computeExports]. |
| 595 */ | 597 */ |
| (...skipping 19 matching lines...) Expand all Loading... |
| 615 */ | 617 */ |
| 616 Link<ExportLink> dependencies = const Link<ExportLink>(); | 618 Link<ExportLink> dependencies = const Link<ExportLink>(); |
| 617 | 619 |
| 618 /** | 620 /** |
| 619 * The export scope for [library] which is gradually computed by the work-list | 621 * The export scope for [library] which is gradually computed by the work-list |
| 620 * computation in [LibraryDependencyHandler.computeExports]. | 622 * computation in [LibraryDependencyHandler.computeExports]. |
| 621 */ | 623 */ |
| 622 Map<SourceString, Element> exportScope = | 624 Map<SourceString, Element> exportScope = |
| 623 new LinkedHashMap<SourceString, Element>(); | 625 new LinkedHashMap<SourceString, Element>(); |
| 624 | 626 |
| 627 /// Map from exported elements to the export directives that exported them. |
| 628 Map<Element, Link<Export>> exporters = new Map<Element, Link<Export>>(); |
| 629 |
| 625 /** | 630 /** |
| 626 * The set of exported elements that need to be propageted to dependent | 631 * The set of exported elements that need to be propageted to dependent |
| 627 * libraries as part of the work-list computation performed in | 632 * libraries as part of the work-list computation performed in |
| 628 * [LibraryDependencyHandler.computeExports]. | 633 * [LibraryDependencyHandler.computeExports]. Each export element is mapped |
| 634 * to a list of exports directives that export it. |
| 629 */ | 635 */ |
| 630 Set<Element> pendingExportSet = new Set<Element>(); | 636 Map<Element, Link<Export>> pendingExportMap = |
| 637 new Map<Element, Link<Export>>(); |
| 631 | 638 |
| 632 LibraryDependencyNode(LibraryElement this.library); | 639 LibraryDependencyNode(LibraryElement this.library); |
| 633 | 640 |
| 634 /** | 641 /** |
| 635 * Registers that the library of this node imports [importLibrary] through the | 642 * Registers that the library of this node imports [importLibrary] through the |
| 636 * [import] tag. | 643 * [import] tag. |
| 637 */ | 644 */ |
| 638 void registerImportDependency(Import import, | 645 void registerImportDependency(Import import, |
| 639 LibraryElement importedLibrary) { | 646 LibraryElement importedLibrary) { |
| 640 imports = imports.prepend(new ImportLink(import, importedLibrary)); | 647 imports = imports.prepend(new ImportLink(import, importedLibrary)); |
| 641 } | 648 } |
| 642 | 649 |
| 643 /** | 650 /** |
| 644 * Registers that the library of this node is exported by | 651 * Registers that the library of this node is exported by |
| 645 * [exportingLibraryNode] through the [export] tag. | 652 * [exportingLibraryNode] through the [export] tag. |
| 646 */ | 653 */ |
| 647 void registerExportDependency(Export export, | 654 void registerExportDependency(Export export, |
| 648 LibraryDependencyNode exportingLibraryNode) { | 655 LibraryDependencyNode exportingLibraryNode) { |
| 649 dependencies = | 656 dependencies = |
| 650 dependencies.prepend(new ExportLink(export, exportingLibraryNode)); | 657 dependencies.prepend(new ExportLink(export, exportingLibraryNode)); |
| 651 } | 658 } |
| 652 | 659 |
| 653 /** | 660 /** |
| 654 * Registers all non-private locally declared members of the library of this | 661 * Registers all non-private locally declared members of the library of this |
| 655 * node to be exported. This forms the basis for the work-list computation of | 662 * node to be exported. This forms the basis for the work-list computation of |
| 656 * the export scopes performed in [LibraryDependencyHandler.computeExports]. | 663 * the export scopes performed in [LibraryDependencyHandler.computeExports]. |
| 657 */ | 664 */ |
| 658 void registerInitialExports() { | 665 void registerInitialExports() { |
| 659 pendingExportSet.addAll(library.getNonPrivateElementsInScope()); | 666 for (Element element in library.getNonPrivateElementsInScope()) { |
| 667 pendingExportMap[element] = const Link<Export>(); |
| 668 } |
| 660 } | 669 } |
| 661 | 670 |
| 662 void registerHandledExports(LibraryElement exportedLibraryElement, | 671 void registerHandledExports(LibraryElement exportedLibraryElement, |
| 672 Export export, |
| 663 CombinatorFilter filter) { | 673 CombinatorFilter filter) { |
| 664 assert(invariant(library, exportedLibraryElement.exportsHandled)); | 674 assert(invariant(library, exportedLibraryElement.exportsHandled)); |
| 665 for (Element exportedElement in exportedLibraryElement.exports) { | 675 for (Element exportedElement in exportedLibraryElement.exports) { |
| 666 if (!filter.exclude(exportedElement)) { | 676 if (!filter.exclude(exportedElement)) { |
| 667 pendingExportSet.add(exportedElement); | 677 Link<Export> exports = |
| 678 pendingExportMap.putIfAbsent(exportedElement, |
| 679 () => const Link<Export>()); |
| 680 pendingExportMap[exportedElement] = exports.prepend(export); |
| 668 } | 681 } |
| 669 } | 682 } |
| 670 } | 683 } |
| 671 | 684 |
| 672 /** | 685 /** |
| 673 * Registers the compute export scope with the node library. | 686 * Registers the compute export scope with the node library. |
| 674 */ | 687 */ |
| 675 void registerExports() { | 688 void registerExports() { |
| 676 library.setExports(exportScope.values.toList()); | 689 library.setExports(exportScope.values.toList()); |
| 677 } | 690 } |
| 678 | 691 |
| 679 /** | 692 /** |
| 680 * Registers the imports of the node library. | 693 * Registers the imports of the node library. |
| 681 */ | 694 */ |
| 682 void registerImports(Compiler compiler) { | 695 void registerImports(Compiler compiler) { |
| 683 for (ImportLink link in imports) { | 696 for (ImportLink link in imports) { |
| 684 link.importLibrary(compiler, library); | 697 link.importLibrary(compiler, library); |
| 685 } | 698 } |
| 686 } | 699 } |
| 687 | 700 |
| 688 /** | 701 /** |
| 689 * Copies and clears pending export set for this node. | 702 * Copies and clears pending export set for this node. |
| 690 */ | 703 */ |
| 691 List<Element> pullPendingExports() { | 704 Map<Element, Link<Export>> pullPendingExports() { |
| 692 List<Element> pendingExports = new List.from(pendingExportSet); | 705 Map<Element, Link<Export>> pendingExports = |
| 693 pendingExportSet.clear(); | 706 new Map<Element, Link<Export>>.from(pendingExportMap); |
| 707 pendingExportMap.clear(); |
| 694 return pendingExports; | 708 return pendingExports; |
| 695 } | 709 } |
| 696 | 710 |
| 697 /** | 711 /** |
| 698 * Adds [element] to the export scope for this node. If the [element] name | 712 * Adds [element] to the export scope for this node. If the [element] name |
| 699 * is a duplicate, an error element is inserted into the export scope. | 713 * is a duplicate, an error element is inserted into the export scope. |
| 700 */ | 714 */ |
| 701 Element addElementToExportScope(Compiler compiler, Element element) { | 715 Element addElementToExportScope(Compiler compiler, Element element, |
| 716 Link<Export> exports) { |
| 717 |
| 702 SourceString name = element.name; | 718 SourceString name = element.name; |
| 719 |
| 720 void reportDuplicateExport(Element duplicate, |
| 721 Link<Export> duplicateExports, |
| 722 {bool reportError: true}) { |
| 723 compiler.withCurrentElement(library, () { |
| 724 for (Export export in duplicateExports) { |
| 725 if (reportError) { |
| 726 compiler.reportError(export, |
| 727 MessageKind.DUPLICATE_EXPORT, {'name': name}); |
| 728 reportError = false; |
| 729 } else { |
| 730 compiler.reportInfo(export, |
| 731 MessageKind.DUPLICATE_EXPORT_CONT, {'name': name}); |
| 732 } |
| 733 } |
| 734 }); |
| 735 } |
| 736 |
| 737 void reportDuplicateExportDecl(Element duplicate, |
| 738 Link<Export> duplicateExports) { |
| 739 compiler.reportInfo(duplicate, MessageKind.DUPLICATE_EXPORT_DECL, |
| 740 {'name': name, 'uriString': duplicateExports.head.uri}); |
| 741 } |
| 742 |
| 703 Element existingElement = exportScope[name]; | 743 Element existingElement = exportScope[name]; |
| 704 if (existingElement != null) { | 744 if (existingElement != null && existingElement != element) { |
| 705 if (existingElement.isErroneous()) { | 745 if (existingElement.isErroneous()) { |
| 706 compiler.reportError(element, MessageKind.DUPLICATE_EXPORT, | 746 reportDuplicateExport(element, exports); |
| 707 {'name': name}); | 747 reportDuplicateExportDecl(element, exports); |
| 708 element = existingElement; | 748 element = existingElement; |
| 709 } else if (existingElement.getLibrary() != library) { | 749 } else if (existingElement.getLibrary() != library) { |
| 710 // Declared elements hide exported elements. | 750 // Declared elements hide exported elements. |
| 711 compiler.reportError(existingElement, MessageKind.DUPLICATE_EXPORT, | 751 Link<Export> existingExports = exporters[existingElement]; |
| 712 {'name': name}); | 752 reportDuplicateExport(existingElement, existingExports); |
| 713 compiler.reportError(element, MessageKind.DUPLICATE_EXPORT, | 753 reportDuplicateExport(element, exports, reportError: false); |
| 714 {'name': name}); | 754 reportDuplicateExportDecl(existingElement, existingExports); |
| 755 reportDuplicateExportDecl(element, exports); |
| 715 element = exportScope[name] = new ErroneousElementX( | 756 element = exportScope[name] = new ErroneousElementX( |
| 716 MessageKind.DUPLICATE_EXPORT, {'name': name}, name, library); | 757 MessageKind.DUPLICATE_EXPORT, {'name': name}, name, library); |
| 717 } | 758 } |
| 718 } else { | 759 } else { |
| 719 exportScope[name] = element; | 760 exportScope[name] = element; |
| 761 exporters[element] = exports; |
| 720 } | 762 } |
| 721 return element; | 763 return element; |
| 722 } | 764 } |
| 723 | 765 |
| 724 /** | 766 /** |
| 725 * Propagates the exported [element] to all library nodes that depend upon | 767 * Propagates the exported [element] to all library nodes that depend upon |
| 726 * this node. If the propagation updated any pending exports, [:true:] is | 768 * this node. If the propagation updated any pending exports, [:true:] is |
| 727 * returned. | 769 * returned. |
| 728 */ | 770 */ |
| 729 bool propagateElement(Element element) { | 771 bool propagateElement(Element element) { |
| 730 bool change = false; | 772 bool change = false; |
| 731 for (ExportLink link in dependencies) { | 773 for (ExportLink link in dependencies) { |
| 732 if (link.exportElement(element)) { | 774 if (link.exportElement(element)) { |
| 733 change = true; | 775 change = true; |
| 734 } | 776 } |
| 735 } | 777 } |
| 736 return change; | 778 return change; |
| 737 } | 779 } |
| 738 | 780 |
| 739 /** | 781 /** |
| 740 * Adds [element] to the pending exports of this node and returns [:true:] if | 782 * Adds [element] to the pending exports of this node and returns [:true:] if |
| 741 * the pending export set was modified. The combinators of [export] are used | 783 * the pending export set was modified. The combinators of [export] are used |
| 742 * to filter the element. | 784 * to filter the element. |
| 743 */ | 785 */ |
| 744 bool addElementToPendingExports(Element element) { | 786 bool addElementToPendingExports(Element element, Export export) { |
| 787 bool changed = false; |
| 745 if (!identical(exportScope[element.name], element)) { | 788 if (!identical(exportScope[element.name], element)) { |
| 746 if (!pendingExportSet.contains(element)) { | 789 Link<Export> exports = pendingExportMap.putIfAbsent(element, () { |
| 747 pendingExportSet.add(element); | 790 changed = true; |
| 748 return true; | 791 return const Link<Export>(); |
| 749 } | 792 }); |
| 793 pendingExportMap[element] = exports.prepend(export); |
| 750 } | 794 } |
| 751 return false; | 795 return changed; |
| 752 } | 796 } |
| 753 } | 797 } |
| 754 | 798 |
| 755 /** | 799 /** |
| 756 * Helper class used for computing the possibly cyclic import/export scopes of | 800 * Helper class used for computing the possibly cyclic import/export scopes of |
| 757 * a set of libraries. | 801 * a set of libraries. |
| 758 * | 802 * |
| 759 * This class is used by [ScannerTask.scanLibrary] to collect all newly loaded | 803 * This class is used by [ScannerTask.scanLibrary] to collect all newly loaded |
| 760 * libraries and to compute their import/export scopes through a fixed-point | 804 * libraries and to compute their import/export scopes through a fixed-point |
| 761 * algorithm. | 805 * algorithm. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 776 | 820 |
| 777 /** | 821 /** |
| 778 * Performs a fixed-point computation on the export scopes of all registered | 822 * Performs a fixed-point computation on the export scopes of all registered |
| 779 * libraries and creates the import/export of the libraries based on the | 823 * libraries and creates the import/export of the libraries based on the |
| 780 * fixed-point. | 824 * fixed-point. |
| 781 */ | 825 */ |
| 782 void computeExports() { | 826 void computeExports() { |
| 783 bool changed = true; | 827 bool changed = true; |
| 784 while (changed) { | 828 while (changed) { |
| 785 changed = false; | 829 changed = false; |
| 786 Map<LibraryDependencyNode, List<Element>> tasks = | 830 Map<LibraryDependencyNode, Map<Element, Link<Export>>> tasks = |
| 787 new LinkedHashMap<LibraryDependencyNode, List<Element>>(); | 831 new Map<LibraryDependencyNode, Map<Element, Link<Export>>>(); |
| 788 | 832 |
| 789 // Locally defined elements take precedence over exported | 833 // Locally defined elements take precedence over exported |
| 790 // elements. So we must propagate local elements first. We | 834 // elements. So we must propagate local elements first. We |
| 791 // ensure this by pulling the pending exports before | 835 // ensure this by pulling the pending exports before |
| 792 // propagating. This enforces that we handle exports | 836 // propagating. This enforces that we handle exports |
| 793 // breadth-first, with locally defined elements being level 0. | 837 // breadth-first, with locally defined elements being level 0. |
| 794 nodeMap.forEach((_, LibraryDependencyNode node) { | 838 nodeMap.forEach((_, LibraryDependencyNode node) { |
| 795 List<Element> pendingExports = node.pullPendingExports(); | 839 Map<Element, Link<Export>> pendingExports = node.pullPendingExports(); |
| 796 tasks[node] = pendingExports; | 840 tasks[node] = pendingExports; |
| 797 }); | 841 }); |
| 798 tasks.forEach((LibraryDependencyNode node, List<Element> pendingExports) { | 842 tasks.forEach((LibraryDependencyNode node, |
| 799 pendingExports.forEach((Element element) { | 843 Map<Element, Link<Export>> pendingExports) { |
| 800 element = node.addElementToExportScope(compiler, element); | 844 pendingExports.forEach((Element element, Link<Export> exports) { |
| 845 element = node.addElementToExportScope(compiler, element, exports); |
| 801 if (node.propagateElement(element)) { | 846 if (node.propagateElement(element)) { |
| 802 changed = true; | 847 changed = true; |
| 803 } | 848 } |
| 804 }); | 849 }); |
| 805 }); | 850 }); |
| 806 } | 851 } |
| 807 | 852 |
| 808 // Setup export scopes. These have to be set before computing the import | 853 // Setup export scopes. These have to be set before computing the import |
| 809 // scopes to avoid accessing uncomputed export scopes during handling of | 854 // scopes to avoid accessing uncomputed export scopes during handling of |
| 810 // imports. | 855 // imports. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 826 LibraryElement loadedLibrary) { | 871 LibraryElement loadedLibrary) { |
| 827 if (tag != null) { | 872 if (tag != null) { |
| 828 library.recordResolvedTag(tag, loadedLibrary); | 873 library.recordResolvedTag(tag, loadedLibrary); |
| 829 } | 874 } |
| 830 if (tag is Export) { | 875 if (tag is Export) { |
| 831 // [loadedLibrary] is exported by [library]. | 876 // [loadedLibrary] is exported by [library]. |
| 832 LibraryDependencyNode exportingNode = nodeMap[library]; | 877 LibraryDependencyNode exportingNode = nodeMap[library]; |
| 833 if (loadedLibrary.exportsHandled) { | 878 if (loadedLibrary.exportsHandled) { |
| 834 // Export scope already computed on [loadedLibrary]. | 879 // Export scope already computed on [loadedLibrary]. |
| 835 var combinatorFilter = new CombinatorFilter.fromTag(tag); | 880 var combinatorFilter = new CombinatorFilter.fromTag(tag); |
| 836 exportingNode.registerHandledExports(loadedLibrary, combinatorFilter); | 881 exportingNode.registerHandledExports( |
| 882 loadedLibrary, tag, combinatorFilter); |
| 837 return; | 883 return; |
| 838 } | 884 } |
| 839 LibraryDependencyNode exportedNode = nodeMap[loadedLibrary]; | 885 LibraryDependencyNode exportedNode = nodeMap[loadedLibrary]; |
| 840 assert(invariant(loadedLibrary, exportedNode != null, | 886 assert(invariant(loadedLibrary, exportedNode != null, |
| 841 message: "$loadedLibrary has not been registered")); | 887 message: "$loadedLibrary has not been registered")); |
| 842 assert(invariant(library, exportingNode != null, | 888 assert(invariant(library, exportingNode != null, |
| 843 message: "$library has not been registered")); | 889 message: "$library has not been registered")); |
| 844 exportedNode.registerExportDependency(tag, exportingNode); | 890 exportedNode.registerExportDependency(tag, exportingNode); |
| 845 } else if (tag == null || tag is Import) { | 891 } else if (tag == null || tag is Import) { |
| 846 // [loadedLibrary] is imported by [library]. | 892 // [loadedLibrary] is imported by [library]. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 859 } | 905 } |
| 860 | 906 |
| 861 /** | 907 /** |
| 862 * Registers all top-level entities of [library] as starting point for the | 908 * Registers all top-level entities of [library] as starting point for the |
| 863 * fixed-point computation of the import/export scopes. | 909 * fixed-point computation of the import/export scopes. |
| 864 */ | 910 */ |
| 865 void registerLibraryExports(LibraryElement library) { | 911 void registerLibraryExports(LibraryElement library) { |
| 866 nodeMap[library].registerInitialExports(); | 912 nodeMap[library].registerInitialExports(); |
| 867 } | 913 } |
| 868 } | 914 } |
| OLD | NEW |