Commit c0f62209 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbolizer] Refactor NodeLabel from GNode

Refactor NodeLabel from GNode, which saves memory and is a step towards
decoupling the node layout from the graph structure.

Change-Id: I095a2f7a7ab28067161deffbc37952ae15410e0a
Notry: true
Bug: v8:7327
Reviewed-on: https://chromium-review.googlesource.com/c/1396418
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58571}
parent 76f88936
...@@ -13,7 +13,7 @@ const DEFAULT_NODE_ROW_SEPARATION = 130 ...@@ -13,7 +13,7 @@ const DEFAULT_NODE_ROW_SEPARATION = 130
var traceLayout = false; var traceLayout = false;
function newGraphOccupation(graph:Graph) { function newGraphOccupation(graph: Graph) {
var isSlotFilled = []; var isSlotFilled = [];
var maxSlot = 0; var maxSlot = 0;
var minSlot = 0; var minSlot = 0;
...@@ -122,12 +122,12 @@ function newGraphOccupation(graph:Graph) { ...@@ -122,12 +122,12 @@ function newGraphOccupation(graph:Graph) {
} }
var occupation = { var occupation = {
occupyNodeInputs: function (node) { occupyNodeInputs: function (node: GNode, showTypes: boolean) {
for (var i = 0; i < node.inputs.length; ++i) { for (var i = 0; i < node.inputs.length; ++i) {
if (node.inputs[i].isVisible()) { if (node.inputs[i].isVisible()) {
var edge = node.inputs[i]; var edge = node.inputs[i];
if (!edge.isBackEdge()) { if (!edge.isBackEdge()) {
var horizontalPos = edge.getInputHorizontalPosition(graph); var horizontalPos = edge.getInputHorizontalPosition(graph, showTypes);
if (traceLayout) { if (traceLayout) {
console.log("Occupying input " + i + " of " + node.id + " at " + horizontalPos); console.log("Occupying input " + i + " of " + node.id + " at " + horizontalPos);
} }
...@@ -201,13 +201,13 @@ function newGraphOccupation(graph:Graph) { ...@@ -201,13 +201,13 @@ function newGraphOccupation(graph:Graph) {
}); });
nodeOccupation = []; nodeOccupation = [];
}, },
clearNodeOutputs: function (source) { clearNodeOutputs: function (source: GNode, showTypes: boolean) {
source.outputs.forEach(function (edge) { source.outputs.forEach(function (edge) {
if (edge.isVisible()) { if (edge.isVisible()) {
var target = edge.target; var target = edge.target;
for (var i = 0; i < target.inputs.length; ++i) { for (var i = 0; i < target.inputs.length; ++i) {
if (target.inputs[i].source === source) { if (target.inputs[i].source === source) {
var horizontalPos = edge.getInputHorizontalPosition(graph); var horizontalPos = edge.getInputHorizontalPosition(graph, showTypes);
clearPositionRangeWithMargin(horizontalPos, clearPositionRangeWithMargin(horizontalPos,
horizontalPos, horizontalPos,
NODE_INPUT_WIDTH / 2); NODE_INPUT_WIDTH / 2);
...@@ -343,7 +343,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void { ...@@ -343,7 +343,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void {
} }
firstInput = false; firstInput = false;
} }
if (n.opcode != "Start" && n.opcode != "Phi" && n.opcode != "EffectPhi") { if (n.nodeLabel.opcode != "Start" && n.nodeLabel.opcode != "Phi" && n.nodeLabel.opcode != "EffectPhi" && n.nodeLabel.opcode != "InductionVariablePhi") {
n.rank = newRank; n.rank = newRank;
} }
} }
...@@ -371,7 +371,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void { ...@@ -371,7 +371,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void {
n.rank = maxRank + 1; n.rank = maxRank + 1;
}); });
var rankSets = []; const rankSets: Array<Array<GNode>> = [];
// Collect sets for each rank. // Collect sets for each rank.
for (const n of graph.nodes()) { for (const n of graph.nodes()) {
n.y = n.rank * (DEFAULT_NODE_ROW_SEPARATION + n.getNodeHeight(showTypes) + n.y = n.rank * (DEFAULT_NODE_ROW_SEPARATION + n.getNodeHeight(showTypes) +
...@@ -390,10 +390,10 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void { ...@@ -390,10 +390,10 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void {
// compact and not overlapping live input lines. // compact and not overlapping live input lines.
var occupation = newGraphOccupation(graph); var occupation = newGraphOccupation(graph);
rankSets.reverse().forEach(function (rankSet) { rankSets.reverse().forEach(function (rankSet: Array<GNode>) {
for (var i = 0; i < rankSet.length; ++i) { for (var i = 0; i < rankSet.length; ++i) {
occupation.clearNodeOutputs(rankSet[i]); occupation.clearNodeOutputs(rankSet[i], showTypes);
} }
if (traceLayout) { if (traceLayout) {
...@@ -402,8 +402,14 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void { ...@@ -402,8 +402,14 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void {
} }
var placedCount = 0; var placedCount = 0;
rankSet = rankSet.sort(function (a, b) { rankSet = rankSet.sort((a: GNode, b: GNode) => {
return a.visitOrderWithinRank < b.visitOrderWithinRank; if (a.visitOrderWithinRank < b.visitOrderWithinRank) {
return -1
} else if (a.visitOrderWithinRank == b.visitOrderWithinRank) {
return 0;
} else {
return 1;
}
}); });
for (var i = 0; i < rankSet.length; ++i) { for (var i = 0; i < rankSet.length; ++i) {
var nodeToPlace = rankSet[i]; var nodeToPlace = rankSet[i];
...@@ -434,7 +440,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void { ...@@ -434,7 +440,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void {
for (var i = 0; i < rankSet.length; ++i) { for (var i = 0; i < rankSet.length; ++i) {
var node = rankSet[i]; var node = rankSet[i];
occupation.occupyNodeInputs(node); occupation.occupyNodeInputs(node, showTypes);
} }
if (traceLayout) { if (traceLayout) {
...@@ -449,7 +455,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void { ...@@ -449,7 +455,7 @@ export function layoutNodeGraph(graph: Graph, showTypes: boolean): void {
}); });
graph.maxBackEdgeNumber = 0; graph.maxBackEdgeNumber = 0;
graph.forEachEdge((e) => { graph.forEachEdge((e: Edge) => {
if (e.isBackEdge()) { if (e.isBackEdge()) {
e.backEdgeNumber = ++graph.maxBackEdgeNumber; e.backEdgeNumber = ++graph.maxBackEdgeNumber;
} else { } else {
......
...@@ -32,7 +32,7 @@ interface GraphState { ...@@ -32,7 +32,7 @@ interface GraphState {
export class GraphView extends View implements PhaseView { export class GraphView extends View implements PhaseView {
divElement: d3.Selection<any, any, any, any>; divElement: d3.Selection<any, any, any, any>;
svg: d3.Selection<any, any, any, any>; svg: d3.Selection<any, any, any, any>;
showPhaseByName: (string) => void; showPhaseByName: (s: string) => void;
state: GraphState; state: GraphState;
selectionHandler: NodeSelectionHandler & ClearableHandler; selectionHandler: NodeSelectionHandler & ClearableHandler;
graphElement: d3.Selection<any, any, any, any>; graphElement: d3.Selection<any, any, any, any>;
...@@ -53,8 +53,8 @@ export class GraphView extends View implements PhaseView { ...@@ -53,8 +53,8 @@ export class GraphView extends View implements PhaseView {
return pane; return pane;
} }
constructor(id, broker, showPhaseByName: (string) => void) { constructor(idOrContainer: string | HTMLElement, broker: SelectionBroker, showPhaseByName: (s: string) => void) {
super(id); super(idOrContainer);
const view = this; const view = this;
this.broker = broker; this.broker = broker;
this.showPhaseByName = showPhaseByName; this.showPhaseByName = showPhaseByName;
...@@ -83,21 +83,21 @@ export class GraphView extends View implements PhaseView { ...@@ -83,21 +83,21 @@ export class GraphView extends View implements PhaseView {
broker.broadcastClear(this); broker.broadcastClear(this);
view.updateGraphVisibility(); view.updateGraphVisibility();
}, },
select: function (nodes, selected) { select: function (nodes: Iterable<GNode>, selected: boolean) {
let locations = []; let locations = [];
for (const node of nodes) { for (const node of nodes) {
if (node.sourcePosition) { if (node.nodeLabel.sourcePosition) {
locations.push(node.sourcePosition); locations.push(node.nodeLabel.sourcePosition);
} }
if (node.origin && node.origin.bytecodePosition) { if (node.nodeLabel.origin && node.nodeLabel.origin.bytecodePosition) {
locations.push({ bytecodePosition: node.origin.bytecodePosition }); locations.push({ bytecodePosition: node.nodeLabel.origin.bytecodePosition });
} }
} }
view.state.selection.select(nodes, selected); view.state.selection.select(nodes, selected);
broker.broadcastSourcePositionSelect(this, locations, selected); broker.broadcastSourcePositionSelect(this, locations, selected);
view.updateGraphVisibility(); view.updateGraphVisibility();
}, },
brokeredNodeSelect: function (locations, selected) { brokeredNodeSelect: function (locations, selected: boolean) {
if (!view.graph) return; if (!view.graph) return;
let selection = view.graph.nodes((n) => { let selection = view.graph.nodes((n) => {
return locations.has(nodeToStringKey(n)) return locations.has(nodeToStringKey(n))
...@@ -772,7 +772,7 @@ export class GraphView extends View implements PhaseView { ...@@ -772,7 +772,7 @@ export class GraphView extends View implements PhaseView {
.text(function (l) { .text(function (l) {
return d.getTitle(); return d.getTitle();
}) })
if (d.type != undefined) { if (d.nodeLabel.type != undefined) {
d3.select(this).append("text") d3.select(this).append("text")
.classed("label", true) .classed("label", true)
.classed("type", true) .classed("type", true)
......
import { GNode, MINIMUM_NODE_OUTPUT_APPROACH, NODE_INPUT_WIDTH } from "./node"; import { GNode } from "./node";
import { MAX_RANK_SENTINEL } from "./constants";
import { alignUp, measureText } from "./util";
import { Edge, MINIMUM_EDGE_SEPARATION } from "./edge"; import { Edge, MINIMUM_EDGE_SEPARATION } from "./edge";
export class Graph { export class Graph {
...@@ -20,30 +18,8 @@ export class Graph { ...@@ -20,30 +18,8 @@ export class Graph {
this.minGraphY = 0; this.minGraphY = 0;
this.maxGraphY = 1; this.maxGraphY = 1;
data.nodes.forEach((n: any) => { data.nodes.forEach((json_node: any) => {
n.__proto__ = GNode.prototype; this.nodeMap[json_node.id] = new GNode(json_node.nodeLabel);
n.visible = false;
n.x = 0;
n.y = 0;
if (typeof n.pos === "number") {
// Backwards compatibility.
n.sourcePosition = { scriptOffset: n.pos, inliningId: -1 };
}
n.rank = MAX_RANK_SENTINEL;
n.inputs = [];
n.outputs = [];
n.outputApproach = MINIMUM_NODE_OUTPUT_APPROACH;
// Every control node is a CFG node.
n.cfg = n.control;
this.nodeMap[n.id] = n;
n.displayLabel = n.getDisplayLabel();
n.labelbbox = measureText(n.displayLabel);
const typebbox = measureText(n.getDisplayType());
const innerwidth = Math.max(n.labelbbox.width, typebbox.width);
n.width = alignUp(innerwidth + NODE_INPUT_WIDTH * 2,
NODE_INPUT_WIDTH);
const innerheight = Math.max(n.labelbbox.height, typebbox.height);
n.normalheight = innerheight + 20;
}); });
data.edges.forEach((e: any) => { data.edges.forEach((e: any) => {
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function formatOrigin(origin) {
if (origin.nodeId) {
return `#${origin.nodeId} in phase ${origin.phase}/${origin.reducer}`;
}
if (origin.bytecodePosition) {
return `Bytecode line ${origin.bytecodePosition} in phase ${origin.phase}/${origin.reducer}`;
}
return "unknown origin";
}
export class NodeLabel {
id: number;
label: string;
title: string;
live: boolean;
properties: string;
sourcePosition: any;
origin: any;
opcode: string;
control: boolean;
opinfo: string;
type: string;
constructor(id: number, label: string, title: string, live: boolean, properties: string, sourcePosition: any, origin: any, opcode: string, control: boolean, opinfo: string, type: string) {
this.id = id;
this.label = label;
this.title = title;
this.live = live;
this.properties = properties;
this.sourcePosition = sourcePosition;
this.opcode = opcode;
this.control = control;
this.opinfo = opinfo;
this.type = type;
}
equals(that?: NodeLabel) {
if (!that) return false;
if (this.id != that.id) return false;
if (this.label != that.label) return false;
if (this.title != that.title) return false;
if (this.live != that.live) return false;
if (this.properties != that.properties) return false;
if (this.opcode != that.opcode) return false;
if (this.control != that.control) return false;
if (this.opinfo != that.opinfo) return false;
if (this.type != that.type) return false;
return true;
}
getTitle() {
let propsString = "";
if (this.properties === "") {
propsString = "no properties";
} else {
propsString = "[" + this.properties + "]";
}
let title = this.title + "\n" + propsString + "\n" + this.opinfo;
if (this.origin) {
title += `\nOrigin: ${formatOrigin(this.origin)}`;
}
return title;
}
getDisplayLabel() {
const result = `${this.id}: ${this.label}`;
if (result.length > 40) {
return `${this.id}: ${this.opcode}`;
}
return result;
}
}
\ No newline at end of file
...@@ -2,74 +2,83 @@ ...@@ -2,74 +2,83 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import { NodeOrigin } from "../src/source-resolver"
import { MINIMUM_EDGE_SEPARATION, Edge } from "../src/edge" import { MINIMUM_EDGE_SEPARATION, Edge } from "../src/edge"
import { NodeLabel } from "./node-label";
import { MAX_RANK_SENTINEL } from "./constants";
import { alignUp, measureText } from "./util";
export const DEFAULT_NODE_BUBBLE_RADIUS = 12; export const DEFAULT_NODE_BUBBLE_RADIUS = 12;
export const NODE_INPUT_WIDTH = 50; export const NODE_INPUT_WIDTH = 50;
export const MINIMUM_NODE_OUTPUT_APPROACH = 15; export const MINIMUM_NODE_OUTPUT_APPROACH = 15;
const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS; const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS;
function formatOrigin(origin) {
if (origin.nodeId) {
return `#${origin.nodeId} in phase ${origin.phase}/${origin.reducer}`;
}
if (origin.bytecodePosition) {
return `Bytecode line ${origin.bytecodePosition} in phase ${origin.phase}/${origin.reducer}`;
}
return "unknown origin";
}
export class GNode { export class GNode {
control: boolean; id: number;
opcode: string; nodeLabel: NodeLabel;
live: boolean; displayLabel: string;
inputs: Array<Edge>; inputs: Array<Edge>;
width: number;
properties: string;
title: string;
label: string;
origin: NodeOrigin;
outputs: Array<Edge>; outputs: Array<Edge>;
outputApproach: number; visible: boolean;
type: string;
id: number;
x: number; x: number;
y: number; y: number;
visible: boolean;
rank: number; rank: number;
opinfo: string; outputApproach: number;
labelbbox: { width: number, height: number };
visitOrderWithinRank: number;
cfg: boolean; cfg: boolean;
labelbbox: { width: number, height: number };
width: number;
normalheight: number; normalheight: number;
visitOrderWithinRank: number;
constructor(nodeLabel: NodeLabel) {
this.id = nodeLabel.id;
this.nodeLabel = nodeLabel;
this.displayLabel = nodeLabel.getDisplayLabel();
this.inputs = [];
this.outputs = [];
this.visible = false;
this.x = 0;
this.y = 0;
this.rank = MAX_RANK_SENTINEL;
this.outputApproach = MINIMUM_NODE_OUTPUT_APPROACH;
// Every control node is a CFG node.
this.cfg = nodeLabel.control;
this.labelbbox = measureText(this.displayLabel);
const typebbox = measureText(this.getDisplayType());
const innerwidth = Math.max(this.labelbbox.width, typebbox.width);
this.width = alignUp(innerwidth + NODE_INPUT_WIDTH * 2,
NODE_INPUT_WIDTH);
const innerheight = Math.max(this.labelbbox.height, typebbox.height);
this.normalheight = innerheight + 20;
this.visitOrderWithinRank = 0;
}
isControl() { isControl() {
return this.control; return this.nodeLabel.control;
} }
isInput() { isInput() {
return this.opcode == 'Parameter' || this.opcode.endsWith('Constant'); return this.nodeLabel.opcode == 'Parameter' || this.nodeLabel.opcode.endsWith('Constant');
} }
isLive() { isLive() {
return this.live !== false; return this.nodeLabel.live !== false;
} }
isJavaScript() { isJavaScript() {
return this.opcode.startsWith('JS'); return this.nodeLabel.opcode.startsWith('JS');
} }
isSimplified() { isSimplified() {
if (this.isJavaScript()) return false; if (this.isJavaScript()) return false;
return this.opcode.endsWith('Phi') || const opcode = this.nodeLabel.opcode;
this.opcode.startsWith('Boolean') || return opcode.endsWith('Phi') ||
this.opcode.startsWith('Number') || opcode.startsWith('Boolean') ||
this.opcode.startsWith('String') || opcode.startsWith('Number') ||
this.opcode.startsWith('Change') || opcode.startsWith('String') ||
this.opcode.startsWith('Object') || opcode.startsWith('Change') ||
this.opcode.startsWith('Reference') || opcode.startsWith('Object') ||
this.opcode.startsWith('Any') || opcode.startsWith('Reference') ||
this.opcode.endsWith('ToNumber') || opcode.startsWith('Any') ||
(this.opcode == 'AnyToBoolean') || opcode.endsWith('ToNumber') ||
(this.opcode.startsWith('Load') && this.opcode.length > 4) || (opcode == 'AnyToBoolean') ||
(this.opcode.startsWith('Store') && this.opcode.length > 5); (opcode.startsWith('Load') && opcode.length > 4) ||
(opcode.startsWith('Store') && opcode.length > 5);
} }
isMachine() { isMachine() {
return !(this.isControl() || this.isInput() || return !(this.isControl() || this.isInput() ||
...@@ -80,33 +89,16 @@ export class GNode { ...@@ -80,33 +89,16 @@ export class GNode {
return Math.max(inputWidth, this.width); return Math.max(inputWidth, this.width);
} }
getTitle() { getTitle() {
var propsString; return this.nodeLabel.getTitle();
if (this.properties === undefined) {
propsString = "";
} else if (this.properties === "") {
propsString = "no properties";
} else {
propsString = "[" + this.properties + "]";
}
let title = this.title + "\n" + propsString + "\n" + this.opinfo;
if (this.origin) {
title += `\nOrigin: ${formatOrigin(this.origin)}`;
}
return title;
} }
getDisplayLabel() { getDisplayLabel() {
var result = this.id + ":" + this.label; return this.nodeLabel.getDisplayLabel();
if (result.length > 40) {
return this.id + ":" + this.opcode;
} else {
return result;
}
} }
getType() { getType() {
return this.type; return this.nodeLabel.type;
} }
getDisplayType() { getDisplayType() {
var type_string = this.type; var type_string = this.nodeLabel.type;
if (type_string == undefined) return ""; if (type_string == undefined) return "";
if (type_string.length > 24) { if (type_string.length > 24) {
type_string = type_string.substr(0, 25) + "..."; type_string = type_string.substr(0, 25) + "...";
...@@ -159,14 +151,14 @@ export class GNode { ...@@ -159,14 +151,14 @@ export class GNode {
return this.y - MINIMUM_NODE_INPUT_APPROACH - return this.y - MINIMUM_NODE_INPUT_APPROACH -
(index % 4) * MINIMUM_EDGE_SEPARATION - DEFAULT_NODE_BUBBLE_RADIUS (index % 4) * MINIMUM_EDGE_SEPARATION - DEFAULT_NODE_BUBBLE_RADIUS
} }
getNodeHeight(showTypes:boolean): number { getNodeHeight(showTypes: boolean): number {
if (showTypes) { if (showTypes) {
return this.normalheight + this.labelbbox.height; return this.normalheight + this.labelbbox.height;
} else { } else {
return this.normalheight; return this.normalheight;
} }
} }
getOutputApproach(showTypes:boolean) { getOutputApproach(showTypes: boolean) {
return this.y + this.outputApproach + this.getNodeHeight(showTypes) + return this.y + this.outputApproach + this.getNodeHeight(showTypes) +
+ DEFAULT_NODE_BUBBLE_RADIUS; + DEFAULT_NODE_BUBBLE_RADIUS;
} }
...@@ -179,9 +171,9 @@ export class GNode { ...@@ -179,9 +171,9 @@ export class GNode {
return this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2); return this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2);
} }
hasBackEdges() { hasBackEdges() {
return (this.opcode == "Loop") || return (this.nodeLabel.opcode == "Loop") ||
((this.opcode == "Phi" || this.opcode == "EffectPhi") && ((this.nodeLabel.opcode == "Phi" || this.nodeLabel.opcode == "EffectPhi" || this.nodeLabel.opcode == "InductionVariablePhi") &&
this.inputs[this.inputs.length - 1].source.opcode == "Loop"); this.inputs[this.inputs.length - 1].source.nodeLabel.opcode == "Loop");
} }
}; };
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import { sortUnique, anyToString } from "../src/util" import { sortUnique, anyToString } from "../src/util"
import { NodeLabel } from "./node-label";
function sourcePositionLe(a, b) { function sourcePositionLe(a, b) {
if (a.inliningId == b.inliningId) { if (a.inliningId == b.inliningId) {
...@@ -87,6 +88,7 @@ interface GraphPhase { ...@@ -87,6 +88,7 @@ interface GraphPhase {
name: string; name: string;
data: any; data: any;
highestNodeId: number; highestNodeId: number;
nodeLabelMap: Array<NodeLabel>;
} }
type Phase = GraphPhase | InstructionsPhase | OtherPhase; type Phase = GraphPhase | InstructionsPhase | OtherPhase;
...@@ -333,8 +335,14 @@ export class SourceResolver { ...@@ -333,8 +335,14 @@ export class SourceResolver {
this.positionToNodes.set(key, []); this.positionToNodes.set(key, []);
} }
const A = this.positionToNodes.get(key); const A = this.positionToNodes.get(key);
if (!A.includes(node.id)) A.push("" + node.id); if (!A.includes(node.id)) A.push(`${node.id}`);
} }
// Backwards compatibility.
if (typeof node.pos === "number") {
node.sourcePosition = { scriptOffset: node.pos, inliningId: -1 };
}
} }
} }
...@@ -435,6 +443,7 @@ export class SourceResolver { ...@@ -435,6 +443,7 @@ export class SourceResolver {
} }
parsePhases(phases) { parsePhases(phases) {
const nodeLabelMap = [];
for (const [, phase] of Object.entries<Phase>(phases)) { for (const [, phase] of Object.entries<Phase>(phases)) {
switch (phase.type) { switch (phase.type) {
case 'disassembly': case 'disassembly':
...@@ -463,6 +472,8 @@ export class SourceResolver { ...@@ -463,6 +472,8 @@ export class SourceResolver {
const graphPhase: GraphPhase = Object.assign(phase, { highestNodeId: 0 }); const graphPhase: GraphPhase = Object.assign(phase, { highestNodeId: 0 });
this.phases.push(graphPhase); this.phases.push(graphPhase);
this.recordOrigins(graphPhase); this.recordOrigins(graphPhase);
this.internNodeLabels(graphPhase, nodeLabelMap);
graphPhase.nodeLabelMap = nodeLabelMap.slice();
this.phaseNames.set(graphPhase.name, this.phases.length); this.phaseNames.set(graphPhase.name, this.phases.length);
break; break;
default: default:
...@@ -471,6 +482,19 @@ export class SourceResolver { ...@@ -471,6 +482,19 @@ export class SourceResolver {
} }
} }
internNodeLabels(phase: GraphPhase, nodeLabelMap: Array<NodeLabel>) {
for (const n of phase.data.nodes) {
const label = new NodeLabel(n.id, n.label, n.title, n.live,
n.properties, n.sourcePosition, n.origin, n.opcode, n.control,
n.opinfo, n.type);
const previous = nodeLabelMap[label.id];
if (!label.equals(previous)) {
nodeLabelMap[label.id] = label;
}
n.nodeLabel = nodeLabelMap[label.id];
}
}
repairPhaseId(anyPhaseId) { repairPhaseId(anyPhaseId) {
return Math.max(0, Math.min(anyPhaseId, this.phases.length - 1)) return Math.max(0, Math.min(anyPhaseId, this.phases.length - 1))
} }
......
...@@ -31,6 +31,6 @@ export abstract class View { ...@@ -31,6 +31,6 @@ export abstract class View {
} }
export interface PhaseView { export interface PhaseView {
onresize(); onresize(): void;
searchInputAction(searchInput: HTMLInputElement, e: Event); searchInputAction(searchInput: HTMLInputElement, e: Event): void;
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
"src/node.ts", "src/node.ts",
"src/edge.ts", "src/edge.ts",
"src/graph.ts", "src/graph.ts",
"src/node-label.ts",
"src/source-resolver.ts", "src/source-resolver.ts",
"src/selection.ts", "src/selection.ts",
"src/selection-broker.ts", "src/selection-broker.ts",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment