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