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