Commit 41d5c9cb authored by Danylo Boiko's avatar Danylo Boiko Committed by V8 LUCI CQ

[turbolizer] Source and bytecode positions

New features:
- bytecode source view handlers
- turboshaft's nodes origins
- turboshaft's nodes history
- turboshaft's nodes source/bytecode positions

Bug: v8:7327
Change-Id: Icb240dd84762284f1aa37db3c93bd133f8e70960
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3829481Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Danylo Boiko <danielboyko02@gmail.com>
Cr-Commit-Position: refs/heads/main@{#82682}
parent bf5e3a8a
......@@ -30,6 +30,10 @@
<td>b</td>
<td>Show graph with selected nodes for previous phase</td>
</tr>
<tr>
<td>h</td>
<td>Show hovered node's history</td>
</tr>
<tr>
<td>a</td>
<td>Select all nodes</td>
......@@ -61,10 +65,6 @@
<td>u</td>
<td>Hide unselected nodes</td>
</tr>
<tr>
<td>h</td>
<td>Show hovered node's history</td>
</tr>
</table>
</div>
</div>
......
......@@ -7,13 +7,14 @@ import { storageGetItem, storageSetItem } from "./common/util";
import { GraphView } from "./views/graph-view";
import { ScheduleView } from "./views/schedule-view";
import { SequenceView } from "./views/sequence-view";
import { GenericPhase, SourceResolver } from "./source-resolver";
import { DynamicPhase, SourceResolver } from "./source-resolver";
import { SelectionBroker } from "./selection/selection-broker";
import { PhaseView, View } from "./views/view";
import { GraphPhase } from "./phases/graph-phase/graph-phase";
import { PhaseType } from "./phases/phase";
import { TurboshaftGraphView } from "./views/turboshaft-graph-view";
import { SelectionStorage } from "./selection/selection-storage";
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
const toolboxHTML = `
<div class="graph-toolbox">
......@@ -90,21 +91,23 @@ export class GraphMultiView extends View {
const lastPhaseIndex = storageGetItem("lastSelectedPhase");
const initialPhaseIndex = this.sourceResolver.repairPhaseId(lastPhaseIndex);
this.selectMenu.selectedIndex = initialPhaseIndex;
this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex));
this.displayPhase(this.sourceResolver.getDynamicPhase(initialPhaseIndex));
}
public displayPhaseByName(phaseName: string, selection?: SelectionStorage): void {
this.currentPhaseView.hide();
const phaseId = this.sourceResolver.getPhaseIdByName(phaseName);
this.selectMenu.selectedIndex = phaseId;
this.displayPhase(this.sourceResolver.getPhase(phaseId), selection);
this.displayPhase(this.sourceResolver.getDynamicPhase(phaseId), selection);
}
public onresize(): void {
this.currentPhaseView?.onresize();
}
private displayPhase(phase: GenericPhase, selection?: SelectionStorage): void {
private displayPhase(phase: DynamicPhase, selection?: SelectionStorage): void {
this.sourceResolver.positions = phase.positions;
this.sourceResolver.instructionsPhase = phase.instructionsPhase;
if (phase.type == PhaseType.Graph) {
this.displayPhaseView(this.graph, phase, selection);
} else if (phase.type == PhaseType.TurboshaftGraph) {
......@@ -116,7 +119,7 @@ export class GraphMultiView extends View {
}
}
private displayPhaseView(view: PhaseView, data: GenericPhase, selection?: SelectionStorage):
private displayPhaseView(view: PhaseView, data: DynamicPhase, selection?: SelectionStorage):
void {
const rememberedSelection = selection ? selection : this.hideCurrentPhase();
view.initializeContent(data, rememberedSelection);
......@@ -126,8 +129,8 @@ export class GraphMultiView extends View {
private displayNextGraphPhase(): void {
let nextPhaseIndex = this.selectMenu.selectedIndex + 1;
while (nextPhaseIndex < this.sourceResolver.phases.length) {
const nextPhase = this.sourceResolver.getPhase(nextPhaseIndex);
if (nextPhase.isGraph()) {
const nextPhase = this.sourceResolver.getDynamicPhase(nextPhaseIndex);
if (nextPhase && nextPhase.isGraph()) {
this.selectMenu.selectedIndex = nextPhaseIndex;
storageSetItem("lastSelectedPhase", nextPhaseIndex);
this.displayPhase(nextPhase);
......@@ -140,8 +143,8 @@ export class GraphMultiView extends View {
private displayPreviousGraphPhase(): void {
let previousPhaseIndex = this.selectMenu.selectedIndex - 1;
while (previousPhaseIndex >= 0) {
const previousPhase = this.sourceResolver.getPhase(previousPhaseIndex);
if (previousPhase.isGraph()) {
const previousPhase = this.sourceResolver.getDynamicPhase(previousPhaseIndex);
if (previousPhase && previousPhase.isGraph()) {
this.selectMenu.selectedIndex = previousPhaseIndex;
storageSetItem("lastSelectedPhase", previousPhaseIndex);
this.displayPhase(previousPhase);
......@@ -157,7 +160,8 @@ export class GraphMultiView extends View {
for (const phase of view.sourceResolver.phases) {
const optionElement = document.createElement("option");
let maxNodeId = "";
if (phase instanceof GraphPhase && phase.highestNodeId != 0) {
if ((phase instanceof GraphPhase || phase instanceof TurboshaftGraphPhase)
&& phase.highestNodeId != 0) {
maxNodeId = ` ${phase.highestNodeId}`;
}
optionElement.text = `${phase.name}${maxNodeId}`;
......@@ -166,7 +170,7 @@ export class GraphMultiView extends View {
this.selectMenu.onchange = function (this: HTMLSelectElement) {
const phaseIndex = this.selectedIndex;
storageSetItem("lastSelectedPhase", phaseIndex);
view.displayPhase(view.sourceResolver.getPhase(phaseIndex));
view.displayPhase(view.sourceResolver.getDynamicPhase(phaseIndex));
};
}
......
......@@ -11,7 +11,8 @@ export class NodeLabel {
title: string;
live: boolean;
properties: string;
sourcePosition: SourcePosition | BytecodePosition;
sourcePosition: SourcePosition;
bytecodePosition: BytecodePosition;
origin: NodeOrigin | BytecodeOrigin;
opcode: string;
control: boolean;
......@@ -20,15 +21,16 @@ export class NodeLabel {
inplaceUpdatePhase: string;
constructor(id: number, label: string, title: string, live: boolean,
properties: string, sourcePosition: SourcePosition | BytecodePosition,
origin: NodeOrigin | BytecodeOrigin, opcode: string, control: boolean,
opinfo: string, type: string) {
properties: string, sourcePosition: SourcePosition,
bytecodePosition: BytecodePosition, origin: NodeOrigin | BytecodeOrigin,
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.bytecodePosition = bytecodePosition;
this.origin = origin;
this.opcode = opcode;
this.control = control;
......
......@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { GraphNode } from "./phases/graph-phase/graph-node";
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
export abstract class Origin {
phase: string;
reducer: string;
......@@ -14,10 +17,13 @@ export abstract class Origin {
export class NodeOrigin extends Origin {
nodeId: number;
node: GraphNode | TurboshaftGraphNode;
constructor(nodeId: number, phase: string, reducer: string) {
constructor(nodeId: number, node: GraphNode | TurboshaftGraphNode, phase: string,
reducer: string) {
super(phase, reducer);
this.nodeId = nodeId;
this.node = node;
}
public identifier(): string {
......
......@@ -7,6 +7,7 @@ import { alignUp, measureText } from "../../common/util";
import { NodeLabel } from "../../node-label";
import { Node } from "../../node";
import { GraphEdge } from "./graph-edge";
import { NodeOrigin } from "../../origin";
export class GraphNode extends Node<GraphEdge> {
nodeLabel: NodeLabel;
......@@ -79,6 +80,19 @@ export class GraphNode extends Node<GraphEdge> {
return this.nodeLabel.getTitle();
}
public getHistoryLabel(): string {
return `${this.id} ${this.nodeLabel.opcode}`;
}
public getNodeOrigin(): NodeOrigin {
const origin = this.nodeLabel.origin;
return origin instanceof NodeOrigin ? origin : null;
}
public getInplaceUpdatePhase(): string {
return this?.nodeLabel?.inplaceUpdatePhase;
}
public getDisplayLabel(): string {
return this.nodeLabel.getDisplayLabel();
}
......
......@@ -5,81 +5,109 @@
import { Phase, PhaseType } from "../phase";
import { NodeLabel } from "../../node-label";
import { BytecodeOrigin, NodeOrigin } from "../../origin";
import { SourcePosition } from "../../position";
import { GraphNode } from "./graph-node";
import { GraphEdge } from "./graph-edge";
import { Source } from "../../source";
import { InstructionsPhase } from "../instructions-phase";
import {
BytecodePosition,
InliningPosition,
PositionsContainer,
SourcePosition
} from "../../position";
export class GraphPhase extends Phase {
highestNodeId: number;
data: GraphData;
stateType: GraphStateType;
nodeLabelMap: Array<NodeLabel>;
instructionsPhase: InstructionsPhase;
nodeIdToNodeMap: Array<GraphNode>;
originIdToNodesMap: Map<string, Array<GraphNode>>;
positions: PositionsContainer;
highestNodeId: number;
rendered: boolean;
transform: { x: number, y: number, scale: number };
constructor(name: string, highestNodeId: number, dataJson, nodeLabelMap?: Array<NodeLabel>) {
constructor(name: string, dataJson, nodeMap: Array<GraphNode>, sources: Array<Source>,
inlinings: Array<InliningPosition>) {
super(name, PhaseType.Graph);
this.highestNodeId = highestNodeId;
this.data = new GraphData();
this.stateType = GraphStateType.NeedToFullRebuild;
this.instructionsPhase = new InstructionsPhase();
this.nodeIdToNodeMap = new Array<GraphNode>();
this.originIdToNodesMap = new Map<string, Array<GraphNode>>();
this.positions = new PositionsContainer();
this.highestNodeId = 0;
this.rendered = false;
this.parseDataFromJSON(dataJson, nodeLabelMap);
this.nodeLabelMap = nodeLabelMap?.slice();
this.parseDataFromJSON(dataJson, nodeMap, sources, inlinings);
}
private parseDataFromJSON(dataJson, nodeLabelMap: Array<NodeLabel>): void {
private parseDataFromJSON(dataJson, nodeMap: Array<GraphNode>, sources: Array<Source>,
inlinings: Array<InliningPosition>): void {
this.data = new GraphData();
this.nodeIdToNodeMap = this.parseNodesFromJSON(dataJson.nodes, nodeLabelMap);
this.parseNodesFromJSON(dataJson.nodes, nodeMap, sources, inlinings);
this.parseEdgesFromJSON(dataJson.edges);
}
private parseNodesFromJSON(nodesJSON, nodeLabelMap: Array<NodeLabel>): Array<GraphNode> {
const nodeIdToNodeMap = new Array<GraphNode>();
private parseNodesFromJSON(nodesJSON, nodeMap: Array<GraphNode>, sources: Array<Source>,
inlinings: Array<InliningPosition>): void {
for (const node of nodesJSON) {
let sourcePosition: SourcePosition = null;
const sourcePositionJson = node.sourcePosition;
if (sourcePositionJson) {
const scriptOffset = sourcePositionJson.scriptOffset;
const inliningId = sourcePositionJson.inliningId;
sourcePosition = new SourcePosition(scriptOffset, inliningId);
}
let origin: NodeOrigin | BytecodeOrigin = null;
const jsonOrigin = node.origin;
if (jsonOrigin) {
if (jsonOrigin.nodeId) {
origin = new NodeOrigin(jsonOrigin.nodeId, jsonOrigin.phase, jsonOrigin.reducer);
let bytecodePosition: BytecodePosition = null;
const originJson = node.origin;
if (originJson) {
const nodeId = originJson.nodeId;
if (nodeId) {
origin = new NodeOrigin(nodeId, nodeMap[nodeId], originJson.phase, originJson.reducer);
bytecodePosition = nodeMap[nodeId]?.nodeLabel.bytecodePosition;
} else {
origin = new BytecodeOrigin(jsonOrigin.bytecodePosition, jsonOrigin.phase,
jsonOrigin.reducer);
origin = new BytecodeOrigin(originJson.bytecodePosition, originJson.phase,
originJson.reducer);
const inliningId = sourcePosition ? sourcePosition.inliningId : -1;
bytecodePosition = new BytecodePosition(originJson.bytecodePosition, inliningId);
}
}
let sourcePosition: SourcePosition = null;
if (node.sourcePosition) {
const scriptOffset = node.sourcePosition.scriptOffset;
const inliningId = node.sourcePosition.inliningId;
sourcePosition = new SourcePosition(scriptOffset, inliningId);
}
const label = new NodeLabel(node.id, node.label, node.title, node.live, node.properties,
sourcePosition, origin, node.opcode, node.control, node.opinfo, node.type);
sourcePosition, bytecodePosition, origin, node.opcode, node.control, node.opinfo,
node.type);
const previous = nodeLabelMap[label.id];
if (!label.equals(previous)) {
if (previous !== undefined) {
label.setInplaceUpdatePhase(this.name);
}
nodeLabelMap[label.id] = label;
}
const newNode = new GraphNode(label);
this.data.nodes.push(newNode);
nodeIdToNodeMap[newNode.identifier()] = newNode;
if (origin) {
this.highestNodeId = Math.max(this.highestNodeId, newNode.id);
this.nodeIdToNodeMap[newNode.identifier()] = newNode;
const previous = nodeMap[newNode.id];
if (!newNode.equals(previous)) {
if (previous) newNode.nodeLabel.setInplaceUpdatePhase(this.name);
nodeMap[newNode.id] = newNode;
}
if (origin && origin instanceof NodeOrigin) {
const identifier = origin.identifier();
if (!this.originIdToNodesMap.has(identifier)) {
this.originIdToNodesMap.set(identifier, new Array<GraphNode>());
}
this.originIdToNodesMap.get(identifier).push(newNode);
}
if (sourcePosition) {
const inlining = inlinings[sourcePosition.inliningId];
if (inlining) sources[inlining.sourceId].sourcePositions.push(sourcePosition);
this.positions.addSourcePosition(newNode.identifier(), sourcePosition);
}
if (bytecodePosition) {
this.positions.addBytecodePosition(newNode.identifier(), bytecodePosition);
}
}
return nodeIdToNodeMap;
}
private parseEdgesFromJSON(edgesJSON): void {
......
......@@ -37,6 +37,18 @@ export class InstructionsPhase extends Phase {
return -1;
}
public merge(other: InstructionsPhase): void {
if (!other) return;
if (this.name == "") this.name = "merged data";
this.nodeIdToInstructionRange = new Array<[number, number]>();
this.blockIdToInstructionRange = other.blockIdToInstructionRange;
this.instructionOffsetToPCOffset = other.instructionOffsetToPCOffset;
this.codeOffsetsInfo = other.codeOffsetsInfo;
this.instructionToPCOffset = other.instructionToPCOffset;
this.pcOffsetToInstructions = other.pcOffsetToInstructions;
this.pcOffsets = other.pcOffsets;
}
public instructionToPcOffsets(instruction: number): TurbolizerInstructionStartInfo {
return this.instructionToPCOffset[instruction];
}
......
......@@ -12,8 +12,11 @@ export abstract class Phase {
}
public isGraph(): boolean {
return this.type == PhaseType.Graph ||
this.type == PhaseType.TurboshaftGraph;
return this.type == PhaseType.Graph || this.type == PhaseType.TurboshaftGraph;
}
public isDynamic(): boolean {
return this.isGraph() || this.type == PhaseType.Schedule || this.type == PhaseType.Sequence;
}
}
......
......@@ -3,13 +3,19 @@
// found in the LICENSE file.
import { Phase, PhaseType } from "./phase";
import { PositionsContainer } from "../position";
import { InstructionsPhase } from "./instructions-phase";
export class SchedulePhase extends Phase {
data: ScheduleData;
instructionsPhase: InstructionsPhase;
positions: PositionsContainer;
constructor(name: string, dataJson) {
super(name, PhaseType.Schedule);
this.data = new ScheduleData();
this.instructionsPhase = new InstructionsPhase();
this.positions = new PositionsContainer();
this.parseScheduleFromJSON(dataJson);
}
......
......@@ -4,13 +4,19 @@
import * as C from "../common/constants";
import { Phase, PhaseType } from "./phase";
import { PositionsContainer } from "../position";
import { InstructionsPhase } from "./instructions-phase";
export class SequencePhase extends Phase {
blocks: Array<SequenceBlock>;
instructionsPhase: InstructionsPhase;
positions: PositionsContainer;
registerAllocation: RegisterAllocation;
constructor(name: string, blocksJSON, registerAllocationJSON) {
super(name, PhaseType.Sequence);
this.instructionsPhase = new InstructionsPhase();
this.positions = new PositionsContainer();
this.parseBlocksFromJSON(blocksJSON);
this.parseRegisterAllocationFromJSON(registerAllocationJSON);
}
......
......@@ -7,17 +7,26 @@ import { measureText } from "../../common/util";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
import { Node } from "../../node";
import { BytecodePosition, SourcePosition } from "../../position";
import { NodeOrigin } from "../../origin";
export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGraphNode>> {
title: string;
block: TurboshaftGraphBlock;
sourcePosition: SourcePosition;
bytecodePosition: BytecodePosition;
origin: NodeOrigin;
opPropertiesType: OpPropertiesType;
constructor(id: number, title: string, block: TurboshaftGraphBlock,
opPropertiesType: OpPropertiesType) {
sourcePosition: SourcePosition, bytecodePosition: BytecodePosition,
origin: NodeOrigin, opPropertiesType: OpPropertiesType) {
super(id);
this.title = title;
this.block = block;
this.sourcePosition = sourcePosition;
this.bytecodePosition = bytecodePosition;
this.origin = origin;
this.opPropertiesType = opPropertiesType;
this.visible = true;
}
......@@ -37,6 +46,9 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap
public getTitle(): string {
let title = `${this.id} ${this.title} ${this.opPropertiesType}`;
if (this.origin) {
title += `\nOrigin: ${this.origin.toString()}`;
}
if (this.inputs.length > 0) {
title += `\nInputs: ${this.inputs.map(i => i.source.id).join(", ")}`;
}
......@@ -46,10 +58,24 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap
return title;
}
public getHistoryLabel(): string {
return `${this.id} ${this.title}`;
}
public getNodeOrigin(): NodeOrigin {
return this.origin;
}
public getInlineLabel(): string {
if (this.inputs.length == 0) return `${this.id} ${this.title}`;
return `${this.id} ${this.title}(${this.inputs.map(i => i.source.id).join(",")})`;
}
public equals(that?: TurboshaftGraphNode): boolean {
if (!that) return false;
if (this.id !== that.id) return false;
return this.title === that.title;
}
}
export enum OpPropertiesType {
......
......@@ -7,31 +7,51 @@ import { TurboshaftGraphNode } from "./turboshaft-graph-node";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
import { DataTarget, TurboshaftCustomDataPhase } from "../turboshaft-custom-data-phase";
import { GraphNode } from "../graph-phase/graph-node";
import { NodeOrigin } from "../../origin";
import { Source } from "../../source";
import { InstructionsPhase } from "../instructions-phase";
import {
BytecodePosition,
InliningPosition,
PositionsContainer,
SourcePosition
} from "../../position";
export class TurboshaftGraphPhase extends Phase {
data: TurboshaftGraphData;
customData: TurboshaftCustomData;
stateType: GraphStateType;
instructionsPhase: InstructionsPhase;
nodeIdToNodeMap: Array<TurboshaftGraphNode>;
blockIdToBlockMap: Array<TurboshaftGraphBlock>;
originIdToNodesMap: Map<string, Array<TurboshaftGraphNode>>;
positions: PositionsContainer;
highestNodeId: number;
rendered: boolean;
customDataShowed: boolean;
transform: { x: number, y: number, scale: number };
constructor(name: string, dataJson) {
constructor(name: string, dataJson, nodeMap: Array<GraphNode | TurboshaftGraphNode>,
sources: Array<Source>, inlinings: Array<InliningPosition>) {
super(name, PhaseType.TurboshaftGraph);
this.stateType = GraphStateType.NeedToFullRebuild;
this.instructionsPhase = new InstructionsPhase();
this.customData = new TurboshaftCustomData();
this.nodeIdToNodeMap = new Array<TurboshaftGraphNode>();
this.blockIdToBlockMap = new Array<TurboshaftGraphBlock>();
this.originIdToNodesMap = new Map<string, Array<TurboshaftGraphNode>>();
this.positions = new PositionsContainer();
this.highestNodeId = 0;
this.rendered = false;
this.parseDataFromJSON(dataJson);
this.parseDataFromJSON(dataJson, nodeMap, sources, inlinings);
}
private parseDataFromJSON(dataJson): void {
private parseDataFromJSON(dataJson, nodeMap: Array<GraphNode | TurboshaftGraphNode>,
sources: Array<Source>, inlinings: Array<InliningPosition>): void {
this.data = new TurboshaftGraphData();
this.parseBlocksFromJSON(dataJson.blocks);
this.parseNodesFromJSON(dataJson.nodes);
this.parseNodesFromJSON(dataJson.nodes, nodeMap, sources, inlinings);
this.parseEdgesFromJSON(dataJson.edges);
}
......@@ -52,14 +72,62 @@ export class TurboshaftGraphPhase extends Phase {
}
}
private parseNodesFromJSON(nodesJson): void {
private parseNodesFromJSON(nodesJson, nodeMap: Array<GraphNode | TurboshaftGraphNode>,
sources: Array<Source>, inlinings: Array<InliningPosition>): void {
for (const nodeJson of nodesJson) {
const block = this.blockIdToBlockMap[nodeJson.block_id];
const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title,
block, nodeJson.op_properties_type);
let sourcePosition: SourcePosition = null;
const sourcePositionJson = nodeJson.sourcePosition;
if (sourcePositionJson) {
const scriptOffset = sourcePositionJson.scriptOffset;
const inliningId = sourcePositionJson.inliningId;
sourcePosition = new SourcePosition(scriptOffset, inliningId);
}
let origin: NodeOrigin = null;
let bytecodePosition: BytecodePosition = null;
const originJson = nodeJson.origin;
if (originJson) {
const nodeId = originJson.nodeId;
const originNode = nodeMap[nodeId];
origin = new NodeOrigin(nodeId, originNode, originJson.phase, originJson.reducer);
if (originNode) {
if (originNode instanceof GraphNode) {
bytecodePosition = originNode.nodeLabel.bytecodePosition;
} else {
bytecodePosition = originNode.bytecodePosition;
}
}
}
const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title, block, sourcePosition,
bytecodePosition, origin, nodeJson.op_properties_type);
block.nodes.push(node);
this.data.nodes.push(node);
this.nodeIdToNodeMap[node.identifier()] = node;
this.highestNodeId = Math.max(this.highestNodeId, node.id);
if (origin) {
const identifier = origin.identifier();
if (!this.originIdToNodesMap.has(identifier)) {
this.originIdToNodesMap.set(identifier, new Array<TurboshaftGraphNode>());
}
this.originIdToNodesMap.get(identifier).push(node);
}
nodeMap[node.id] = node;
if (sourcePosition) {
const inlining = inlinings[sourcePosition.inliningId];
if (inlining) sources[inlining.sourceId].sourcePositions.push(sourcePosition);
this.positions.addSourcePosition(node.identifier(), sourcePosition);
}
if (bytecodePosition) {
this.positions.addBytecodePosition(node.identifier(), bytecodePosition);
}
}
for (const block of this.blockIdToBlockMap) {
block.initCollapsedLabel();
......@@ -127,7 +195,9 @@ export class TurboshaftCustomData {
private concatCustomData(key: number, items: Map<string, TurboshaftCustomDataPhase>): string {
let customData = "";
for (const [name, dataPhase] of items.entries()) {
customData += `\n${name}: ${dataPhase.data[key] ?? ""}`;
if (dataPhase.data[key] && dataPhase.data[key].length > 0) {
customData += `\n${name}: ${dataPhase.data[key]}`;
}
}
return customData;
}
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
export class InliningPosition {
sourceId: number;
inliningPosition: SourcePosition;
......@@ -43,9 +45,11 @@ export class SourcePosition {
export class BytecodePosition {
bytecodePosition: number;
inliningId: number;
constructor(bytecodePosition: number) {
constructor(bytecodePosition: number, inliningId: number) {
this.bytecodePosition = bytecodePosition;
this.inliningId = inliningId;
}
public isValid(): boolean {
......@@ -53,6 +57,54 @@ export class BytecodePosition {
}
public toString(): string {
return `BCP:${this.bytecodePosition}`;
return `BCP:${this.inliningId}:${this.bytecodePosition}`;
}
}
export class PositionsContainer {
nodeIdToSourcePositionMap: Array<SourcePosition>;
nodeIdToBytecodePositionMap: Array<BytecodePosition>;
sourcePositionToNodes: Map<string, Array<string>>;
bytecodePositionToNodes: Map<string, Array<string>>;
constructor() {
this.nodeIdToSourcePositionMap = new Array<SourcePosition>();
this.nodeIdToBytecodePositionMap = new Array<BytecodePosition>();
this.sourcePositionToNodes = new Map<string, Array<string>>();
this.bytecodePositionToNodes = new Map<string, Array<string>>();
}
public addSourcePosition(nodeIdentifier: string, sourcePosition: SourcePosition): void {
this.nodeIdToSourcePositionMap[nodeIdentifier] = sourcePosition;
const key = sourcePosition.toString();
if (!this.sourcePositionToNodes.has(key)) {
this.sourcePositionToNodes.set(key, new Array<string>());
}
const nodes = this.sourcePositionToNodes.get(key);
if (!nodes.includes(nodeIdentifier)) nodes.push(nodeIdentifier);
}
public addBytecodePosition(nodeIdentifier: string, bytecodePosition: BytecodePosition): void {
this.nodeIdToBytecodePositionMap[nodeIdentifier] = bytecodePosition;
const key = bytecodePosition.toString();
if (!this.bytecodePositionToNodes.has(key)) {
this.bytecodePositionToNodes.set(key, new Array<string>());
}
const nodes = this.bytecodePositionToNodes.get(key);
if (!nodes.includes(nodeIdentifier)) nodes.push(nodeIdentifier);
}
public merge(nodes: Array<TurboshaftGraphNode>, replacements: Map<number, number>): void {
for (const node of nodes) {
const sourcePosition = node.sourcePosition;
const bytecodePosition = node.bytecodePosition;
const nodeId = replacements.has(node.id) ? replacements.get(node.id) : node.id;
if (sourcePosition && !this.nodeIdToSourcePositionMap[nodeId]) {
this.addSourcePosition(String(nodeId), sourcePosition);
}
if (bytecodePosition && !this.nodeIdToBytecodePositionMap[nodeId]) {
this.addBytecodePosition(String(nodeId), bytecodePosition);
}
}
}
}
......@@ -4,6 +4,8 @@
import { GenericPosition, SourceResolver } from "../source-resolver";
import { GraphNode } from "../phases/graph-phase/graph-node";
import { BytecodePosition } from "../position";
import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft-graph-node";
import {
ClearableHandler,
SourcePositionSelectionHandler,
......@@ -11,7 +13,8 @@ import {
BlockSelectionHandler,
InstructionSelectionHandler,
RegisterAllocationSelectionHandler,
HistoryHandler
HistoryHandler,
BytecodeOffsetSelectionHandler
} from "./selection-handler";
export class SelectionBroker {
......@@ -22,6 +25,7 @@ export class SelectionBroker {
blockHandlers: Array<BlockSelectionHandler>;
instructionHandlers: Array<InstructionSelectionHandler>;
sourcePositionHandlers: Array<SourcePositionSelectionHandler>;
bytecodeOffsetHandlers: Array<BytecodeOffsetSelectionHandler>;
registerAllocationHandlers: Array<RegisterAllocationSelectionHandler>;
constructor(sourceResolver: SourceResolver) {
......@@ -32,6 +36,7 @@ export class SelectionBroker {
this.blockHandlers = new Array<BlockSelectionHandler>();
this.instructionHandlers = new Array<InstructionSelectionHandler>();
this.sourcePositionHandlers = new Array<SourcePositionSelectionHandler>();
this.bytecodeOffsetHandlers = new Array<BytecodeOffsetSelectionHandler>();
this.registerAllocationHandlers = new Array<RegisterAllocationSelectionHandler>();
}
......@@ -74,15 +79,22 @@ export class SelectionBroker {
this.sourcePositionHandlers.push(handler);
}
public addBytecodeOffsetHandler(handler: BytecodeOffsetSelectionHandler & ClearableHandler):
void {
this.allHandlers.push(handler);
this.bytecodeOffsetHandlers.push(handler);
}
public addRegisterAllocatorHandler(handler: RegisterAllocationSelectionHandler
& ClearableHandler): void {
this.allHandlers.push(handler);
this.registerAllocationHandlers.push(handler);
}
public broadcastHistoryShow(from, node: GraphNode, phaseName: string): void {
public broadcastHistoryShow(from, node: GraphNode | TurboshaftGraphNode, phaseName: string):
void {
for (const handler of this.historyHandlers) {
if (handler != from) handler.showTurbofanNodeHistory(node, phaseName);
if (handler != from) handler.showNodeHistory(node, phaseName);
}
}
......@@ -93,7 +105,7 @@ export class SelectionBroker {
if (handler != from) handler.brokeredInstructionSelect([instructionOffsets], selected);
}
// Select the lines from the source panel (left panel)
// Select the lines from the source and bytecode panels (left panels)
const pcOffsets = this.sourceResolver.instructionsPhase
.instructionsToKeyPcOffsets(instructionOffsets);
......@@ -103,12 +115,16 @@ export class SelectionBroker {
for (const handler of this.sourcePositionHandlers) {
if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected);
}
const bytecodePositions = this.sourceResolver.nodeIdsToBytecodePositions(nodes);
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
}
// The middle panel lines have already been selected so there's no need to reselect them.
}
public broadcastSourcePositionSelect(from, sourcePositions: Array<GenericPosition>,
selected: boolean): void {
selected: boolean, selectedNodes?: Set<string>): void {
sourcePositions = sourcePositions.filter(sourcePosition => {
if (!sourcePosition.isValid()) {
console.warn("Invalid source position");
......@@ -128,6 +144,44 @@ export class SelectionBroker {
if (handler != from) handler.brokeredNodeSelect(nodes, selected);
}
// Select bytecode source panel (left panel)
const bytecodePositions = selectedNodes
? this.sourceResolver.nodeIdsToBytecodePositions(selectedNodes)
: this.sourceResolver.nodeIdsToBytecodePositions(nodes);
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
this.selectInstructionsAndRegisterAllocations(from, nodes, selected);
}
public broadcastBytecodePositionsSelect(from, bytecodePositions: Array<BytecodePosition>,
selected: boolean): void {
bytecodePositions = bytecodePositions.filter(bytecodePosition => {
if (!bytecodePosition.isValid()) {
console.warn("Invalid bytecode position");
return false;
}
return true;
});
// Select the lines from the bytecode panel (left panel)
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
// Select the nodes (middle panel)
const nodes = this.sourceResolver.bytecodePositionsToNodeIds(bytecodePositions);
for (const handler of this.nodeHandlers) {
if (handler != from) handler.brokeredNodeSelect(nodes, selected);
}
// Select the lines from the source panel (left panel)
const sourcePositions = this.sourceResolver.nodeIdsToSourcePositions(nodes);
for (const handler of this.sourcePositionHandlers) {
if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected);
}
this.selectInstructionsAndRegisterAllocations(from, nodes, selected);
}
......@@ -143,6 +197,12 @@ export class SelectionBroker {
if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected);
}
// Select bytecode source panel (left panel)
const bytecodePositions = this.sourceResolver.nodeIdsToBytecodePositions(nodes);
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
this.selectInstructionsAndRegisterAllocations(from, nodes, selected);
}
......
......@@ -6,13 +6,14 @@ import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft
import { GraphNode } from "../phases/graph-phase/graph-node";
import { TurboshaftGraphBlock } from "../phases/turboshaft-graph-phase/turboshaft-graph-block";
import { GenericPosition } from "../source-resolver";
import { BytecodePosition } from "../position";
export interface ClearableHandler {
brokeredClear(): void;
}
export interface HistoryHandler {
showTurbofanNodeHistory(node: GraphNode, phaseName: string): void;
showNodeHistory(node: GraphNode | TurboshaftGraphNode, phaseName: string): void;
}
export interface NodeSelectionHandler {
......@@ -41,6 +42,12 @@ export interface SourcePositionSelectionHandler {
brokeredSourcePositionSelect(sourcePositions: Array<GenericPosition>, selected: boolean): void;
}
export interface BytecodeOffsetSelectionHandler {
select(offsets: Array<number>, selected: boolean): void;
clear(): void;
brokeredBytecodeOffsetSelect(positions: Array<BytecodePosition>, selected: boolean): void;
}
export interface RegisterAllocationSelectionHandler {
// These are called instructionIds since the class of the divs is "instruction-id"
select(instructionIds: Array<number>, selected: boolean): void;
......
This diff is collapsed.
......@@ -15,16 +15,15 @@ export class Source {
endPosition?: number;
constructor(sourceName: string, functionName: string, sourceText: string, sourceId: number,
backwardsCompatibility: boolean, sourcePositions?: Array<SourcePosition>,
startPosition?: number, endPosition?: number) {
backwardsCompatibility: boolean, startPosition?: number, endPosition?: number) {
this.sourceName = sourceName;
this.functionName = functionName;
this.sourceText = sourceText;
this.sourceId = sourceId;
this.backwardsCompatibility = backwardsCompatibility;
this.sourcePositions = sourcePositions ?? new Array<SourcePosition>();
this.startPosition = startPosition;
this.endPosition = endPosition;
this.sourcePositions = new Array<SourcePosition>();
}
public toString(): string {
......
......@@ -70,7 +70,7 @@ window.onload = function () {
sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
sourceResolver.setSources(jsonObj.sources, mainFunction);
sourceResolver.setBytecodeSources(jsonObj.bytecodeSources);
sourceResolver.setNodePositionMap(jsonObj.nodePositions);
sourceResolver.setFinalNodeOrigins(jsonObj.nodeOrigins);
sourceResolver.parsePhases(jsonObj.phases);
const [sourceTab, sourceContainer] = sourceTabs.addTabAndContent("Source");
......
......@@ -7,6 +7,10 @@ import { CodeMode, View } from "./view";
import { SelectionBroker } from "../selection/selection-broker";
import { BytecodeSource } from "../source";
import { SourceResolver } from "../source-resolver";
import { SelectionMap } from "../selection/selection-map";
import { ViewElements } from "../common/view-elements";
import { BytecodeOffsetSelectionHandler, ClearableHandler } from "../selection/selection-handler";
import { BytecodePosition } from "../position";
export class BytecodeSourceView extends View {
broker: SelectionBroker;
......@@ -14,6 +18,8 @@ export class BytecodeSourceView extends View {
sourceResolver: SourceResolver;
codeMode: CodeMode;
bytecodeOffsetToHtmlElement: Map<number, HTMLElement>;
bytecodeOffsetSelection: SelectionMap;
bytecodeOffsetSelectionHandler: BytecodeOffsetSelectionHandler & ClearableHandler;
constructor(parent: HTMLElement, broker: SelectionBroker, sourceFunction: BytecodeSource,
sourceResolver: SourceResolver, codeMode: CodeMode) {
......@@ -23,6 +29,9 @@ export class BytecodeSourceView extends View {
this.sourceResolver = sourceResolver;
this.codeMode = codeMode;
this.bytecodeOffsetToHtmlElement = new Map<number, HTMLElement>();
this.bytecodeOffsetSelection = new SelectionMap((offset: number) => String(offset));
this.bytecodeOffsetSelectionHandler = this.initializeBytecodeOffsetSelectionHandler();
this.broker.addBytecodeOffsetHandler(this.bytecodeOffsetSelectionHandler);
this.initializeCode();
}
......@@ -86,6 +95,44 @@ export class BytecodeSourceView extends View {
codePre.appendChild(constantList);
}
private initializeBytecodeOffsetSelectionHandler(): BytecodeOffsetSelectionHandler
& ClearableHandler {
const view = this;
const broker = this.broker;
return {
select: function (offsets: Array<number>, selected: boolean) {
const bytecodePositions = new Array<BytecodePosition>();
for (const offset of offsets) {
bytecodePositions.push(new BytecodePosition(offset, view.source.sourceId));
}
view.bytecodeOffsetSelection.select(offsets, selected);
view.updateSelection();
broker.broadcastBytecodePositionsSelect(this, bytecodePositions, selected);
},
clear: function () {
view.bytecodeOffsetSelection.clear();
view.updateSelection();
broker.broadcastClear(this);
},
brokeredBytecodeOffsetSelect: function (positions: Array<BytecodePosition>,
selected: boolean) {
const offsets = new Array<number>();
const firstSelect = view.bytecodeOffsetSelection.isEmpty();
for (const position of positions) {
if (position.inliningId == view.source.sourceId) {
offsets.push(position.bytecodePosition);
}
}
view.bytecodeOffsetSelection.select(offsets, selected);
view.updateSelection(firstSelect);
},
brokeredClear: function () {
view.bytecodeOffsetSelection.clear();
view.updateSelection();
},
};
}
private getBytecodeHeaderHtmlElementName(): string {
return `source-pre-${this.source.sourceId}-header`;
}
......@@ -98,13 +145,36 @@ export class BytecodeSourceView extends View {
return this.codeMode == CodeMode.MainSource ? "main-source" : "inlined-source";
}
private updateSelection(scrollIntoView: boolean = false): void {
const mkVisible = new ViewElements(this.divNode.parentNode as HTMLElement);
for (const [offset, element] of this.bytecodeOffsetToHtmlElement.entries()) {
const key = this.bytecodeOffsetSelection.stringKey(offset);
const isSelected = this.bytecodeOffsetSelection.isKeySelected(key);
mkVisible.consider(element, isSelected);
element.classList.toggle("selected", isSelected);
}
mkVisible.apply(scrollIntoView);
}
private onSelectBytecodeOffset(offset: number, doClear: boolean) {
if (doClear) {
this.bytecodeOffsetSelectionHandler.clear();
}
this.bytecodeOffsetSelectionHandler.select([offset], undefined);
}
private insertLineContent(lineElement: HTMLElement, content: string): void {
const lineContentElement = createElement("span", "", content);
lineElement.appendChild(lineContentElement);
}
private insertLineNumber(lineElement: HTMLElement, lineNumber: number): void {
const view = this;
const lineNumberElement = createElement("div", "line-number", String(lineNumber));
lineNumberElement.onclick = function (e: MouseEvent) {
e.stopPropagation();
view.onSelectBytecodeOffset(lineNumber, !e.shiftKey);
};
lineElement.insertBefore(lineNumberElement, lineElement.firstChild);
}
}
......@@ -182,7 +182,8 @@ export class CodeView extends View {
};
}
private addHtmlElementToSourcePosition(sourcePosition, element): void {
private addHtmlElementToSourcePosition(sourcePosition: GenericPosition, element: HTMLElement):
void {
const key = sourcePosition.toString();
if (!this.sourcePositionToHtmlElements.has(key)) {
this.sourcePositionToHtmlElements.set(key, new Array<HTMLElement>());
......
......@@ -12,8 +12,7 @@ import { GraphNode } from "../phases/graph-phase/graph-node";
import { GraphEdge } from "../phases/graph-phase/graph-edge";
import { GraphLayout } from "../graph-layout";
import { GraphPhase, GraphStateType } from "../phases/graph-phase/graph-phase";
import { BytecodePosition } from "../position";
import { BytecodeOrigin, NodeOrigin } from "../origin";
import { NodeOrigin } from "../origin";
import { MovableView } from "./movable-view";
import { ClearableHandler, NodeSelectionHandler } from "../selection/selection-handler";
import { GenericPosition } from "../source-resolver";
......@@ -32,8 +31,10 @@ export class GraphView extends MovableView<Graph> {
toolbox: HTMLElement) {
super(idOrContainer, broker, showPhaseByName, toolbox);
this.state.selection = new SelectionMap(node => node.identifier(),
node => node.nodeLabel?.origin?.identifier());
this.state.selection = new SelectionMap(node => node.identifier(), node => {
if (node instanceof GraphNode) return node.nodeLabel?.origin?.identifier();
return node?.origin?.identifier();
});
this.nodeSelectionHandler = this.initializeNodeSelectionHandler();
this.svg.on("click", () => this.nodeSelectionHandler.clear());
......@@ -406,16 +407,19 @@ export class GraphView extends MovableView<Graph> {
return {
select: function (selectedNodes: Array<GraphNode>, selected: boolean) {
const locations = new Array<GenericPosition>();
const nodes = new Set<string>();
for (const node of selectedNodes) {
if (node.nodeLabel.sourcePosition) {
locations.push(node.nodeLabel.sourcePosition);
nodes.add(node.identifier());
}
if (node.nodeLabel.origin && node.nodeLabel.origin instanceof BytecodeOrigin) {
locations.push(new BytecodePosition(node.nodeLabel.origin.bytecodePosition));
if (node.nodeLabel.bytecodePosition) {
locations.push(node.nodeLabel.bytecodePosition);
nodes.add(node.identifier());
}
}
view.state.selection.select(selectedNodes, selected);
view.broker.broadcastSourcePositionSelect(this, locations, selected);
view.broker.broadcastSourcePositionSelect(this, locations, selected, nodes);
view.updateGraphVisibility();
},
clear: function () {
......@@ -434,10 +438,10 @@ export class GraphView extends MovableView<Graph> {
if (!node) continue;
node.visible = true;
node.inputs.forEach(edge => {
edge.visible = edge.visible || view.state.selection.isSelected(edge.source);
edge.visible = edge.visible || edge.source.visible;
});
node.outputs.forEach(edge => {
edge.visible = edge.visible || view.state.selection.isSelected(edge.target);
edge.visible = edge.visible || edge.target.visible;
});
}
view.updateGraphVisibility();
......@@ -710,12 +714,6 @@ export class GraphView extends MovableView<Graph> {
this.updateGraphVisibility();
}
public showHoveredNodeHistory(): void {
const node = this.graph.nodeMap[this.hoveredNodeIdentifier];
if (!node) return;
this.broker.broadcastHistoryShow(null, node, this.phaseName);
}
private selectOrigins(): void {
const selection = new SelectionStorage();
const origins = new Array<GraphNode>();
......
......@@ -11,11 +11,15 @@ import { SourceResolver } from "../source-resolver";
import { GraphNode } from "../phases/graph-phase/graph-node";
import { HistoryHandler } from "../selection/selection-handler";
import { GraphPhase } from "../phases/graph-phase/graph-phase";
import { NodeOrigin } from "../origin";
import { SelectionStorage } from "../selection/selection-storage";
import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft-graph-node";
import { TurboshaftGraphPhase } from "../phases/turboshaft-graph-phase/turboshaft-graph-phase";
type GNode = GraphNode | TurboshaftGraphNode;
type GPhase = GraphPhase | TurboshaftGraphPhase;
export class HistoryView extends View {
node: GraphNode;
node: GNode;
broker: SelectionBroker;
sourceResolver: SourceResolver;
historyHandler: HistoryHandler;
......@@ -78,7 +82,7 @@ export class HistoryView extends View {
private initializeNodeSelectionHandler(): HistoryHandler {
const view = this;
return {
showTurbofanNodeHistory: function (node: GraphNode, phaseName: string) {
showNodeHistory: function (node: GNode, phaseName: string) {
view.clear();
view.node = node;
const phaseId = view.sourceResolver.getPhaseIdByName(phaseName);
......@@ -264,7 +268,7 @@ export class HistoryView extends View {
}
private setLabel(): void {
this.label = `${this.node.id} ${this.node.nodeLabel.opcode}`;
this.label = this.node.getHistoryLabel();
const coefficient = this.getCoefficient("history-tspan-font-size");
this.labelBox = measureText(this.label, coefficient);
}
......@@ -275,14 +279,14 @@ export class HistoryView extends View {
return Math.min(tspanSize, varSize) / Math.max(tspanSize, varSize);
}
private getPhaseHistory(historyChain: Map<number, GraphNode>): void {
private getPhaseHistory(historyChain: Map<number, GNode>): void {
const uniqueAncestors = new Set<string>();
const coefficient = this.getCoefficient("history-item-tspan-font-size");
let prevNode = null;
let first = true;
for (let i = 0; i < this.sourceResolver.phases.length; i++) {
const phase = this.sourceResolver.getPhase(i);
if (!(phase instanceof GraphPhase)) continue;
const phase = this.sourceResolver.getGraphPhase(i);
if (!phase) continue;
const phaseNameMeasure = measureText(phase.name, coefficient);
this.maxPhaseNameWidth = Math.max(this.maxPhaseNameWidth, phaseNameMeasure.width);
......@@ -298,10 +302,11 @@ export class HistoryView extends View {
if (prevNode && !prevNode.equals(node) &&
phase.originIdToNodesMap.has(prevNode.identifier())) {
const prevNodeCurrentState = phase.nodeIdToNodeMap[prevNode.identifier()];
const inplaceUpdate = prevNodeCurrentState?.nodeLabel?.inplaceUpdatePhase;
if (!prevNodeCurrentState) {
this.addToHistory(i, prevNode, HistoryChange.Removed);
} else if (!prevNodeCurrentState?.equals(node) && inplaceUpdate == phase.name) {
} else if (!this.nodeEquals(prevNodeCurrentState, node) &&
prevNodeCurrentState instanceof GraphNode &&
prevNodeCurrentState.getInplaceUpdatePhase() == phase.name) {
this.addToHistory(i, prevNodeCurrentState, HistoryChange.InplaceUpdated);
} else if (node.identifier() != prevNode.identifier()) {
this.addToHistory(i, prevNodeCurrentState, HistoryChange.Survived);
......@@ -314,7 +319,7 @@ export class HistoryView extends View {
continue;
}
if (node.nodeLabel.inplaceUpdatePhase && node.nodeLabel.inplaceUpdatePhase == phase.name) {
if (node instanceof GraphNode && node.getInplaceUpdatePhase() == phase.name) {
this.addToHistory(i, node, HistoryChange.InplaceUpdated);
}
......@@ -328,7 +333,7 @@ export class HistoryView extends View {
}
}
private addHistoryAncestors(key: string, phase: GraphPhase, uniqueAncestors: Set<string>):
private addHistoryAncestors(key: string, phase: GPhase, uniqueAncestors: Set<string>):
boolean {
let changed = false;
const phaseId = this.sourceResolver.getPhaseIdByName(phase.name);
......@@ -343,22 +348,22 @@ export class HistoryView extends View {
return changed;
}
private getHistoryChain(phaseId: number, node: GraphNode): Map<number, GraphNode> {
private getHistoryChain(phaseId: number, node: GNode): Map<number, GNode> {
const leftChain = this.getLeftHistoryChain(phaseId, node);
const rightChain = this.getRightHistoryChain(phaseId, node);
return new Map([...leftChain, ...rightChain]);
}
private getLeftHistoryChain(phaseId: number, node: GraphNode): Map<number, GraphNode> {
const leftChain = new Map<number, GraphNode>();
private getLeftHistoryChain(phaseId: number, node: GNode): Map<number, GNode> {
const leftChain = new Map<number, GNode>();
for (let i = phaseId; i >= 0; i--) {
const phase = this.sourceResolver.getPhase(i);
if (!(phase instanceof GraphPhase)) continue;
const phase = this.sourceResolver.getGraphPhase(i);
if (!phase) continue;
let currentNode = phase.nodeIdToNodeMap[node.identifier()];
if (!currentNode) {
const nodeOrigin = node.nodeLabel.origin;
if (nodeOrigin instanceof NodeOrigin) {
const nodeOrigin = node.getNodeOrigin();
if (nodeOrigin) {
currentNode = phase.nodeIdToNodeMap[nodeOrigin.identifier()];
}
if (!currentNode) return leftChain;
......@@ -370,12 +375,12 @@ export class HistoryView extends View {
return leftChain;
}
private getRightHistoryChain(phaseId: number, node: GraphNode): Map<number, GraphNode> {
const rightChain = new Map<number, GraphNode>();
private getRightHistoryChain(phaseId: number, node: GNode): Map<number, GNode> {
const rightChain = new Map<number, GNode>();
for (let i = phaseId + 1; i < this.sourceResolver.phases.length; i++) {
const phase = this.sourceResolver.getPhase(i);
if (!(phase instanceof GraphPhase)) continue;
const phase = this.sourceResolver.getGraphPhase(i);
if (!phase) continue;
const currentNode = phase.nodeIdToNodeMap[node.identifier()];
if (!currentNode) return rightChain;
rightChain.set(i, currentNode);
......@@ -385,7 +390,7 @@ export class HistoryView extends View {
return rightChain;
}
private addToHistory(phaseId: number, node: GraphNode, change: HistoryChange): void {
private addToHistory(phaseId: number, node: GNode, change: HistoryChange): void {
if (!this.phaseIdToHistory.has(phaseId)) {
this.phaseIdToHistory.set(phaseId, new PhaseHistory(phaseId));
}
......@@ -397,6 +402,16 @@ export class HistoryView extends View {
}
}
private nodeEquals(first: GNode, second: GNode): boolean {
if (!first || !second) return false;
if ((first instanceof GraphNode && second instanceof GraphNode)) {
return first.equals(second);
} else if (first instanceof TurboshaftGraphNode && second instanceof TurboshaftGraphNode) {
return first.equals(second);
}
return first.getHistoryLabel() == second.getHistoryLabel();
}
private clear(): void {
this.phaseIdToHistory.clear();
this.maxNodeWidth = 0;
......@@ -458,7 +473,7 @@ export class PhaseHistory {
this.nodeIdToRecord = new Map<string, HistoryRecord>();
}
public addChange(node: GraphNode, change: HistoryChange): void {
public addChange(node: GNode, change: HistoryChange): void {
const key = node.identifier();
if (!this.nodeIdToRecord.has(key)) {
this.nodeIdToRecord.set(key, new HistoryRecord(node));
......@@ -475,10 +490,10 @@ export class PhaseHistory {
}
export class HistoryRecord {
node: GraphNode;
node: GNode;
changes: Set<HistoryChange>;
constructor(node: GraphNode) {
constructor(node: GNode) {
this.node = node;
this.changes = new Set<HistoryChange>();
}
......
......@@ -252,6 +252,12 @@ export abstract class MovableView<GraphType extends Graph | TurboshaftGraph> ext
})];
}
protected showHoveredNodeHistory(): void {
const node = this.graph.nodeMap[this.hoveredNodeIdentifier];
if (!node) return;
this.broker.broadcastHistoryShow(null, node, this.phaseName);
}
protected createImgToggleInput(id: string, title: string, initState: boolean, onClick):
HTMLElement {
const input = this.createImgInput(id, title, onClick);
......
......@@ -21,6 +21,7 @@ import { TurboshaftGraphLayout } from "../turboshaft-graph-layout";
import { GraphStateType } from "../phases/graph-phase/graph-phase";
import { SelectionStorage } from "../selection/selection-storage";
import { DataTarget } from "../phases/turboshaft-custom-data-phase";
import { SourcePosition } from "../position";
import {
TurboshaftCustomData,
TurboshaftGraphPhase
......@@ -99,8 +100,12 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
this.broker.addNodeHandler(this.nodeSelectionHandler);
this.broker.addBlockHandler(this.blockSelectionHandler);
if (adaptedSelection.isAdapted()) {
this.attachSelection(adaptedSelection);
const countOfSelectedItems = adaptedSelection.isAdapted()
? this.attachSelection(adaptedSelection)
: 0;
if (countOfSelectedItems > 0) {
this.updateGraphVisibility();
this.viewSelection();
} else {
if (this.state.cacheLayout && data.transform) {
......@@ -142,6 +147,9 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
eventHandled = false;
}
break;
case 72: // 'h'
this.showHoveredNodeHistory();
break;
case 73: // 'i'
this.selectNodesOfSelectedBlocks();
break;
......@@ -255,7 +263,16 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
const view = this;
return {
select: function (selectedNodes: Array<TurboshaftGraphNode>, selected: boolean) {
const sourcePositions = new Array<SourcePosition>();
const nodes = new Set<string>();
for (const node of selectedNodes) {
if (node.sourcePosition) {
sourcePositions.push(node.sourcePosition);
nodes.add(node.identifier());
}
}
view.state.selection.select(selectedNodes, selected);
view.broker.broadcastSourcePositionSelect(this, sourcePositions, selected, nodes);
view.updateGraphVisibility();
},
clear: function () {
......@@ -367,6 +384,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
const selectedCustomData = select.options[this.selectedIndex].text;
storageSetItem(storageKey, selectedCustomData);
view.updateGraphVisibility();
view.updateInlineNodesCustomData();
};
this.toolbox.appendChild(select);
......@@ -603,13 +621,11 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
private updateInlineNodes(): void {
const view = this;
const state = this.state;
const storageKey = this.customDataStorageKey();
const selectedCustomData = storageGetItem(storageKey, null, false);
const showCustomData = this.nodesCustomDataShowed();
let totalHeight = 0;
let blockId = 0;
view.visibleNodes.each(function (node: TurboshaftGraphNode) {
const nodeSvg = d3.select(this);
const showCustomData = view.nodesCustomDataShowed();
if (blockId != node.block.id) {
blockId = node.block.id;
totalHeight = 0;
......@@ -623,18 +639,25 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
.attr("dy", nodeY)
.attr("visibility", !node.block.collapsed ? "visible" : "hidden");
const svgNodeCustomData = nodeSvg
nodeSvg
.select(".inline-node-custom-data")
.attr("visibility", !node.block.collapsed && showCustomData ? "visible" : "hidden");
});
}
if (!node.block.collapsed && showCustomData) {
const customData = view.graph.getCustomData(selectedCustomData, node.id, DataTarget.Nodes);
svgNodeCustomData
.select("tspan")
.text(view.getReadableString(customData, node.block.width))
.append("title")
.text(customData);
}
private updateInlineNodesCustomData(): void {
const view = this;
const storageKey = this.customDataStorageKey();
const selectedCustomData = storageGetItem(storageKey, null, false);
if (!this.nodesCustomDataShowed()) return;
view.visibleNodes.each(function (node: TurboshaftGraphNode) {
const customData = view.graph.getCustomData(selectedCustomData, node.id, DataTarget.Nodes);
d3.select(this)
.select(".inline-node-custom-data")
.select("tspan")
.text(view.getReadableString(customData, node.block.width))
.append("title")
.text(customData);
});
}
......@@ -705,8 +728,8 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
}
}
private attachSelection(selection: SelectionStorage): void {
if (!(selection instanceof SelectionStorage)) return;
private attachSelection(selection: SelectionStorage): number {
if (!(selection instanceof SelectionStorage)) return 0;
this.nodeSelectionHandler.clear();
this.blockSelectionHandler.clear();
const selectedNodes = [
......@@ -719,6 +742,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
selection.adaptedBocks.has(this.state.blocksSelection.stringKey(block)))
];
this.blockSelectionHandler.select(selectedBlocks, true);
return selectedNodes.length + selectedBlocks.length;
}
private nodesCustomDataShowed(): boolean {
......@@ -813,6 +837,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
const extent = view.graph.redetermineGraphBoundingBox(view.state.showCustomData);
view.panZoom.translateExtent(extent);
view.adaptiveUpdateGraphVisibility();
view.updateInlineNodesCustomData();
}
private toggleLayoutCachingAction(view: TurboshaftGraphView): void {
......@@ -823,7 +848,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
// Hotkeys handlers
private selectAllNodes(): void {
this.state.selection.select(this.graph.nodeMap, true);
this.nodeSelectionHandler.select(this.graph.nodeMap, true);
this.updateGraphVisibility();
}
......@@ -865,7 +890,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
block.collapsed = false;
selectedNodes = selectedNodes.concat(block.nodes);
}
this.state.selection.select(selectedNodes, true);
this.nodeSelectionHandler.select(selectedNodes, true);
this.updateGraphVisibility();
}
}
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