| Index: tests/compiler/dart2js/equivalence/id_equivalence.dart
|
| diff --git a/tests/compiler/dart2js/equivalence/id_equivalence.dart b/tests/compiler/dart2js/equivalence/id_equivalence.dart
|
| index ed2a9dccd4c01574744c13b9461e3731a9147262..a9bc295ce9879a91d4d9e95008bc6387eb3328ca 100644
|
| --- a/tests/compiler/dart2js/equivalence/id_equivalence.dart
|
| +++ b/tests/compiler/dart2js/equivalence/id_equivalence.dart
|
| @@ -8,11 +8,14 @@ import 'package:compiler/src/resolution/access_semantics.dart';
|
| import 'package:compiler/src/resolution/send_structure.dart';
|
| import 'package:compiler/src/resolution/tree_elements.dart';
|
| import 'package:compiler/src/tree/nodes.dart' as ast;
|
| +import 'package:expect/expect.dart';
|
| import 'package:kernel/ast.dart' as ir;
|
|
|
| enum IdKind {
|
| element,
|
| node,
|
| + invoke,
|
| + update,
|
| }
|
|
|
| /// Id for a code point or element with type inference information.
|
| @@ -20,6 +23,61 @@ abstract class Id {
|
| IdKind get kind;
|
| }
|
|
|
| +class IdValue {
|
| + final Id id;
|
| + final String value;
|
| +
|
| + const IdValue(this.id, this.value);
|
| +
|
| + int get hashCode => id.hashCode * 13 + value.hashCode * 17;
|
| +
|
| + bool operator ==(other) {
|
| + if (identical(this, other)) return true;
|
| + if (other is! IdValue) return false;
|
| + return id == other.id && value == other.value;
|
| + }
|
| +
|
| + String toString() {
|
| + switch (id.kind) {
|
| + case IdKind.element:
|
| + ElementId elementId = id;
|
| + return '$elementPrefix${elementId.name}:$value';
|
| + case IdKind.node:
|
| + return value;
|
| + case IdKind.invoke:
|
| + return '$invokePrefix$value';
|
| + case IdKind.update:
|
| + return '$updatePrefix$value';
|
| + }
|
| + throw new UnsupportedError("Unexpected id kind: ${id.kind}");
|
| + }
|
| +
|
| + static const String elementPrefix = "element: ";
|
| + static const String invokePrefix = "invoke: ";
|
| + static const String updatePrefix = "update: ";
|
| +
|
| + static IdValue decode(int offset, String text) {
|
| + Id id;
|
| + String expected;
|
| + if (text.startsWith(elementPrefix)) {
|
| + text = text.substring(elementPrefix.length);
|
| + int colonPos = text.indexOf(':');
|
| + id = new ElementId(text.substring(0, colonPos));
|
| + expected = text.substring(colonPos + 1);
|
| + } else if (text.startsWith(invokePrefix)) {
|
| + id = new NodeId(offset, IdKind.invoke);
|
| + expected = text.substring(invokePrefix.length);
|
| + } else if (text.startsWith(updatePrefix)) {
|
| + id = new NodeId(offset, IdKind.update);
|
| + expected = text.substring(updatePrefix.length);
|
| + } else {
|
| + id = new NodeId(offset, IdKind.node);
|
| + expected = text;
|
| + }
|
| + return new IdValue(id, expected);
|
| + }
|
| +}
|
| +
|
| /// Id for an element with type inference information.
|
| // TODO(johnniwinther): Support local variables, functions and parameters.
|
| class ElementId implements Id {
|
| @@ -48,42 +106,65 @@ class ElementId implements Id {
|
|
|
| IdKind get kind => IdKind.element;
|
|
|
| - String toString() =>
|
| - className != null ? '$className.$memberName' : memberName;
|
| + String get name => className != null ? '$className.$memberName' : memberName;
|
| +
|
| + String toString() => name;
|
| }
|
|
|
| /// Id for a code point with type inference information.
|
| // TODO(johnniwinther): Create an [NodeId]-based equivalence with the kernel IR.
|
| class NodeId implements Id {
|
| final int value;
|
| + final IdKind kind;
|
|
|
| - const NodeId(this.value);
|
| + const NodeId(this.value, this.kind);
|
|
|
| - int get hashCode => value.hashCode;
|
| + int get hashCode => value.hashCode * 13 + kind.hashCode * 17;
|
|
|
| bool operator ==(other) {
|
| if (identical(this, other)) return true;
|
| if (other is! NodeId) return false;
|
| - return value == other.value;
|
| + return value == other.value && kind == other.kind;
|
| }
|
|
|
| - IdKind get kind => IdKind.node;
|
| -
|
| String toString() => '$kind:$value';
|
| }
|
|
|
| class ActualData {
|
| - final Id id;
|
| - final String value;
|
| + final IdValue value;
|
| final SourceSpan sourceSpan;
|
| final Object object;
|
|
|
| - ActualData(this.id, this.value, this.sourceSpan, this.object);
|
| + ActualData(this.value, this.sourceSpan, this.object);
|
| +}
|
| +
|
| +abstract class DataRegistry {
|
| + DiagnosticReporter get reporter;
|
| + Map<Id, ActualData> get actualMap;
|
| +
|
| + void registerValue(
|
| + SourceSpan sourceSpan, Id id, String value, Object object) {
|
| + if (actualMap.containsKey(id)) {
|
| + ActualData existingData = actualMap[id];
|
| + reportHere(reporter, sourceSpan,
|
| + "Duplicate id ${id}, value=$value, object=$object");
|
| + reportHere(
|
| + reporter,
|
| + sourceSpan,
|
| + "Duplicate id ${id}, value=${existingData.value}, "
|
| + "object=${existingData.object}");
|
| + Expect.fail("Duplicate id $id.");
|
| + }
|
| + if (value != null) {
|
| + actualMap[id] =
|
| + new ActualData(new IdValue(id, value), sourceSpan, object);
|
| + }
|
| + }
|
| }
|
|
|
| /// Abstract AST visitor for computing data corresponding to a node or element,
|
| // and record it with a generic [Id].
|
| -abstract class AstDataExtractor extends ast.Visitor {
|
| +abstract class AstDataExtractor extends ast.Visitor with DataRegistry {
|
| final DiagnosticReporter reporter;
|
| final Map<Id, ActualData> actualMap;
|
| final ResolvedAst resolvedAst;
|
| @@ -93,23 +174,16 @@ abstract class AstDataExtractor extends ast.Visitor {
|
| /// Implement this to compute the data corresponding to [element].
|
| ///
|
| /// If `null` is returned, [element] has no associated data.
|
| - String computeElementValue(AstElement element);
|
| + String computeElementValue(Id id, AstElement element);
|
|
|
| /// Implement this to compute the data corresponding to [node]. If [node] has
|
| /// a corresponding [AstElement] this is provided in [element].
|
| ///
|
| /// If `null` is returned, [node] has no associated data.
|
| - String computeNodeValue(ast.Node node, AstElement element);
|
| + String computeNodeValue(Id id, ast.Node node, AstElement element);
|
|
|
| TreeElements get elements => resolvedAst.elements;
|
|
|
| - void registerValue(
|
| - SourceSpan sourceSpan, Id id, String value, Object object) {
|
| - if (value != null) {
|
| - actualMap[id] = new ActualData(id, value, sourceSpan, object);
|
| - }
|
| - }
|
| -
|
| ElementId computeElementId(AstElement element) {
|
| String memberName = element.name;
|
| if (element.isSetter) {
|
| @@ -119,7 +193,7 @@ abstract class AstDataExtractor extends ast.Visitor {
|
| return new ElementId.internal(memberName, className);
|
| }
|
|
|
| - NodeId computeAccessId(ast.Send node, AccessSemantics access) {
|
| + ast.Node computeAccessPosition(ast.Send node, AccessSemantics access) {
|
| switch (access.kind) {
|
| case AccessKind.DYNAMIC_PROPERTY:
|
| case AccessKind.LOCAL_VARIABLE:
|
| @@ -128,7 +202,18 @@ abstract class AstDataExtractor extends ast.Visitor {
|
| case AccessKind.PARAMETER:
|
| case AccessKind.FINAL_PARAMETER:
|
| case AccessKind.EXPRESSION:
|
| - return computeDefaultNodeId(node.selector);
|
| + return node.selector;
|
| + default:
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + ast.Node computeUpdatePosition(ast.Send node, AccessSemantics access) {
|
| + switch (access.kind) {
|
| + case AccessKind.DYNAMIC_PROPERTY:
|
| + case AccessKind.LOCAL_VARIABLE:
|
| + case AccessKind.PARAMETER:
|
| + return node.selector;
|
| default:
|
| return null;
|
| }
|
| @@ -137,13 +222,13 @@ abstract class AstDataExtractor extends ast.Visitor {
|
| void computeForElement(AstElement element) {
|
| ElementId id = computeElementId(element);
|
| if (id == null) return;
|
| - String value = computeElementValue(element);
|
| + String value = computeElementValue(id, element);
|
| registerValue(element.sourcePosition, id, value, element);
|
| }
|
|
|
| void computeForNode(ast.Node node, NodeId id, [AstElement element]) {
|
| if (id == null) return;
|
| - String value = computeNodeValue(node, element);
|
| + String value = computeNodeValue(id, node, element);
|
| SourceSpan sourceSpan = computeSourceSpan(node);
|
| registerValue(sourceSpan, id, value, element ?? node);
|
| }
|
| @@ -154,17 +239,28 @@ abstract class AstDataExtractor extends ast.Visitor {
|
| }
|
|
|
| NodeId computeDefaultNodeId(ast.Node node) {
|
| - return new NodeId(node.getBeginToken().charOffset);
|
| + return new NodeId(node.getBeginToken().charOffset, IdKind.node);
|
| + }
|
| +
|
| + NodeId createAccessId(ast.Node node) {
|
| + return new NodeId(node.getBeginToken().charOffset, IdKind.node);
|
| + }
|
| +
|
| + NodeId createInvokeId(ast.Node node) {
|
| + return new NodeId(node.getBeginToken().charOffset, IdKind.invoke);
|
| }
|
|
|
| - NodeId computeLoopNodeId(ast.Node node) => computeDefaultNodeId(node);
|
| + NodeId createUpdateId(ast.Node node) {
|
| + return new NodeId(node.getBeginToken().charOffset, IdKind.update);
|
| + }
|
| +
|
| + NodeId createLoopId(ast.Node node) => computeDefaultNodeId(node);
|
|
|
| - NodeId computeGotoNodeId(ast.Node node) => computeDefaultNodeId(node);
|
| + NodeId createGotoId(ast.Node node) => computeDefaultNodeId(node);
|
|
|
| - NodeId computeSwitchNodeId(ast.SwitchStatement node) =>
|
| - computeDefaultNodeId(node);
|
| + NodeId createSwitchId(ast.SwitchStatement node) => computeDefaultNodeId(node);
|
|
|
| - NodeId computeSwitchCaseNodeId(ast.SwitchCase node) {
|
| + NodeId createSwitchCaseId(ast.SwitchCase node) {
|
| ast.Node position;
|
| for (ast.Node child in node.labelsAndCases) {
|
| if (child.asCaseMatch() != null) {
|
| @@ -213,11 +309,45 @@ abstract class AstDataExtractor extends ast.Visitor {
|
| if (sendStructure != null) {
|
| switch (sendStructure.kind) {
|
| case SendStructureKind.GET:
|
| + ast.Node position =
|
| + computeAccessPosition(node, sendStructure.semantics);
|
| + if (position != null) {
|
| + computeForNode(node, computeDefaultNodeId(position));
|
| + }
|
| + break;
|
| case SendStructureKind.INVOKE:
|
| case SendStructureKind.BINARY:
|
| case SendStructureKind.EQUALS:
|
| case SendStructureKind.NOT_EQUALS:
|
| - computeForNode(node, computeAccessId(node, sendStructure.semantics));
|
| + ast.Node position =
|
| + computeAccessPosition(node, sendStructure.semantics);
|
| + if (position != null) {
|
| + computeForNode(node, createInvokeId(position));
|
| + }
|
| + break;
|
| + case SendStructureKind.SET:
|
| + break;
|
| + default:
|
| + }
|
| + }
|
| + visitNode(node);
|
| + }
|
| +
|
| + visitSendSet(ast.SendSet node) {
|
| + dynamic sendStructure = elements.getSendStructure(node);
|
| + if (sendStructure != null) {
|
| + switch (sendStructure.kind) {
|
| + case SendStructureKind.SET:
|
| + ast.Node position =
|
| + computeUpdatePosition(node, sendStructure.semantics);
|
| + if (position != null) {
|
| + computeForNode(node, createUpdateId(position));
|
| + }
|
| + break;
|
| + case SendStructureKind.POSTFIX:
|
| + computeForNode(node, createAccessId(node.selector));
|
| + computeForNode(node, createInvokeId(node.assignmentOperator));
|
| + computeForNode(node, createUpdateId(node.selector));
|
| break;
|
| default:
|
| }
|
| @@ -226,49 +356,43 @@ abstract class AstDataExtractor extends ast.Visitor {
|
| }
|
|
|
| visitLoop(ast.Loop node) {
|
| - computeForNode(node, computeLoopNodeId(node));
|
| + computeForNode(node, createLoopId(node));
|
| visitNode(node);
|
| }
|
|
|
| visitGotoStatement(ast.GotoStatement node) {
|
| - computeForNode(node, computeGotoNodeId(node));
|
| + computeForNode(node, createGotoId(node));
|
| visitNode(node);
|
| }
|
|
|
| visitSwitchStatement(ast.SwitchStatement node) {
|
| - computeForNode(node, computeSwitchNodeId(node));
|
| + computeForNode(node, createSwitchId(node));
|
| visitNode(node);
|
| }
|
|
|
| visitSwitchCase(ast.SwitchCase node) {
|
| - computeForNode(node, computeSwitchCaseNodeId(node));
|
| + computeForNode(node, createSwitchCaseId(node));
|
| visitNode(node);
|
| }
|
| }
|
|
|
| /// Abstract IR visitor for computing data corresponding to a node or element,
|
| /// and record it with a generic [Id]
|
| -abstract class IrDataExtractor extends ir.Visitor {
|
| +abstract class IrDataExtractor extends ir.Visitor with DataRegistry {
|
| + final DiagnosticReporter reporter;
|
| final Map<Id, ActualData> actualMap;
|
|
|
| - void registerValue(
|
| - SourceSpan sourceSpan, Id id, String value, Object object) {
|
| - if (value != null) {
|
| - actualMap[id] = new ActualData(id, value, sourceSpan, object);
|
| - }
|
| - }
|
| -
|
| /// Implement this to compute the data corresponding to [member].
|
| ///
|
| /// If `null` is returned, [member] has no associated data.
|
| - String computeMemberValue(ir.Member member);
|
| + String computeMemberValue(Id id, ir.Member member);
|
|
|
| /// Implement this to compute the data corresponding to [node].
|
| ///
|
| /// If `null` is returned, [node] has no associated data.
|
| - String computeNodeValue(ir.TreeNode node);
|
| + String computeNodeValue(Id id, ir.TreeNode node);
|
|
|
| - IrDataExtractor(this.actualMap);
|
| + IrDataExtractor(this.reporter, this.actualMap);
|
| Id computeElementId(ir.Member node) {
|
| String className;
|
| if (node.enclosingClass != null) {
|
| @@ -284,13 +408,13 @@ abstract class IrDataExtractor extends ir.Visitor {
|
| void computeForMember(ir.Member member) {
|
| ElementId id = computeElementId(member);
|
| if (id == null) return;
|
| - String value = computeMemberValue(member);
|
| + String value = computeMemberValue(id, member);
|
| registerValue(computeSourceSpan(member), id, value, member);
|
| }
|
|
|
| void computeForNode(ir.TreeNode node, NodeId id) {
|
| if (id == null) return;
|
| - String value = computeNodeValue(node);
|
| + String value = computeNodeValue(id, node);
|
| registerValue(computeSourceSpan(node), id, value, node);
|
| }
|
|
|
| @@ -301,15 +425,24 @@ abstract class IrDataExtractor extends ir.Visitor {
|
|
|
| NodeId computeDefaultNodeId(ir.TreeNode node) {
|
| assert(node.fileOffset != ir.TreeNode.noOffset);
|
| - return new NodeId(node.fileOffset);
|
| + return new NodeId(node.fileOffset, IdKind.node);
|
| + }
|
| +
|
| + NodeId createInvokeId(ir.TreeNode node) {
|
| + assert(node.fileOffset != ir.TreeNode.noOffset);
|
| + return new NodeId(node.fileOffset, IdKind.invoke);
|
| }
|
|
|
| - NodeId computeLoopNodeId(ir.TreeNode node) => computeDefaultNodeId(node);
|
| - NodeId computeGotoNodeId(ir.TreeNode node) => computeDefaultNodeId(node);
|
| - NodeId computeSwitchNodeId(ir.SwitchStatement node) =>
|
| - computeDefaultNodeId(node);
|
| - NodeId computeSwitchCaseNodeId(ir.SwitchCase node) =>
|
| - new NodeId(node.expressionOffsets.first);
|
| + NodeId createUpdateId(ir.TreeNode node) {
|
| + assert(node.fileOffset != ir.TreeNode.noOffset);
|
| + return new NodeId(node.fileOffset, IdKind.update);
|
| + }
|
| +
|
| + NodeId createLoopId(ir.TreeNode node) => computeDefaultNodeId(node);
|
| + NodeId createGotoId(ir.TreeNode node) => computeDefaultNodeId(node);
|
| + NodeId createSwitchId(ir.SwitchStatement node) => computeDefaultNodeId(node);
|
| + NodeId createSwitchCaseId(ir.SwitchCase node) =>
|
| + new NodeId(node.expressionOffsets.first, IdKind.node);
|
|
|
| void run(ir.Node root) {
|
| root.accept(this);
|
| @@ -325,7 +458,7 @@ abstract class IrDataExtractor extends ir.Visitor {
|
| }
|
|
|
| visitMethodInvocation(ir.MethodInvocation node) {
|
| - computeForNode(node, computeDefaultNodeId(node));
|
| + computeForNode(node, createInvokeId(node));
|
| super.visitMethodInvocation(node);
|
| }
|
|
|
| @@ -335,7 +468,9 @@ abstract class IrDataExtractor extends ir.Visitor {
|
| }
|
|
|
| visitVariableDeclaration(ir.VariableDeclaration node) {
|
| - computeForNode(node, computeDefaultNodeId(node));
|
| + if (node.parent is! ir.FunctionDeclaration) {
|
| + computeForNode(node, computeDefaultNodeId(node));
|
| + }
|
| super.visitVariableDeclaration(node);
|
| }
|
|
|
| @@ -354,43 +489,53 @@ abstract class IrDataExtractor extends ir.Visitor {
|
| super.visitVariableGet(node);
|
| }
|
|
|
| + visitPropertySet(ir.PropertySet node) {
|
| + computeForNode(node, createUpdateId(node));
|
| + super.visitPropertySet(node);
|
| + }
|
| +
|
| + visitVariableSet(ir.VariableSet node) {
|
| + computeForNode(node, createUpdateId(node));
|
| + super.visitVariableSet(node);
|
| + }
|
| +
|
| visitDoStatement(ir.DoStatement node) {
|
| - computeForNode(node, computeLoopNodeId(node));
|
| + computeForNode(node, createLoopId(node));
|
| super.visitDoStatement(node);
|
| }
|
|
|
| visitForStatement(ir.ForStatement node) {
|
| - computeForNode(node, computeLoopNodeId(node));
|
| + computeForNode(node, createLoopId(node));
|
| super.visitForStatement(node);
|
| }
|
|
|
| visitForInStatement(ir.ForInStatement node) {
|
| - computeForNode(node, computeLoopNodeId(node));
|
| + computeForNode(node, createLoopId(node));
|
| super.visitForInStatement(node);
|
| }
|
|
|
| visitWhileStatement(ir.WhileStatement node) {
|
| - computeForNode(node, computeLoopNodeId(node));
|
| + computeForNode(node, createLoopId(node));
|
| super.visitWhileStatement(node);
|
| }
|
|
|
| visitBreakStatement(ir.BreakStatement node) {
|
| - computeForNode(node, computeGotoNodeId(node));
|
| + computeForNode(node, createGotoId(node));
|
| super.visitBreakStatement(node);
|
| }
|
|
|
| visitSwitchStatement(ir.SwitchStatement node) {
|
| - computeForNode(node, computeSwitchNodeId(node));
|
| + computeForNode(node, createSwitchId(node));
|
| super.visitSwitchStatement(node);
|
| }
|
|
|
| visitSwitchCase(ir.SwitchCase node) {
|
| - computeForNode(node, computeSwitchCaseNodeId(node));
|
| + computeForNode(node, createSwitchCaseId(node));
|
| super.visitSwitchCase(node);
|
| }
|
|
|
| visitContinueSwitchStatement(ir.ContinueSwitchStatement node) {
|
| - computeForNode(node, computeGotoNodeId(node));
|
| + computeForNode(node, createGotoId(node));
|
| super.visitContinueSwitchStatement(node);
|
| }
|
| }
|
|
|