Chromium Code Reviews| 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..24ee672ab1e74f53657f9c476de3bf8041a4024c 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,58 @@ 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 '${elementId.name}:$value'; |
|
Siggi Cherem (dart-lang)
2017/08/31 23:04:03
to make it easier to understand the ID when readin
Johnni Winther
2017/09/01 14:00:03
Good idea. Encoding changed to
'element: Sub.meth
|
| + case IdKind.node: |
| + return value; |
| + case IdKind.invoke: |
| + return '()$value'; |
| + case IdKind.update: |
| + return '=$value'; |
| + } |
| + throw new UnsupportedError("Unexpected id kind: ${id.kind}"); |
| + } |
| + |
| + static IdValue decode(int offset, String text) { |
| + int colonPos = text.indexOf(':'); |
| + Id id; |
| + String expected; |
| + if (colonPos == -1) { |
| + if (text.startsWith('=')) { |
| + id = new NodeId(offset, IdKind.update); |
| + expected = text.substring(1); |
| + } else if (text.startsWith('()')) { |
| + id = new NodeId(offset, IdKind.invoke); |
| + expected = text.substring(2); |
| + } else { |
| + id = new NodeId(offset, IdKind.node); |
| + expected = text; |
| + } |
| + } else { |
| + id = new ElementId(text.substring(0, colonPos)); |
| + expected = text.substring(colonPos + 1); |
| + } |
| + 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 +103,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 +171,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 +190,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 +199,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 +219,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 +236,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 computeLoopNodeId(ast.Node node) => computeDefaultNodeId(node); |
| + NodeId createInvokeId(ast.Node node) { |
| + return new NodeId(node.getBeginToken().charOffset, IdKind.invoke); |
| + } |
| - NodeId computeGotoNodeId(ast.Node node) => computeDefaultNodeId(node); |
| + NodeId createUpdateId(ast.Node node) { |
| + return new NodeId(node.getBeginToken().charOffset, IdKind.update); |
| + } |
| - NodeId computeSwitchNodeId(ast.SwitchStatement node) => |
| - computeDefaultNodeId(node); |
| + NodeId createLoopId(ast.Node node) => computeDefaultNodeId(node); |
| - NodeId computeSwitchCaseNodeId(ast.SwitchCase node) { |
| + NodeId createGotoId(ast.Node node) => computeDefaultNodeId(node); |
| + |
| + NodeId createSwitchId(ast.SwitchStatement node) => computeDefaultNodeId(node); |
| + |
| + NodeId createSwitchCaseId(ast.SwitchCase node) { |
| ast.Node position; |
| for (ast.Node child in node.labelsAndCases) { |
| if (child.asCaseMatch() != null) { |
| @@ -213,11 +306,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 +353,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 +405,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 +422,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 +455,7 @@ abstract class IrDataExtractor extends ir.Visitor { |
| } |
| visitMethodInvocation(ir.MethodInvocation node) { |
| - computeForNode(node, computeDefaultNodeId(node)); |
| + computeForNode(node, createInvokeId(node)); |
| super.visitMethodInvocation(node); |
| } |
| @@ -335,7 +465,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 +486,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); |
| } |
| } |