| 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 |