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'; |
16 | 21 |
17 /// Equality based equivalence function. | 22 /// Equality based equivalence function. |
18 bool equality(a, b) => a == b; | 23 bool equality(a, b) => a == b; |
19 | 24 |
20 /// Returns `true` if the elements in [a] and [b] are pair-wise equivalent | 25 /// Returns `true` if the elements in [a] and [b] are pair-wise equivalent |
21 /// according to [elementEquivalence]. | 26 /// according to [elementEquivalence]. |
22 bool areListsEquivalent(List a, List b, | 27 bool areListsEquivalent(List a, List b, |
23 [bool elementEquivalence(a, b) = equality]) { | 28 [bool elementEquivalence(a, b) = equality]) { |
24 if (a.length != b.length) return false; | 29 if (a.length != b.length) return false; |
25 for (int i = 0; i < a.length && i < b.length; i++) { | 30 for (int i = 0; i < a.length && i < b.length; i++) { |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 } | 88 } |
84 | 89 |
85 /// Returns `true` if the lists of constants, [a] and [b], are equivalent. | 90 /// Returns `true` if the lists of constants, [a] and [b], are equivalent. |
86 bool areConstantListsEquivalent( | 91 bool areConstantListsEquivalent( |
87 List<ConstantExpression> a, List<ConstantExpression> b) { | 92 List<ConstantExpression> a, List<ConstantExpression> b) { |
88 return areListsEquivalent(a, b, areConstantsEquivalent); | 93 return areListsEquivalent(a, b, areConstantsEquivalent); |
89 } | 94 } |
90 | 95 |
91 /// Returns `true` if the selectors [a] and [b] are equivalent. | 96 /// Returns `true` if the selectors [a] and [b] are equivalent. |
92 bool areSelectorsEquivalent(Selector a, Selector b) { | 97 bool areSelectorsEquivalent(Selector a, Selector b) { |
| 98 if (identical(a, b)) return true; |
| 99 if (a == null || b == null) return false; |
93 return a.kind == b.kind && | 100 return a.kind == b.kind && |
94 a.callStructure == b.callStructure && | 101 a.callStructure == b.callStructure && |
95 areNamesEquivalent(a.memberName, b.memberName); | 102 areNamesEquivalent(a.memberName, b.memberName); |
96 } | 103 } |
97 | 104 |
98 /// Returns `true` if the names [a] and [b] are equivalent. | 105 /// Returns `true` if the names [a] and [b] are equivalent. |
99 bool areNamesEquivalent(Name a, Name b) { | 106 bool areNamesEquivalent(Name a, Name b) { |
100 return a.text == b.text && | 107 return a.text == b.text && |
101 a.isSetter == b.isSetter && | 108 a.isSetter == b.isSetter && |
102 areElementsEquivalent(a.library, b.library); | 109 areElementsEquivalent(a.library, b.library); |
(...skipping 21 matching lines...) Expand all Loading... |
124 a.isEmpty == b.isEmpty; | 131 a.isEmpty == b.isEmpty; |
125 } | 132 } |
126 | 133 |
127 /// Returns `true` if the map literal uses [a] and [b] are equivalent. | 134 /// Returns `true` if the map literal uses [a] and [b] are equivalent. |
128 bool areMapLiteralUsesEquivalent(MapLiteralUse a, MapLiteralUse b) { | 135 bool areMapLiteralUsesEquivalent(MapLiteralUse a, MapLiteralUse b) { |
129 return areTypesEquivalent(a.type, b.type) && | 136 return areTypesEquivalent(a.type, b.type) && |
130 a.isConstant == b.isConstant && | 137 a.isConstant == b.isConstant && |
131 a.isEmpty == b.isEmpty; | 138 a.isEmpty == b.isEmpty; |
132 } | 139 } |
133 | 140 |
| 141 /// Returns `true` if the send structures [a] and [b] are equivalent. |
| 142 bool areSendStructuresEquivalent(SendStructure a, SendStructure b) { |
| 143 if (identical(a, b)) return true; |
| 144 if (a == null || b == null) return false; |
| 145 if (a.kind != b.kind) return false; |
| 146 // TODO(johnniwinther): Compute a deep equivalence. |
| 147 return true; |
| 148 } |
| 149 |
| 150 /// Returns `true` if the new structures [a] and [b] are equivalent. |
| 151 bool areNewStructuresEquivalent(NewStructure a, NewStructure b) { |
| 152 if (identical(a, b)) return true; |
| 153 if (a == null || b == null) return false; |
| 154 if (a.kind != b.kind) return false; |
| 155 // TODO(johnniwinther): Compute a deep equivalence. |
| 156 return true; |
| 157 } |
| 158 |
134 /// Strategy for testing equivalence. | 159 /// Strategy for testing equivalence. |
135 /// | 160 /// |
136 /// Use this strategy to determine equivalence without failing on inequivalence. | 161 /// Use this strategy to determine equivalence without failing on inequivalence. |
137 class TestStrategy { | 162 class TestStrategy { |
138 const TestStrategy(); | 163 const TestStrategy(); |
139 | 164 |
140 bool test(var object1, var object2, String property, var value1, var value2) { | 165 bool test(var object1, var object2, String property, var value1, var value2, |
141 return value1 == value2; | 166 [bool equivalence(a, b) = equality]) { |
| 167 return equivalence(value1, value2); |
142 } | 168 } |
143 | 169 |
144 bool testLists( | 170 bool testLists( |
145 Object object1, Object object2, String property, List list1, List list2, | 171 Object object1, Object object2, String property, List list1, List list2, |
146 [bool elementEquivalence(a, b) = equality]) { | 172 [bool elementEquivalence(a, b) = equality]) { |
147 return areListsEquivalent(list1, list2, elementEquivalence); | 173 return areListsEquivalent(list1, list2, elementEquivalence); |
148 } | 174 } |
149 | 175 |
150 bool testSets( | 176 bool testSets( |
151 var object1, var object2, String property, Iterable set1, Iterable set2, | 177 var object1, var object2, String property, Iterable set1, Iterable set2, |
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
476 | 502 |
477 @override | 503 @override |
478 bool visitPositional( | 504 bool visitPositional( |
479 PositionalArgumentReference exp1, PositionalArgumentReference exp2) { | 505 PositionalArgumentReference exp1, PositionalArgumentReference exp2) { |
480 return strategy.test(exp1, exp2, 'index', exp1.index, exp2.index); | 506 return strategy.test(exp1, exp2, 'index', exp1.index, exp2.index); |
481 } | 507 } |
482 | 508 |
483 @override | 509 @override |
484 bool visitSymbol( | 510 bool visitSymbol( |
485 SymbolConstantExpression exp1, SymbolConstantExpression exp2) { | 511 SymbolConstantExpression exp1, SymbolConstantExpression exp2) { |
486 // TODO: implement visitSymbol | 512 // TODO(johnniwinther): Handle private names. Currently not even supported |
487 return true; | 513 // in resolution. |
| 514 return strategy.test(exp1, exp2, 'name', exp1.name, exp2.name); |
488 } | 515 } |
489 | 516 |
490 @override | 517 @override |
491 bool visitType(TypeConstantExpression exp1, TypeConstantExpression exp2) { | 518 bool visitType(TypeConstantExpression exp1, TypeConstantExpression exp2) { |
492 return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type); | 519 return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type); |
493 } | 520 } |
494 | 521 |
495 @override | 522 @override |
496 bool visitUnary(UnaryConstantExpression exp1, UnaryConstantExpression exp2) { | 523 bool visitUnary(UnaryConstantExpression exp1, UnaryConstantExpression exp2) { |
497 return strategy.test( | 524 return strategy.test( |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
565 @override | 592 @override |
566 bool visitStringLength(StringLengthConstantExpression exp1, | 593 bool visitStringLength(StringLengthConstantExpression exp1, |
567 StringLengthConstantExpression exp2) { | 594 StringLengthConstantExpression exp2) { |
568 return strategy.testConstants( | 595 return strategy.testConstants( |
569 exp1, exp2, 'expression', exp1.expression, exp2.expression); | 596 exp1, exp2, 'expression', exp1.expression, exp2.expression); |
570 } | 597 } |
571 | 598 |
572 @override | 599 @override |
573 bool visitDeferred( | 600 bool visitDeferred( |
574 DeferredConstantExpression exp1, DeferredConstantExpression exp2) { | 601 DeferredConstantExpression exp1, DeferredConstantExpression exp2) { |
575 // TODO: implement visitDeferred | 602 // TODO(johnniwinther): Implement this. |
576 return true; | 603 return true; |
577 } | 604 } |
578 } | 605 } |
579 | 606 |
580 /// Tests the equivalence of [impact1] and [impact2] using [strategy]. | 607 /// Tests the equivalence of [impact1] and [impact2] using [strategy]. |
581 bool testResolutionImpactEquivalence( | 608 bool testResolutionImpactEquivalence( |
582 ResolutionImpact impact1, ResolutionImpact impact2, | 609 ResolutionImpact impact1, ResolutionImpact impact2, |
583 [TestStrategy strategy = const TestStrategy()]) { | 610 [TestStrategy strategy = const TestStrategy()]) { |
584 return strategy.testSets(impact1, impact2, 'constSymbolNames', | 611 return strategy.testSets(impact1, impact2, 'constSymbolNames', |
585 impact1.constSymbolNames, impact2.constSymbolNames) && | 612 impact1.constSymbolNames, impact2.constSymbolNames) && |
(...skipping 10 matching lines...) Expand all Loading... |
596 impact1, impact2, 'features', impact1.features, impact2.features) && | 623 impact1, impact2, 'features', impact1.features, impact2.features) && |
597 strategy.testSets(impact1, impact2, 'listLiterals', impact1.listLiterals, | 624 strategy.testSets(impact1, impact2, 'listLiterals', impact1.listLiterals, |
598 impact2.listLiterals, areListLiteralUsesEquivalent) && | 625 impact2.listLiterals, areListLiteralUsesEquivalent) && |
599 strategy.testSets(impact1, impact2, 'mapLiterals', impact1.mapLiterals, | 626 strategy.testSets(impact1, impact2, 'mapLiterals', impact1.mapLiterals, |
600 impact2.mapLiterals, areMapLiteralUsesEquivalent) && | 627 impact2.mapLiterals, areMapLiteralUsesEquivalent) && |
601 strategy.testSets(impact1, impact2, 'staticUses', impact1.staticUses, | 628 strategy.testSets(impact1, impact2, 'staticUses', impact1.staticUses, |
602 impact2.staticUses, areStaticUsesEquivalent) && | 629 impact2.staticUses, areStaticUsesEquivalent) && |
603 strategy.testSets(impact1, impact2, 'typeUses', impact1.typeUses, | 630 strategy.testSets(impact1, impact2, 'typeUses', impact1.typeUses, |
604 impact2.typeUses, areTypeUsesEquivalent); | 631 impact2.typeUses, areTypeUsesEquivalent); |
605 } | 632 } |
| 633 |
| 634 /// Tests the equivalence of [resolvedAst1] and [resolvedAst2] using [strategy]. |
| 635 bool testResolvedAstEquivalence( |
| 636 ResolvedAst resolvedAst1, ResolvedAst resolvedAst2, |
| 637 [TestStrategy strategy = const TestStrategy()]) { |
| 638 return strategy.testElements(resolvedAst1, resolvedAst2, 'element', |
| 639 resolvedAst1.element, resolvedAst2.element) && |
| 640 // Compute AST equivalence by structural comparison. |
| 641 strategy.test( |
| 642 resolvedAst1, |
| 643 resolvedAst2, |
| 644 'node', |
| 645 resolvedAst1.node.toDebugString(), |
| 646 resolvedAst2.node.toDebugString()) && |
| 647 testTreeElementsEquivalence(resolvedAst1, resolvedAst2, strategy); |
| 648 } |
| 649 |
| 650 /// Tests the equivalence of the data stored in the [TreeElements] of |
| 651 /// [resolvedAst1] and [resolvedAst2] using [strategy]. |
| 652 bool testTreeElementsEquivalence( |
| 653 ResolvedAst resolvedAst1, ResolvedAst resolvedAst2, |
| 654 [TestStrategy strategy = const TestStrategy()]) { |
| 655 AstIndexComputer indices1 = new AstIndexComputer(); |
| 656 resolvedAst1.node.accept(indices1); |
| 657 AstIndexComputer indices2 = new AstIndexComputer(); |
| 658 resolvedAst2.node.accept(indices2); |
| 659 |
| 660 TreeElements elements1 = resolvedAst1.elements; |
| 661 TreeElements elements2 = resolvedAst2.elements; |
| 662 |
| 663 TreeElementsEquivalenceVisitor visitor = new TreeElementsEquivalenceVisitor( |
| 664 indices1, indices2, elements1, elements2, strategy); |
| 665 resolvedAst1.node.accept(visitor); |
| 666 return visitor.success; |
| 667 } |
| 668 |
| 669 /// Visitor that checks the equivalence of [TreeElements] data. |
| 670 class TreeElementsEquivalenceVisitor extends Visitor { |
| 671 final TestStrategy strategy; |
| 672 final AstIndexComputer indices1; |
| 673 final AstIndexComputer indices2; |
| 674 final TreeElements elements1; |
| 675 final TreeElements elements2; |
| 676 bool success = true; |
| 677 |
| 678 TreeElementsEquivalenceVisitor( |
| 679 this.indices1, this.indices2, this.elements1, this.elements2, |
| 680 [this.strategy = const TestStrategy()]); |
| 681 |
| 682 visitNode(Node node1) { |
| 683 if (!success) return; |
| 684 int index = indices1.nodeIndices[node1]; |
| 685 Node node2 = indices2.nodeList[index]; |
| 686 success = strategy.testElements( |
| 687 node1, node2, '[$index]', elements1[node1], elements2[node2]) && |
| 688 strategy.testTypes(node1, node2, 'getType($index)', |
| 689 elements1.getType(node1), elements2.getType(node2)) && |
| 690 strategy.test( |
| 691 node1, |
| 692 node2, |
| 693 'getSelector($index)', |
| 694 elements1.getSelector(node1), |
| 695 elements2.getSelector(node2), |
| 696 areSelectorsEquivalent) && |
| 697 strategy.testConstants(node1, node2, 'getConstant($index)', |
| 698 elements1.getConstant(node1), elements2.getConstant(node2)) && |
| 699 strategy.testTypes(node1, node2, 'typesCache[$index]', |
| 700 elements1.typesCache[node1], elements2.typesCache[node2]); |
| 701 |
| 702 node1.visitChildren(this); |
| 703 } |
| 704 |
| 705 @override |
| 706 visitSend(Send node1) { |
| 707 visitExpression(node1); |
| 708 if (!success) return; |
| 709 int index = indices1.nodeIndices[node1]; |
| 710 Send node2 = indices2.nodeList[index]; |
| 711 success = strategy.test(node1, node2, 'isTypeLiteral($index)', |
| 712 elements1.isTypeLiteral(node1), elements2.isTypeLiteral(node2)) && |
| 713 strategy.testTypes( |
| 714 node1, |
| 715 node2, |
| 716 'getTypeLiteralType($index)', |
| 717 elements1.getTypeLiteralType(node1), |
| 718 elements2.getTypeLiteralType(node2)) && |
| 719 strategy.test( |
| 720 node1, |
| 721 node2, |
| 722 'getSendStructure($index)', |
| 723 elements1.getSendStructure(node1), |
| 724 elements2.getSendStructure(node2), |
| 725 areSendStructuresEquivalent); |
| 726 } |
| 727 |
| 728 @override |
| 729 visitNewExpression(NewExpression node1) { |
| 730 visitExpression(node1); |
| 731 if (!success) return; |
| 732 int index = indices1.nodeIndices[node1]; |
| 733 NewExpression node2 = indices2.nodeList[index]; |
| 734 success = strategy.test( |
| 735 node1, |
| 736 node2, |
| 737 'getNewStructure($index)', |
| 738 elements1.getNewStructure(node1), |
| 739 elements2.getNewStructure(node2), |
| 740 areNewStructuresEquivalent); |
| 741 } |
| 742 |
| 743 @override |
| 744 visitSendSet(SendSet node1) { |
| 745 visitSend(node1); |
| 746 if (!success) return; |
| 747 int index = indices1.nodeIndices[node1]; |
| 748 SendSet node2 = indices2.nodeList[index]; |
| 749 success = strategy.test( |
| 750 node1, |
| 751 node2, |
| 752 'getGetterSelectorInComplexSendSet($index)', |
| 753 elements1.getGetterSelectorInComplexSendSet(node1), |
| 754 elements2.getGetterSelectorInComplexSendSet(node2), |
| 755 areSelectorsEquivalent) && |
| 756 strategy.test( |
| 757 node1, |
| 758 node2, |
| 759 'getOperatorSelectorInComplexSendSet($index)', |
| 760 elements1.getOperatorSelectorInComplexSendSet(node1), |
| 761 elements2.getOperatorSelectorInComplexSendSet(node2), |
| 762 areSelectorsEquivalent); |
| 763 } |
| 764 |
| 765 @override |
| 766 visitFunctionExpression(FunctionExpression node1) { |
| 767 visitNode(node1); |
| 768 if (!success) return; |
| 769 int index = indices1.nodeIndices[node1]; |
| 770 FunctionExpression node2 = indices2.nodeList[index]; |
| 771 if (elements1[node1] is! FunctionElement) { |
| 772 // [getFunctionDefinition] is currently stored in [] which doesn't always |
| 773 // contain a [FunctionElement]. |
| 774 return; |
| 775 } |
| 776 success = strategy.testElements( |
| 777 node1, |
| 778 node2, |
| 779 'getFunctionDefinition($index)', |
| 780 elements1.getFunctionDefinition(node1), |
| 781 elements2.getFunctionDefinition(node2)); |
| 782 } |
| 783 |
| 784 @override |
| 785 visitForIn(ForIn node1) { |
| 786 visitLoop(node1); |
| 787 if (!success) return; |
| 788 int index = indices1.nodeIndices[node1]; |
| 789 ForIn node2 = indices2.nodeList[index]; |
| 790 success = strategy.testElements(node1, node2, 'getForInVariable($index)', |
| 791 elements1.getForInVariable(node1), elements2.getForInVariable(node2)); |
| 792 } |
| 793 |
| 794 @override |
| 795 visitRedirectingFactoryBody(RedirectingFactoryBody node1) { |
| 796 visitStatement(node1); |
| 797 if (!success) return; |
| 798 int index = indices1.nodeIndices[node1]; |
| 799 RedirectingFactoryBody node2 = indices2.nodeList[index]; |
| 800 success = strategy.testElements( |
| 801 node1, |
| 802 node2, |
| 803 'getRedirectingTargetConstructor($index)', |
| 804 elements1.getRedirectingTargetConstructor(node1), |
| 805 elements2.getRedirectingTargetConstructor(node2)); |
| 806 } |
| 807 } |
OLD | NEW |