| Index: pkg/compiler/lib/src/serialization/equivalence.dart
|
| diff --git a/pkg/compiler/lib/src/serialization/equivalence.dart b/pkg/compiler/lib/src/serialization/equivalence.dart
|
| index 5df0a19cae4fffd884e2adcb968c578ccbbce194..be28121447478a00f77bd9d59506b7b5bb9cae63 100644
|
| --- a/pkg/compiler/lib/src/serialization/equivalence.dart
|
| +++ b/pkg/compiler/lib/src/serialization/equivalence.dart
|
| @@ -11,8 +11,13 @@ import '../constants/expressions.dart';
|
| import '../dart_types.dart';
|
| import '../elements/elements.dart';
|
| import '../elements/visitor.dart';
|
| +import '../resolution/send_structure.dart';
|
| +import '../resolution/tree_elements.dart';
|
| +import '../tokens/token.dart';
|
| +import '../tree/nodes.dart';
|
| import '../universe/selector.dart';
|
| import '../universe/use.dart';
|
| +import 'resolved_ast_serialization.dart';
|
|
|
| /// Equality based equivalence function.
|
| bool equality(a, b) => a == b;
|
| @@ -90,6 +95,8 @@ bool areConstantListsEquivalent(
|
|
|
| /// Returns `true` if the selectors [a] and [b] are equivalent.
|
| bool areSelectorsEquivalent(Selector a, Selector b) {
|
| + if (identical(a, b)) return true;
|
| + if (a == null || b == null) return false;
|
| return a.kind == b.kind &&
|
| a.callStructure == b.callStructure &&
|
| areNamesEquivalent(a.memberName, b.memberName);
|
| @@ -131,14 +138,33 @@ bool areMapLiteralUsesEquivalent(MapLiteralUse a, MapLiteralUse b) {
|
| a.isEmpty == b.isEmpty;
|
| }
|
|
|
| +/// Returns `true` if the send structures [a] and [b] are equivalent.
|
| +bool areSendStructuresEquivalent(SendStructure a, SendStructure b) {
|
| + if (identical(a, b)) return true;
|
| + if (a == null || b == null) return false;
|
| + if (a.kind != b.kind) return false;
|
| + // TODO(johnniwinther): Compute a deep equivalence.
|
| + return true;
|
| +}
|
| +
|
| +/// Returns `true` if the new structures [a] and [b] are equivalent.
|
| +bool areNewStructuresEquivalent(NewStructure a, NewStructure b) {
|
| + if (identical(a, b)) return true;
|
| + if (a == null || b == null) return false;
|
| + if (a.kind != b.kind) return false;
|
| + // TODO(johnniwinther): Compute a deep equivalence.
|
| + return true;
|
| +}
|
| +
|
| /// Strategy for testing equivalence.
|
| ///
|
| /// Use this strategy to determine equivalence without failing on inequivalence.
|
| class TestStrategy {
|
| const TestStrategy();
|
|
|
| - bool test(var object1, var object2, String property, var value1, var value2) {
|
| - return value1 == value2;
|
| + bool test(var object1, var object2, String property, var value1, var value2,
|
| + [bool equivalence(a, b) = equality]) {
|
| + return equivalence(value1, value2);
|
| }
|
|
|
| bool testLists(
|
| @@ -483,8 +509,9 @@ class ConstantEquivalence
|
| @override
|
| bool visitSymbol(
|
| SymbolConstantExpression exp1, SymbolConstantExpression exp2) {
|
| - // TODO: implement visitSymbol
|
| - return true;
|
| + // TODO(johnniwinther): Handle private names. Currently not even supported
|
| + // in resolution.
|
| + return strategy.test(exp1, exp2, 'name', exp1.name, exp2.name);
|
| }
|
|
|
| @override
|
| @@ -572,7 +599,7 @@ class ConstantEquivalence
|
| @override
|
| bool visitDeferred(
|
| DeferredConstantExpression exp1, DeferredConstantExpression exp2) {
|
| - // TODO: implement visitDeferred
|
| + // TODO(johnniwinther): Implement this.
|
| return true;
|
| }
|
| }
|
| @@ -603,3 +630,178 @@ bool testResolutionImpactEquivalence(
|
| strategy.testSets(impact1, impact2, 'typeUses', impact1.typeUses,
|
| impact2.typeUses, areTypeUsesEquivalent);
|
| }
|
| +
|
| +/// Tests the equivalence of [resolvedAst1] and [resolvedAst2] using [strategy].
|
| +bool testResolvedAstEquivalence(
|
| + ResolvedAst resolvedAst1, ResolvedAst resolvedAst2,
|
| + [TestStrategy strategy = const TestStrategy()]) {
|
| + return strategy.testElements(resolvedAst1, resolvedAst2, 'element',
|
| + resolvedAst1.element, resolvedAst2.element) &&
|
| + // Compute AST equivalence by structural comparison.
|
| + strategy.test(
|
| + resolvedAst1,
|
| + resolvedAst2,
|
| + 'node',
|
| + resolvedAst1.node.toDebugString(),
|
| + resolvedAst2.node.toDebugString()) &&
|
| + testTreeElementsEquivalence(resolvedAst1, resolvedAst2, strategy);
|
| +}
|
| +
|
| +/// Tests the equivalence of the data stored in the [TreeElements] of
|
| +/// [resolvedAst1] and [resolvedAst2] using [strategy].
|
| +bool testTreeElementsEquivalence(
|
| + ResolvedAst resolvedAst1, ResolvedAst resolvedAst2,
|
| + [TestStrategy strategy = const TestStrategy()]) {
|
| + AstIndexComputer indices1 = new AstIndexComputer();
|
| + resolvedAst1.node.accept(indices1);
|
| + AstIndexComputer indices2 = new AstIndexComputer();
|
| + resolvedAst2.node.accept(indices2);
|
| +
|
| + TreeElements elements1 = resolvedAst1.elements;
|
| + TreeElements elements2 = resolvedAst2.elements;
|
| +
|
| + TreeElementsEquivalenceVisitor visitor = new TreeElementsEquivalenceVisitor(
|
| + indices1, indices2, elements1, elements2, strategy);
|
| + resolvedAst1.node.accept(visitor);
|
| + return visitor.success;
|
| +}
|
| +
|
| +/// Visitor that checks the equivalence of [TreeElements] data.
|
| +class TreeElementsEquivalenceVisitor extends Visitor {
|
| + final TestStrategy strategy;
|
| + final AstIndexComputer indices1;
|
| + final AstIndexComputer indices2;
|
| + final TreeElements elements1;
|
| + final TreeElements elements2;
|
| + bool success = true;
|
| +
|
| + TreeElementsEquivalenceVisitor(
|
| + this.indices1, this.indices2, this.elements1, this.elements2,
|
| + [this.strategy = const TestStrategy()]);
|
| +
|
| + visitNode(Node node1) {
|
| + if (!success) return;
|
| + int index = indices1.nodeIndices[node1];
|
| + Node node2 = indices2.nodeList[index];
|
| + success = strategy.testElements(
|
| + node1, node2, '[$index]', elements1[node1], elements2[node2]) &&
|
| + strategy.testTypes(node1, node2, 'getType($index)',
|
| + elements1.getType(node1), elements2.getType(node2)) &&
|
| + strategy.test(
|
| + node1,
|
| + node2,
|
| + 'getSelector($index)',
|
| + elements1.getSelector(node1),
|
| + elements2.getSelector(node2),
|
| + areSelectorsEquivalent) &&
|
| + strategy.testConstants(node1, node2, 'getConstant($index)',
|
| + elements1.getConstant(node1), elements2.getConstant(node2)) &&
|
| + strategy.testTypes(node1, node2, 'typesCache[$index]',
|
| + elements1.typesCache[node1], elements2.typesCache[node2]);
|
| +
|
| + node1.visitChildren(this);
|
| + }
|
| +
|
| + @override
|
| + visitSend(Send node1) {
|
| + visitExpression(node1);
|
| + if (!success) return;
|
| + int index = indices1.nodeIndices[node1];
|
| + Send node2 = indices2.nodeList[index];
|
| + success = strategy.test(node1, node2, 'isTypeLiteral($index)',
|
| + elements1.isTypeLiteral(node1), elements2.isTypeLiteral(node2)) &&
|
| + strategy.testTypes(
|
| + node1,
|
| + node2,
|
| + 'getTypeLiteralType($index)',
|
| + elements1.getTypeLiteralType(node1),
|
| + elements2.getTypeLiteralType(node2)) &&
|
| + strategy.test(
|
| + node1,
|
| + node2,
|
| + 'getSendStructure($index)',
|
| + elements1.getSendStructure(node1),
|
| + elements2.getSendStructure(node2),
|
| + areSendStructuresEquivalent);
|
| + }
|
| +
|
| + @override
|
| + visitNewExpression(NewExpression node1) {
|
| + visitExpression(node1);
|
| + if (!success) return;
|
| + int index = indices1.nodeIndices[node1];
|
| + NewExpression node2 = indices2.nodeList[index];
|
| + success = strategy.test(
|
| + node1,
|
| + node2,
|
| + 'getNewStructure($index)',
|
| + elements1.getNewStructure(node1),
|
| + elements2.getNewStructure(node2),
|
| + areNewStructuresEquivalent);
|
| + }
|
| +
|
| + @override
|
| + visitSendSet(SendSet node1) {
|
| + visitSend(node1);
|
| + if (!success) return;
|
| + int index = indices1.nodeIndices[node1];
|
| + SendSet node2 = indices2.nodeList[index];
|
| + success = strategy.test(
|
| + node1,
|
| + node2,
|
| + 'getGetterSelectorInComplexSendSet($index)',
|
| + elements1.getGetterSelectorInComplexSendSet(node1),
|
| + elements2.getGetterSelectorInComplexSendSet(node2),
|
| + areSelectorsEquivalent) &&
|
| + strategy.test(
|
| + node1,
|
| + node2,
|
| + 'getOperatorSelectorInComplexSendSet($index)',
|
| + elements1.getOperatorSelectorInComplexSendSet(node1),
|
| + elements2.getOperatorSelectorInComplexSendSet(node2),
|
| + areSelectorsEquivalent);
|
| + }
|
| +
|
| + @override
|
| + visitFunctionExpression(FunctionExpression node1) {
|
| + visitNode(node1);
|
| + if (!success) return;
|
| + int index = indices1.nodeIndices[node1];
|
| + FunctionExpression node2 = indices2.nodeList[index];
|
| + if (elements1[node1] is! FunctionElement) {
|
| + // [getFunctionDefinition] is currently stored in [] which doesn't always
|
| + // contain a [FunctionElement].
|
| + return;
|
| + }
|
| + success = strategy.testElements(
|
| + node1,
|
| + node2,
|
| + 'getFunctionDefinition($index)',
|
| + elements1.getFunctionDefinition(node1),
|
| + elements2.getFunctionDefinition(node2));
|
| + }
|
| +
|
| + @override
|
| + visitForIn(ForIn node1) {
|
| + visitLoop(node1);
|
| + if (!success) return;
|
| + int index = indices1.nodeIndices[node1];
|
| + ForIn node2 = indices2.nodeList[index];
|
| + success = strategy.testElements(node1, node2, 'getForInVariable($index)',
|
| + elements1.getForInVariable(node1), elements2.getForInVariable(node2));
|
| + }
|
| +
|
| + @override
|
| + visitRedirectingFactoryBody(RedirectingFactoryBody node1) {
|
| + visitStatement(node1);
|
| + if (!success) return;
|
| + int index = indices1.nodeIndices[node1];
|
| + RedirectingFactoryBody node2 = indices2.nodeList[index];
|
| + success = strategy.testElements(
|
| + node1,
|
| + node2,
|
| + 'getRedirectingTargetConstructor($index)',
|
| + elements1.getRedirectingTargetConstructor(node1),
|
| + elements2.getRedirectingTargetConstructor(node2));
|
| + }
|
| +}
|
|
|