| // Copyright (c) 2017, 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. |
| |
| import 'package:compiler/src/elements/elements.dart'; |
| 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:kernel/ast.dart' as ir; |
| |
| enum IdKind { element, node } |
| |
| /// Id for a code point or element with type inference information. |
| abstract class Id { |
| IdKind get kind; |
| } |
| |
| /// Id for an element with type inference information. |
| // TODO(johnniwinther): Support local variables, functions and parameters. |
| class ElementId implements Id { |
| final String className; |
| final String memberName; |
| |
| factory ElementId(String text) { |
| int dotPos = text.indexOf('.'); |
| if (dotPos != -1) { |
| return new ElementId.internal( |
| text.substring(dotPos + 1), text.substring(0, dotPos)); |
| } else { |
| return new ElementId.internal(text); |
| } |
| } |
| |
| ElementId.internal(this.memberName, [this.className]); |
| |
| int get hashCode => className.hashCode * 13 + memberName.hashCode * 17; |
| |
| bool operator ==(other) { |
| if (identical(this, other)) return true; |
| if (other is! ElementId) return false; |
| return className == other.className && memberName == other.memberName; |
| } |
| |
| IdKind get kind => IdKind.element; |
| |
| String toString() => |
| className != null ? '$className.$memberName' : memberName; |
| } |
| |
| /// 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; |
| |
| const NodeId(this.value); |
| |
| int get hashCode => value.hashCode; |
| |
| bool operator ==(other) { |
| if (identical(this, other)) return true; |
| if (other is! NodeId) return false; |
| return value == other.value; |
| } |
| |
| IdKind get kind => IdKind.node; |
| |
| String toString() => value.toString(); |
| } |
| |
| abstract class AstEnumeratorMixin { |
| TreeElements get elements; |
| |
| ElementId computeElementId(AstElement element) { |
| String memberName = element.name; |
| if (element.isSetter) { |
| memberName += '='; |
| } |
| String className = element.enclosingClass?.name; |
| return new ElementId.internal(memberName, className); |
| } |
| |
| NodeId computeAccessId(ast.Send node, AccessSemantics access) { |
| switch (access.kind) { |
| case AccessKind.DYNAMIC_PROPERTY: |
| return new NodeId(node.selector.getBeginToken().charOffset); |
| default: |
| return new NodeId(node.getBeginToken().charOffset); |
| } |
| } |
| |
| NodeId computeNodeId(ast.Send node) { |
| var sendStructure = elements.getSendStructure(node); |
| if (sendStructure == null) return null; |
| switch (sendStructure.kind) { |
| case SendStructureKind.GET: |
| case SendStructureKind.INVOKE: |
| case SendStructureKind.INCOMPATIBLE_INVOKE: |
| return computeAccessId(node, sendStructure.semantics); |
| default: |
| return new NodeId(node.getBeginToken().charOffset); |
| } |
| } |
| } |
| |
| /// Visitor that finds the AST node or element corresponding to an [Id]. |
| class AstIdFinder extends ast.Visitor with AstEnumeratorMixin { |
| Id soughtId; |
| var /*AstElement|ast.Node*/ found; |
| final TreeElements elements; |
| |
| AstIdFinder(this.elements); |
| |
| /// Visits the subtree of [root] returns the [ast.Node] or [AstElement] |
| /// corresponding to [id]. |
| /*AstElement|ast.Node*/ find(ast.Node root, Id id) { |
| soughtId = id; |
| root.accept(this); |
| var result = found; |
| found = null; |
| return result; |
| } |
| |
| visit(ast.Node node) { |
| if (found == null) { |
| node?.accept(this); |
| } |
| } |
| |
| visitNode(ast.Node node) { |
| if (found == null) { |
| node.visitChildren(this); |
| } |
| } |
| |
| visitSend(ast.Send node) { |
| if (found == null) { |
| visitNode(node); |
| Id id = computeNodeId(node); |
| if (id == soughtId) { |
| found = node; |
| } |
| } |
| } |
| |
| visitVariableDefinitions(ast.VariableDefinitions node) { |
| if (found == null) { |
| for (ast.Node child in node.definitions) { |
| AstElement element = elements[child]; |
| if (element != null) { |
| Id id = computeElementId(element); |
| if (id == soughtId) { |
| found = element; |
| return; |
| } |
| } |
| } |
| visitNode(node); |
| } |
| } |
| |
| visitFunctionExpression(ast.FunctionExpression node) { |
| if (found == null) { |
| AstElement element = elements.getFunctionDefinition(node); |
| if (element != null) { |
| Id id = computeElementId(element); |
| if (id == soughtId) { |
| found = element; |
| return; |
| } |
| } |
| visitNode(node); |
| } |
| } |
| } |
| |
| abstract class IrEnumeratorMixin { |
| Id computeElementId(ir.Member node) { |
| String className; |
| if (node.enclosingClass != null) { |
| className = node.enclosingClass.name; |
| } |
| String memberName = node.name.name; |
| if (node is ir.Procedure && node.kind == ir.ProcedureKind.Setter) { |
| memberName += '='; |
| } |
| return new ElementId.internal(memberName, className); |
| } |
| |
| Id computeNodeId(ir.Node node) { |
| if (node is ir.MethodInvocation) { |
| assert(node.fileOffset != ir.TreeNode.noOffset); |
| return new NodeId(node.fileOffset); |
| } else if (node is ir.PropertyGet) { |
| assert(node.fileOffset != ir.TreeNode.noOffset); |
| return new NodeId(node.fileOffset); |
| } |
| return null; |
| } |
| } |
| |
| /// Visitor that finds the IR node corresponding to an [Id]. |
| class IrIdFinder extends ir.Visitor with IrEnumeratorMixin { |
| Id soughtId; |
| ir.Node found; |
| |
| /// Visits the subtree of [root] returns the [ir.Node] corresponding to [id]. |
| ir.Node find(ir.Node root, Id id) { |
| soughtId = id; |
| root.accept(this); |
| var result = found; |
| found = null; |
| return result; |
| } |
| |
| defaultNode(ir.Node node) { |
| if (found == null) { |
| Id id = computeNodeId(node); |
| if (id == soughtId) { |
| found = node; |
| return; |
| } |
| node.visitChildren(this); |
| } |
| } |
| |
| defaultMember(ir.Member node) { |
| if (found == null) { |
| Id id = computeElementId(node); |
| if (id == soughtId) { |
| found = node; |
| return; |
| } |
| defaultNode(node); |
| } |
| } |
| } |