| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of js_ast; |
| |
| /// Visitor that computes whether two [Node]s are structurally equivalent. |
| class EquivalenceVisitor implements NodeVisitor1<bool, Node> { |
| const EquivalenceVisitor(); |
| |
| /// Called when [node1] and [node2] are not equivalent. |
| /// |
| /// Override this to collect or report information on the inequivalent nodes. |
| bool failAt(Node node1, Node node2) => false; |
| |
| /// Returns whether the non-node values [value1] and [value2] are equivalent. |
| bool testValues(Node node1, Object value1, Node node2, Object value2) => |
| value1 == value2; |
| |
| /// Returns whether the labels [label1] and [label2] are equivalent. |
| bool testLabels(Node node1, String label1, Node node2, String label2) => |
| label1 == label2; |
| |
| bool testNodes(Node node1, Node node2) { |
| if (identical(node1, node2)) return true; |
| if (node1 == null || node2 == null) return failAt(node1, node2); |
| return node1.accept1(this, node2); |
| } |
| |
| bool testNodeLists(List<Node> list1, List<Node> list2) { |
| int index = 0; |
| while (index < list1.length && index < list2.length) { |
| if (!testNodes(list1[index], list2[index])) return false; |
| index++; |
| } |
| if (index < list1.length) { |
| return failAt(list1[index], null); |
| } else if (index < list2.length) { |
| return failAt(list2[index], null); |
| } |
| return true; |
| } |
| |
| @override |
| bool visitProgram(Program node, Node arg) { |
| if (arg is! Program) return failAt(node, arg); |
| Program other = arg; |
| return testNodeLists(node.body, other.body); |
| } |
| |
| @override |
| bool visitInterpolatedDeclaration(InterpolatedDeclaration node, Node arg) { |
| if (arg is! InterpolatedDeclaration) return failAt(node, arg); |
| InterpolatedDeclaration other = arg; |
| return testValues(node, node.nameOrPosition, other, other.nameOrPosition); |
| } |
| |
| @override |
| bool visitInterpolatedStatement(InterpolatedStatement node, Node arg) { |
| if (arg is! InterpolatedStatement) return failAt(node, arg); |
| InterpolatedStatement other = arg; |
| return testValues(node, node.nameOrPosition, other, other.nameOrPosition); |
| } |
| |
| @override |
| bool visitInterpolatedSelector(InterpolatedSelector node, Node arg) { |
| if (arg is! InterpolatedSelector) return failAt(node, arg); |
| InterpolatedSelector other = arg; |
| return testValues(node, node.nameOrPosition, other, other.nameOrPosition); |
| } |
| |
| @override |
| bool visitInterpolatedParameter(InterpolatedParameter node, Node arg) { |
| if (arg is! InterpolatedParameter) return failAt(node, arg); |
| InterpolatedParameter other = arg; |
| return testValues(node, node.nameOrPosition, other, other.nameOrPosition); |
| } |
| |
| @override |
| bool visitInterpolatedLiteral(InterpolatedLiteral node, Node arg) { |
| if (arg is! InterpolatedLiteral) return failAt(node, arg); |
| InterpolatedLiteral other = arg; |
| return testValues(node, node.nameOrPosition, other, other.nameOrPosition); |
| } |
| |
| @override |
| bool visitInterpolatedExpression(InterpolatedExpression node, Node arg) { |
| if (arg is! InterpolatedExpression) return failAt(node, arg); |
| InterpolatedExpression other = arg; |
| return testValues(node, node.nameOrPosition, other, other.nameOrPosition); |
| } |
| |
| @override |
| bool visitComment(Comment node, Node arg) { |
| if (arg is! Comment) return failAt(node, arg); |
| Comment other = arg; |
| return testValues(node, node.comment, other, other.comment); |
| } |
| |
| @override |
| bool visitAwait(Await node, Node arg) { |
| if (arg is! Await) return failAt(node, arg); |
| Await other = arg; |
| return testNodes(node.expression, other.expression); |
| } |
| |
| @override |
| bool visitRegExpLiteral(RegExpLiteral node, Node arg) { |
| if (arg is! RegExpLiteral) return failAt(node, arg); |
| RegExpLiteral other = arg; |
| return testValues(node, node.pattern, other, other.pattern); |
| } |
| |
| @override |
| bool visitProperty(Property node, Node arg) { |
| if (arg is! Property) return failAt(node, arg); |
| Property other = arg; |
| return testNodes(node.name, other.name) && |
| testNodes(node.value, other.value); |
| } |
| |
| @override |
| bool visitMethodDefinition(MethodDefinition node, Node arg) { |
| if (arg is! MethodDefinition) return failAt(node, arg); |
| MethodDefinition other = arg; |
| return testNodes(node.name, other.name) && |
| testNodes(node.function, other.function); |
| } |
| |
| @override |
| bool visitObjectInitializer(ObjectInitializer node, Node arg) { |
| if (arg is! ObjectInitializer) return failAt(node, arg); |
| ObjectInitializer other = arg; |
| return testNodeLists(node.properties, other.properties); |
| } |
| |
| @override |
| bool visitArrayHole(ArrayHole node, Node arg) { |
| if (arg is! ArrayHole) return failAt(node, arg); |
| return true; |
| } |
| |
| @override |
| bool visitArrayInitializer(ArrayInitializer node, Node arg) { |
| if (arg is! ArrayInitializer) return failAt(node, arg); |
| ArrayInitializer other = arg; |
| return testNodeLists(node.elements, other.elements); |
| } |
| |
| @override |
| bool visitName(Name node, Node arg) { |
| if (arg is! Name) return failAt(node, arg); |
| Name other = arg; |
| return testValues(node, node.key, other, other.key); |
| } |
| |
| @override |
| bool visitParentheses(Parentheses node, Node arg) { |
| if (arg is! Parentheses) return failAt(node, arg); |
| Parentheses other = arg; |
| return testValues(node, node.enclosed, other, other.enclosed); |
| } |
| |
| @override |
| bool visitStringConcatenation(StringConcatenation node, Node arg) { |
| if (arg is! StringConcatenation) return failAt(node, arg); |
| StringConcatenation other = arg; |
| return testNodeLists(node.parts, other.parts); |
| } |
| |
| @override |
| bool visitLiteralNull(LiteralNull node, Node arg) { |
| if (arg is! LiteralNull) return failAt(node, arg); |
| return true; |
| } |
| |
| @override |
| bool visitLiteralNumber(LiteralNumber node, Node arg) { |
| if (arg is! LiteralNumber) return failAt(node, arg); |
| LiteralNumber other = arg; |
| return testValues(node, node.value, other, other.value); |
| } |
| |
| @override |
| bool visitLiteralString(LiteralString node, Node arg) { |
| if (arg is! LiteralString) return failAt(node, arg); |
| LiteralString other = arg; |
| return testValues(node, node.value, other, other.value); |
| } |
| |
| @override |
| bool visitLiteralBool(LiteralBool node, Node arg) { |
| if (arg is! LiteralBool) return failAt(node, arg); |
| LiteralBool other = arg; |
| return testValues(node, node.value, other, other.value); |
| } |
| |
| @override |
| bool visitDeferredString(DeferredString node, Node arg) { |
| if (arg is! DeferredString) return failAt(node, arg); |
| DeferredString other = arg; |
| return testValues(node, node.value, other, other.value); |
| } |
| |
| @override |
| bool visitDeferredNumber(DeferredNumber node, Node arg) { |
| if (arg is! DeferredNumber) return failAt(node, arg); |
| DeferredNumber other = arg; |
| return testValues(node, node.value, other, other.value); |
| } |
| |
| @override |
| bool visitDeferredExpression(DeferredExpression node, Node arg) { |
| if (arg is! DeferredExpression) return failAt(node, arg); |
| DeferredExpression other = arg; |
| return testNodes(node.value, other.value); |
| } |
| |
| @override |
| bool visitDeferredStatement(DeferredStatement node, Node arg) { |
| if (arg is! DeferredStatement) return failAt(node, arg); |
| DeferredStatement other = arg; |
| return testNodes(node.statement, other.statement); |
| } |
| |
| @override |
| bool visitFun(Fun node, Node arg) { |
| if (arg is! Fun) return failAt(node, arg); |
| Fun other = arg; |
| return testNodeLists(node.params, other.params) && |
| testNodes(node.body, other.body) && |
| testValues(node, node.asyncModifier, other, other.asyncModifier); |
| } |
| |
| @override |
| bool visitArrowFunction(ArrowFunction node, Node arg) { |
| if (arg is! ArrowFunction) return failAt(node, arg); |
| ArrowFunction other = arg; |
| return testNodeLists(node.params, other.params) && |
| testNodes(node.body, other.body) && |
| testValues(node, node.asyncModifier, other, other.asyncModifier); |
| } |
| |
| @override |
| bool visitNamedFunction(NamedFunction node, Node arg) { |
| if (arg is! NamedFunction) return failAt(node, arg); |
| NamedFunction other = arg; |
| return testNodes(node.name, other.name) && |
| testNodes(node.function, other.function); |
| } |
| |
| @override |
| bool visitAccess(PropertyAccess node, Node arg) { |
| if (arg is! PropertyAccess) return failAt(node, arg); |
| PropertyAccess other = arg; |
| return testNodes(node.receiver, other.receiver) && |
| testNodes(node.selector, other.selector); |
| } |
| |
| @override |
| bool visitParameter(Parameter node, Node arg) { |
| if (arg is! Parameter) return failAt(node, arg); |
| Parameter other = arg; |
| return testValues(node, node.name, other, other.name); |
| } |
| |
| @override |
| bool visitVariableDeclaration(VariableDeclaration node, Node arg) { |
| if (arg is! VariableDeclaration) return failAt(node, arg); |
| VariableDeclaration other = arg; |
| return testValues(node, node.name, other, other.name) && |
| testValues(node, node.allowRename, other, other.allowRename); |
| } |
| |
| @override |
| bool visitThis(This node, Node arg) { |
| if (arg is! This) return failAt(node, arg); |
| return true; |
| } |
| |
| @override |
| bool visitVariableUse(VariableUse node, Node arg) { |
| if (arg is! VariableUse) return failAt(node, arg); |
| VariableUse other = arg; |
| return testValues(node, node.name, other, other.name); |
| } |
| |
| @override |
| bool visitPostfix(Postfix node, Node arg) { |
| if (arg is! Postfix) return failAt(node, arg); |
| Postfix other = arg; |
| return testValues(node, node.op, other, other.op) && |
| testNodes(node.argument, other.argument); |
| } |
| |
| @override |
| bool visitPrefix(Prefix node, Node arg) { |
| if (arg is! Prefix) return failAt(node, arg); |
| Prefix other = arg; |
| return testValues(node, node.op, other, other.op) && |
| testNodes(node.argument, other.argument); |
| } |
| |
| @override |
| bool visitBinary(Binary node, Node arg) { |
| if (arg is! Binary) return failAt(node, arg); |
| Binary other = arg; |
| return testNodes(node.left, other.left) && |
| testValues(node, node.op, other, other.op) && |
| testNodes(node.right, other.right); |
| } |
| |
| @override |
| bool visitCall(Call node, Node arg) { |
| if (arg is! Call) return failAt(node, arg); |
| Call other = arg; |
| return testNodes(node.target, other.target) && |
| testNodeLists(node.arguments, other.arguments); |
| } |
| |
| @override |
| bool visitNew(New node, Node arg) { |
| if (arg is! New) return failAt(node, arg); |
| New other = arg; |
| return testNodes(node.target, other.target) && |
| testNodeLists(node.arguments, other.arguments); |
| } |
| |
| @override |
| bool visitConditional(Conditional node, Node arg) { |
| if (arg is! Conditional) return failAt(node, arg); |
| Conditional other = arg; |
| return testNodes(node.condition, other.condition) && |
| testNodes(node.then, other.then) && |
| testNodes(node.otherwise, other.otherwise); |
| } |
| |
| @override |
| bool visitVariableInitialization(VariableInitialization node, Node arg) { |
| if (arg is! VariableInitialization) return failAt(node, arg); |
| VariableInitialization other = arg; |
| return testNodes(node.declaration, other.declaration) && |
| testNodes(node.value, other.value); |
| } |
| |
| @override |
| bool visitAssignment(Assignment node, Node arg) { |
| if (arg is! Assignment) return failAt(node, arg); |
| Assignment other = arg; |
| return testNodes(node.leftHandSide, other.leftHandSide) && |
| testValues(node, node.op, other, other.op) && |
| testNodes(node.value, other.value); |
| } |
| |
| @override |
| bool visitVariableDeclarationList(VariableDeclarationList node, Node arg) { |
| if (arg is! VariableDeclarationList) return failAt(node, arg); |
| VariableDeclarationList other = arg; |
| return testNodeLists(node.declarations, other.declarations); |
| } |
| |
| @override |
| bool visitLiteralExpression(LiteralExpression node, Node arg) { |
| if (arg is! LiteralExpression) return failAt(node, arg); |
| LiteralExpression other = arg; |
| return testValues(node, node.template, other, other.template); |
| } |
| |
| @override |
| bool visitDartYield(DartYield node, Node arg) { |
| if (arg is! DartYield) return failAt(node, arg); |
| DartYield other = arg; |
| return testNodes(node.expression, other.expression) && |
| testValues(node, node.hasStar, other, other.hasStar); |
| } |
| |
| @override |
| bool visitLiteralStatement(LiteralStatement node, Node arg) { |
| if (arg is! LiteralStatement) return failAt(node, arg); |
| LiteralStatement other = arg; |
| return testValues(node, node.code, other, other.code); |
| } |
| |
| @override |
| bool visitLabeledStatement(LabeledStatement node, Node arg) { |
| if (arg is! LabeledStatement) return failAt(node, arg); |
| LabeledStatement other = arg; |
| return testLabels(node, node.label, other, other.label) && |
| testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitFunctionDeclaration(FunctionDeclaration node, Node arg) { |
| if (arg is! FunctionDeclaration) return failAt(node, arg); |
| FunctionDeclaration other = arg; |
| return testNodes(node.name, other.name) && |
| testNodes(node.function, other.function); |
| } |
| |
| @override |
| bool visitDefault(Default node, Node arg) { |
| if (arg is! Default) return failAt(node, arg); |
| Default other = arg; |
| return testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitCase(Case node, Node arg) { |
| if (arg is! Case) return failAt(node, arg); |
| Case other = arg; |
| return testNodes(node.expression, other.expression) && |
| testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitSwitch(Switch node, Node arg) { |
| if (arg is! Switch) return failAt(node, arg); |
| Switch other = arg; |
| return testNodes(node.key, other.key) && |
| testNodeLists(node.cases, other.cases); |
| } |
| |
| @override |
| bool visitCatch(Catch node, Node arg) { |
| if (arg is! Catch) return failAt(node, arg); |
| Catch other = arg; |
| return testNodes(node.declaration, other.declaration) && |
| testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitTry(Try node, Node arg) { |
| if (arg is! Try) return failAt(node, arg); |
| Try other = arg; |
| return testNodes(node.body, other.body) && |
| testNodes(node.catchPart, other.catchPart) && |
| testNodes(node.finallyPart, other.finallyPart); |
| } |
| |
| @override |
| bool visitThrow(Throw node, Node arg) { |
| if (arg is! Throw) return failAt(node, arg); |
| Throw other = arg; |
| return testNodes(node.expression, other.expression); |
| } |
| |
| @override |
| bool visitReturn(Return node, Node arg) { |
| if (arg is! Return) return failAt(node, arg); |
| Return other = arg; |
| return testNodes(node.value, other.value); |
| } |
| |
| @override |
| bool visitBreak(Break node, Node arg) { |
| if (arg is! Break) return failAt(node, arg); |
| Break other = arg; |
| return testLabels(node, node.targetLabel, other, other.targetLabel); |
| } |
| |
| @override |
| bool visitContinue(Continue node, Node arg) { |
| if (arg is! Continue) return failAt(node, arg); |
| Continue other = arg; |
| return testLabels(node, node.targetLabel, other, other.targetLabel); |
| } |
| |
| @override |
| bool visitDo(Do node, Node arg) { |
| if (arg is! Do) return failAt(node, arg); |
| Do other = arg; |
| return testNodes(node.condition, other.condition) && |
| testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitWhile(While node, Node arg) { |
| if (arg is! While) return failAt(node, arg); |
| While other = arg; |
| return testNodes(node.condition, other.condition) && |
| testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitForIn(ForIn node, Node arg) { |
| if (arg is! ForIn) return failAt(node, arg); |
| ForIn other = arg; |
| return testNodes(node.leftHandSide, other.leftHandSide) && |
| testNodes(node.object, other.object) && |
| testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitFor(For node, Node arg) { |
| if (arg is! For) return failAt(node, arg); |
| For other = arg; |
| return testNodes(node.init, other.init) && |
| testNodes(node.condition, other.condition) && |
| testNodes(node.update, other.update) && |
| testNodes(node.body, other.body); |
| } |
| |
| @override |
| bool visitIf(If node, Node arg) { |
| if (arg is! If) return failAt(node, arg); |
| If other = arg; |
| return testNodes(node.condition, other.condition) && |
| testNodes(node.then, other.then) && |
| testNodes(node.otherwise, other.otherwise); |
| } |
| |
| @override |
| bool visitEmptyStatement(EmptyStatement node, Node arg) { |
| if (arg is! EmptyStatement) return failAt(node, arg); |
| return true; |
| } |
| |
| @override |
| bool visitExpressionStatement(ExpressionStatement node, Node arg) { |
| if (arg is! ExpressionStatement) return failAt(node, arg); |
| ExpressionStatement other = arg; |
| return testNodes(node.expression, other.expression); |
| } |
| |
| @override |
| bool visitBlock(Block node, Node arg) { |
| if (arg is! Block) return failAt(node, arg); |
| Block other = arg; |
| return testNodeLists(node.statements, other.statements); |
| } |
| } |