OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 /// Functions for asserting equivalence across serialization. | 5 /// Functions for asserting equivalence across serialization. |
6 | 6 |
7 library dart2js.serialization.equivalence; | 7 library dart2js.serialization.equivalence; |
8 | 8 |
9 import '../common/resolution.dart'; | 9 import '../common/resolution.dart'; |
10 import '../constants/expressions.dart'; | 10 import '../constants/expressions.dart'; |
11 import '../dart_types.dart'; | 11 import '../dart_types.dart'; |
12 import '../elements/elements.dart'; | 12 import '../elements/elements.dart'; |
13 import '../elements/visitor.dart'; | 13 import '../elements/visitor.dart'; |
| 14 import '../resolution/send_structure.dart'; |
| 15 import '../resolution/tree_elements.dart'; |
| 16 import '../tokens/token.dart'; |
| 17 import '../tree/nodes.dart'; |
14 import '../universe/selector.dart'; | 18 import '../universe/selector.dart'; |
15 import '../universe/use.dart'; | 19 import '../universe/use.dart'; |
| 20 import 'resolved_ast_serialization.dart'; |
| 21 |
16 | 22 |
17 /// Equality based equivalence function. | 23 /// Equality based equivalence function. |
18 bool equality(a, b) => a == b; | 24 bool equality(a, b) => a == b; |
19 | 25 |
20 /// Returns `true` if the elements in [a] and [b] are pair-wise equivalent | 26 /// Returns `true` if the elements in [a] and [b] are pair-wise equivalent |
21 /// according to [elementEquivalence]. | 27 /// according to [elementEquivalence]. |
22 bool areListsEquivalent( | 28 bool areListsEquivalent( |
23 List a, | 29 List a, |
24 List b, | 30 List b, |
25 [bool elementEquivalence(a, b) = equality]) { | 31 [bool elementEquivalence(a, b) = equality]) { |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 } | 94 } |
89 | 95 |
90 /// Returns `true` if the lists of constants, [a] and [b], are equivalent. | 96 /// Returns `true` if the lists of constants, [a] and [b], are equivalent. |
91 bool areConstantListsEquivalent(List<ConstantExpression> a, | 97 bool areConstantListsEquivalent(List<ConstantExpression> a, |
92 List<ConstantExpression> b) { | 98 List<ConstantExpression> b) { |
93 return areListsEquivalent(a, b, areConstantsEquivalent); | 99 return areListsEquivalent(a, b, areConstantsEquivalent); |
94 } | 100 } |
95 | 101 |
96 /// Returns `true` if the selectors [a] and [b] are equivalent. | 102 /// Returns `true` if the selectors [a] and [b] are equivalent. |
97 bool areSelectorsEquivalent(Selector a, Selector b) { | 103 bool areSelectorsEquivalent(Selector a, Selector b) { |
| 104 if (identical(a, b)) return true; |
| 105 if (a == null || b == null) return false; |
98 return a.kind == b.kind && | 106 return a.kind == b.kind && |
99 a.callStructure == b.callStructure && | 107 a.callStructure == b.callStructure && |
100 areNamesEquivalent(a.memberName, b.memberName); | 108 areNamesEquivalent(a.memberName, b.memberName); |
101 } | 109 } |
102 | 110 |
103 /// Returns `true` if the names [a] and [b] are equivalent. | 111 /// Returns `true` if the names [a] and [b] are equivalent. |
104 bool areNamesEquivalent(Name a, Name b) { | 112 bool areNamesEquivalent(Name a, Name b) { |
105 return a.text == b.text && | 113 return a.text == b.text && |
106 a.isSetter == b.isSetter && | 114 a.isSetter == b.isSetter && |
107 areElementsEquivalent(a.library, b.library); | 115 areElementsEquivalent(a.library, b.library); |
(...skipping 23 matching lines...) Expand all Loading... |
131 a.isEmpty == b.isEmpty; | 139 a.isEmpty == b.isEmpty; |
132 } | 140 } |
133 | 141 |
134 /// Returns `true` if the map literal uses [a] and [b] are equivalent. | 142 /// Returns `true` if the map literal uses [a] and [b] are equivalent. |
135 bool areMapLiteralUsesEquivalent(MapLiteralUse a, MapLiteralUse b) { | 143 bool areMapLiteralUsesEquivalent(MapLiteralUse a, MapLiteralUse b) { |
136 return areTypesEquivalent(a.type, b.type) && | 144 return areTypesEquivalent(a.type, b.type) && |
137 a.isConstant == b.isConstant && | 145 a.isConstant == b.isConstant && |
138 a.isEmpty == b.isEmpty; | 146 a.isEmpty == b.isEmpty; |
139 } | 147 } |
140 | 148 |
| 149 /// Returns `true` if the send structures [a] and [b] are equivalent. |
| 150 bool areSendStructuresEquivalent(SendStructure a, SendStructure b) { |
| 151 if (identical(a, b)) return true; |
| 152 if (a == null || b == null) return false; |
| 153 if (a.kind != b.kind) return false; |
| 154 // TODO(johnniwinther): Compute a deep equivalence. |
| 155 return true; |
| 156 } |
| 157 |
| 158 /// Returns `true` if the new structures [a] and [b] are equivalent. |
| 159 bool areNewStructuresEquivalent(NewStructure a, NewStructure b) { |
| 160 if (identical(a, b)) return true; |
| 161 if (a == null || b == null) return false; |
| 162 if (a.kind != b.kind) return false; |
| 163 // TODO(johnniwinther): Compute a deep equivalence. |
| 164 return true; |
| 165 } |
| 166 |
141 /// Strategy for testing equivalence. | 167 /// Strategy for testing equivalence. |
142 /// | 168 /// |
143 /// Use this strategy to determine equivalence without failing on inequivalence. | 169 /// Use this strategy to determine equivalence without failing on inequivalence. |
144 class TestStrategy { | 170 class TestStrategy { |
145 const TestStrategy(); | 171 const TestStrategy(); |
146 | 172 |
147 bool test(var object1, var object2, String property, var value1, var value2) { | 173 bool test(var object1, var object2, String property, var value1, var value2, |
148 return value1 == value2; | 174 [bool equivalence(a, b) = equality]) { |
| 175 return equivalence(value1, value2); |
149 } | 176 } |
150 | 177 |
151 bool testLists( | 178 bool testLists( |
152 Object object1, Object object2, String property, | 179 Object object1, Object object2, String property, |
153 List list1, List list2, | 180 List list1, List list2, |
154 [bool elementEquivalence(a, b) = equality]) { | 181 [bool elementEquivalence(a, b) = equality]) { |
155 return areListsEquivalent(list1, list2, elementEquivalence); | 182 return areListsEquivalent(list1, list2, elementEquivalence); |
156 } | 183 } |
157 | 184 |
158 bool testSets( | 185 bool testSets( |
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
548 | 575 |
549 @override | 576 @override |
550 bool visitPositional(PositionalArgumentReference exp1, | 577 bool visitPositional(PositionalArgumentReference exp1, |
551 PositionalArgumentReference exp2) { | 578 PositionalArgumentReference exp2) { |
552 return strategy.test(exp1, exp2, 'index', exp1.index, exp2.index); | 579 return strategy.test(exp1, exp2, 'index', exp1.index, exp2.index); |
553 } | 580 } |
554 | 581 |
555 @override | 582 @override |
556 bool visitSymbol(SymbolConstantExpression exp1, | 583 bool visitSymbol(SymbolConstantExpression exp1, |
557 SymbolConstantExpression exp2) { | 584 SymbolConstantExpression exp2) { |
558 // TODO: implement visitSymbol | 585 // TODO(johnniwinther): Handle private names. Currently not even supported |
559 return true; | 586 // in resolution. |
| 587 return strategy.test(exp1, exp2, 'name', exp1.name, exp2.name); |
560 } | 588 } |
561 | 589 |
562 @override | 590 @override |
563 bool visitType(TypeConstantExpression exp1, TypeConstantExpression exp2) { | 591 bool visitType(TypeConstantExpression exp1, TypeConstantExpression exp2) { |
564 return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type); | 592 return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type); |
565 } | 593 } |
566 | 594 |
567 @override | 595 @override |
568 bool visitUnary(UnaryConstantExpression exp1, UnaryConstantExpression exp2) { | 596 bool visitUnary(UnaryConstantExpression exp1, UnaryConstantExpression exp2) { |
569 return | 597 return |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
656 StringLengthConstantExpression exp2) { | 684 StringLengthConstantExpression exp2) { |
657 return | 685 return |
658 strategy.testConstants( | 686 strategy.testConstants( |
659 exp1, exp2, 'expression', | 687 exp1, exp2, 'expression', |
660 exp1.expression, exp2.expression); | 688 exp1.expression, exp2.expression); |
661 } | 689 } |
662 | 690 |
663 @override | 691 @override |
664 bool visitDeferred(DeferredConstantExpression exp1, | 692 bool visitDeferred(DeferredConstantExpression exp1, |
665 DeferredConstantExpression exp2) { | 693 DeferredConstantExpression exp2) { |
666 // TODO: implement visitDeferred | 694 // TODO(johnniwinther): Implement this. |
667 return true; | 695 return true; |
668 } | 696 } |
669 } | 697 } |
670 | 698 |
671 /// Tests the equivalence of [impact1] and [impact2] using [strategy]. | 699 /// Tests the equivalence of [impact1] and [impact2] using [strategy]. |
672 bool testResolutionImpactEquivalence( | 700 bool testResolutionImpactEquivalence( |
673 ResolutionImpact impact1, | 701 ResolutionImpact impact1, |
674 ResolutionImpact impact2, | 702 ResolutionImpact impact2, |
675 [TestStrategy strategy = const TestStrategy()]) { | 703 [TestStrategy strategy = const TestStrategy()]) { |
676 return | 704 return |
(...skipping 21 matching lines...) Expand all Loading... |
698 areMapLiteralUsesEquivalent) && | 726 areMapLiteralUsesEquivalent) && |
699 strategy.testSets( | 727 strategy.testSets( |
700 impact1, impact2, 'staticUses', | 728 impact1, impact2, 'staticUses', |
701 impact1.staticUses, impact2.staticUses, | 729 impact1.staticUses, impact2.staticUses, |
702 areStaticUsesEquivalent) && | 730 areStaticUsesEquivalent) && |
703 strategy.testSets( | 731 strategy.testSets( |
704 impact1, impact2, 'typeUses', | 732 impact1, impact2, 'typeUses', |
705 impact1.typeUses, impact2.typeUses, | 733 impact1.typeUses, impact2.typeUses, |
706 areTypeUsesEquivalent); | 734 areTypeUsesEquivalent); |
707 } | 735 } |
| 736 |
| 737 /// Tests the equivalence of [resolvedAst1] and [resolvedAst2] using [strategy]. |
| 738 bool testResolvedAstEquivalence( |
| 739 ResolvedAst resolvedAst1, |
| 740 ResolvedAst resolvedAst2, |
| 741 [TestStrategy strategy = const TestStrategy()]) { |
| 742 return |
| 743 strategy.testElements( |
| 744 resolvedAst1, resolvedAst2, 'element', |
| 745 resolvedAst1.element, resolvedAst2.element) && |
| 746 // Compute AST equivalence by structural comparison. |
| 747 strategy.test( |
| 748 resolvedAst1, resolvedAst2, 'node', |
| 749 resolvedAst1.node.toDebugString(), |
| 750 resolvedAst2.node.toDebugString()) && |
| 751 testTreeElementsEquivalence( |
| 752 resolvedAst1, resolvedAst2, strategy); |
| 753 } |
| 754 |
| 755 /// Tests the equivalence of the data stored in the [TreeElements] of |
| 756 /// [resolvedAst1] and [resolvedAst2] using [strategy]. |
| 757 bool testTreeElementsEquivalence( |
| 758 ResolvedAst resolvedAst1, |
| 759 ResolvedAst resolvedAst2, |
| 760 [TestStrategy strategy = const TestStrategy()]) { |
| 761 |
| 762 AstIndexComputer indices1 = new AstIndexComputer(); |
| 763 resolvedAst1.node.accept(indices1); |
| 764 AstIndexComputer indices2 = new AstIndexComputer(); |
| 765 resolvedAst2.node.accept(indices2); |
| 766 |
| 767 TreeElements elements1 = resolvedAst1.elements; |
| 768 TreeElements elements2 = resolvedAst2.elements; |
| 769 |
| 770 TreeElementsEquivalenceVisitor visitor = |
| 771 new TreeElementsEquivalenceVisitor( |
| 772 indices1, indices2, elements1, elements2, strategy); |
| 773 resolvedAst1.node.accept(visitor); |
| 774 return visitor.success; |
| 775 } |
| 776 |
| 777 /// Visitor that checks the equivalence of [TreeElements] data. |
| 778 class TreeElementsEquivalenceVisitor extends Visitor { |
| 779 final TestStrategy strategy; |
| 780 final AstIndexComputer indices1; |
| 781 final AstIndexComputer indices2; |
| 782 final TreeElements elements1; |
| 783 final TreeElements elements2; |
| 784 bool success = true; |
| 785 |
| 786 TreeElementsEquivalenceVisitor( |
| 787 this.indices1, this.indices2, |
| 788 this.elements1, this.elements2, |
| 789 [this.strategy = const TestStrategy()]); |
| 790 |
| 791 visitNode(Node node1) { |
| 792 if (!success) return; |
| 793 int index = indices1.nodeIndices[node1]; |
| 794 Node node2 = indices2.nodeList[index]; |
| 795 success = |
| 796 strategy.testElements( |
| 797 node1, node2, '[$index]', |
| 798 elements1[node1], elements2[node2]) && |
| 799 strategy.testTypes( |
| 800 node1, node2, 'getType($index)', |
| 801 elements1.getType(node1), elements2.getType(node2)) && |
| 802 strategy.test( |
| 803 node1, node2, 'getSelector($index)', |
| 804 elements1.getSelector(node1), elements2.getSelector(node2), |
| 805 areSelectorsEquivalent) && |
| 806 strategy.testConstants( |
| 807 node1, node2, 'getConstant($index)', |
| 808 elements1.getConstant(node1), elements2.getConstant(node2)) && |
| 809 strategy.testTypes( |
| 810 node1, node2, 'typesCache[$index]', |
| 811 elements1.typesCache[node1], elements2.typesCache[node2]); |
| 812 |
| 813 node1.visitChildren(this); |
| 814 } |
| 815 |
| 816 @override |
| 817 visitSend(Send node1) { |
| 818 visitExpression(node1); |
| 819 if (!success) return; |
| 820 int index = indices1.nodeIndices[node1]; |
| 821 Send node2 = indices2.nodeList[index]; |
| 822 success = |
| 823 strategy.test( |
| 824 node1, node2, 'isTypeLiteral($index)', |
| 825 elements1.isTypeLiteral(node1), |
| 826 elements2.isTypeLiteral(node2)) && |
| 827 strategy.testTypes( |
| 828 node1, node2, 'getTypeLiteralType($index)', |
| 829 elements1.getTypeLiteralType(node1), |
| 830 elements2.getTypeLiteralType(node2)) && |
| 831 strategy.test( |
| 832 node1, node2, 'getSendStructure($index)', |
| 833 elements1.getSendStructure(node1), |
| 834 elements2.getSendStructure(node2), |
| 835 areSendStructuresEquivalent); |
| 836 } |
| 837 |
| 838 @override |
| 839 visitNewExpression(NewExpression node1) { |
| 840 visitExpression(node1); |
| 841 if (!success) return; |
| 842 int index = indices1.nodeIndices[node1]; |
| 843 NewExpression node2 = indices2.nodeList[index]; |
| 844 success = |
| 845 strategy.test( |
| 846 node1, node2, 'getNewStructure($index)', |
| 847 elements1.getNewStructure(node1), |
| 848 elements2.getNewStructure(node2), |
| 849 areNewStructuresEquivalent); |
| 850 } |
| 851 |
| 852 @override |
| 853 visitSendSet(SendSet node1) { |
| 854 visitSend(node1); |
| 855 if (!success) return; |
| 856 int index = indices1.nodeIndices[node1]; |
| 857 SendSet node2 = indices2.nodeList[index]; |
| 858 success = |
| 859 strategy.test( |
| 860 node1, node2, 'getGetterSelectorInComplexSendSet($index)', |
| 861 elements1.getGetterSelectorInComplexSendSet(node1), |
| 862 elements2.getGetterSelectorInComplexSendSet(node2), |
| 863 areSelectorsEquivalent) && |
| 864 strategy.test( |
| 865 node1, node2, 'getOperatorSelectorInComplexSendSet($index)', |
| 866 elements1.getOperatorSelectorInComplexSendSet(node1), |
| 867 elements2.getOperatorSelectorInComplexSendSet(node2), |
| 868 areSelectorsEquivalent); |
| 869 } |
| 870 |
| 871 @override |
| 872 visitFunctionExpression(FunctionExpression node1) { |
| 873 visitNode(node1); |
| 874 if (!success) return; |
| 875 int index = indices1.nodeIndices[node1]; |
| 876 FunctionExpression node2 = indices2.nodeList[index]; |
| 877 if (elements1[node1] is! FunctionElement) { |
| 878 // [getFunctionDefinition] is currently stored in [] which doesn't always |
| 879 // contain a [FunctionElement]. |
| 880 return; |
| 881 } |
| 882 success = |
| 883 strategy.testElements( |
| 884 node1, node2, 'getFunctionDefinition($index)', |
| 885 elements1.getFunctionDefinition(node1), |
| 886 elements2.getFunctionDefinition(node2)); |
| 887 } |
| 888 |
| 889 @override |
| 890 visitForIn(ForIn node1) { |
| 891 visitLoop(node1); |
| 892 if (!success) return; |
| 893 int index = indices1.nodeIndices[node1]; |
| 894 ForIn node2 = indices2.nodeList[index]; |
| 895 success = |
| 896 strategy.testElements( |
| 897 node1, node2, 'getForInVariable($index)', |
| 898 elements1.getForInVariable(node1), |
| 899 elements2.getForInVariable(node2)); |
| 900 } |
| 901 |
| 902 @override |
| 903 visitRedirectingFactoryBody(RedirectingFactoryBody node1) { |
| 904 visitStatement(node1); |
| 905 if (!success) return; |
| 906 int index = indices1.nodeIndices[node1]; |
| 907 RedirectingFactoryBody node2 = indices2.nodeList[index]; |
| 908 success = |
| 909 strategy.testElements( |
| 910 node1, node2, 'getRedirectingTargetConstructor($index)', |
| 911 elements1.getRedirectingTargetConstructor(node1), |
| 912 elements2.getRedirectingTargetConstructor(node2)); |
| 913 } |
| 914 } |
OLD | NEW |