• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {NodeOrigin} from "./source-resolver.js"
6import {MINIMUM_EDGE_SEPARATION} from "./edge.js"
7
8export const DEFAULT_NODE_BUBBLE_RADIUS = 12;
9export const NODE_INPUT_WIDTH = 50;
10export const MINIMUM_NODE_OUTPUT_APPROACH = 15;
11const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS;
12
13export function isNodeInitiallyVisible(node) {
14  return node.cfg;
15}
16
17function formatOrigin(origin) {
18  if (origin.nodeId) {
19    return `#${origin.nodeId} in phase ${origin.phase}/${origin.reducer}`;
20  }
21  if (origin.bytecodePosition) {
22    return `Bytecode line ${origin.bytecodePosition} in phase ${origin.phase}/${origin.reducer}`;
23  }
24  return "unknown origin";
25}
26
27export class GNode {
28  control: boolean;
29  opcode: string;
30  live: boolean;
31  inputs: Array<any>;
32  width: number;
33  properties: string;
34  title: string;
35  label: string;
36  origin: NodeOrigin;
37  outputs: Array<any>;
38  outputApproach: number;
39  type: string;
40  id: number;
41  x: number;
42  y: number;
43  visible: boolean;
44  rank: number;
45  opinfo: string;
46  labelbbox: { width: number, height: number };
47
48  isControl() {
49    return this.control;
50  }
51  isInput() {
52    return this.opcode == 'Parameter' || this.opcode.endsWith('Constant');
53  }
54  isLive() {
55    return this.live !== false;
56  }
57  isJavaScript() {
58    return this.opcode.startsWith('JS');
59  }
60  isSimplified() {
61    if (this.isJavaScript()) return false;
62    return this.opcode.endsWith('Phi') ||
63      this.opcode.startsWith('Boolean') ||
64      this.opcode.startsWith('Number') ||
65      this.opcode.startsWith('String') ||
66      this.opcode.startsWith('Change') ||
67      this.opcode.startsWith('Object') ||
68      this.opcode.startsWith('Reference') ||
69      this.opcode.startsWith('Any') ||
70      this.opcode.endsWith('ToNumber') ||
71      (this.opcode == 'AnyToBoolean') ||
72      (this.opcode.startsWith('Load') && this.opcode.length > 4) ||
73      (this.opcode.startsWith('Store') && this.opcode.length > 5);
74  }
75  isMachine() {
76    return !(this.isControl() || this.isInput() ||
77      this.isJavaScript() || this.isSimplified());
78  }
79  getTotalNodeWidth() {
80    var inputWidth = this.inputs.length * NODE_INPUT_WIDTH;
81    return Math.max(inputWidth, this.width);
82  }
83  getTitle() {
84    var propsString;
85    if (this.properties === undefined) {
86      propsString = "";
87    } else if (this.properties === "") {
88      propsString = "no properties";
89    } else {
90      propsString = "[" + this.properties + "]";
91    }
92    let title = this.title + "\n" + propsString + "\n" + this.opinfo;
93    if (this.origin) {
94      title += `\nOrigin: ${formatOrigin(this.origin)}`;
95    }
96    return title;
97  }
98  getDisplayLabel() {
99    var result = this.id + ":" + this.label;
100    if (result.length > 40) {
101      return this.id + ":" + this.opcode;
102    } else {
103      return result;
104    }
105  }
106  getType() {
107    return this.type;
108  }
109  getDisplayType() {
110    var type_string = this.type;
111    if (type_string == undefined) return "";
112    if (type_string.length > 24) {
113      type_string = type_string.substr(0, 25) + "...";
114    }
115    return type_string;
116  }
117  deepestInputRank() {
118    var deepestRank = 0;
119    this.inputs.forEach(function (e) {
120      if (e.isVisible() && !e.isBackEdge()) {
121        if (e.source.rank > deepestRank) {
122          deepestRank = e.source.rank;
123        }
124      }
125    });
126    return deepestRank;
127  }
128  areAnyOutputsVisible() {
129    var visibleCount = 0;
130    this.outputs.forEach(function (e) { if (e.isVisible())++visibleCount; });
131    if (this.outputs.length == visibleCount) return 2;
132    if (visibleCount != 0) return 1;
133    return 0;
134  }
135  setOutputVisibility(v) {
136    var result = false;
137    this.outputs.forEach(function (e) {
138      e.visible = v;
139      if (v) {
140        if (!e.target.visible) {
141          e.target.visible = true;
142          result = true;
143        }
144      }
145    });
146    return result;
147  }
148  setInputVisibility(i, v) {
149    var edge = this.inputs[i];
150    edge.visible = v;
151    if (v) {
152      if (!edge.source.visible) {
153        edge.source.visible = true;
154        return true;
155      }
156    }
157    return false;
158  }
159  getInputApproach(index) {
160    return this.y - MINIMUM_NODE_INPUT_APPROACH -
161      (index % 4) * MINIMUM_EDGE_SEPARATION - DEFAULT_NODE_BUBBLE_RADIUS
162  }
163  getOutputApproach(graph) {
164    return this.y + this.outputApproach + graph.getNodeHeight(this) +
165      + DEFAULT_NODE_BUBBLE_RADIUS;
166  }
167  getInputX(index) {
168    var result = this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2) +
169      (index - this.inputs.length + 1) * NODE_INPUT_WIDTH;
170    return result;
171  }
172  getOutputX() {
173    return this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2);
174  }
175  hasBackEdges() {
176    return (this.opcode == "Loop") ||
177      ((this.opcode == "Phi" || this.opcode == "EffectPhi") &&
178        this.inputs[this.inputs.length - 1].source.opcode == "Loop");
179  }
180};
181
182export const nodeToStr = (n: GNode) => "N" + n.id;
183