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); |
} |
} |