[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/+/3829481 Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Commit-Queue: Danylo Boiko <danielboyko02@gmail.com> Cr-Commit-Position: refs/heads/main@{#82682}
This commit is contained in:
parent
bf5e3a8a0e
commit
41d5c9cb15
@ -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 origin: NodeOrigin | BytecodeOrigin = null;
|
||||
const jsonOrigin = node.origin;
|
||||
if (jsonOrigin) {
|
||||
if (jsonOrigin.nodeId) {
|
||||
origin = new NodeOrigin(jsonOrigin.nodeId, jsonOrigin.phase, jsonOrigin.reducer);
|
||||
} else {
|
||||
origin = new BytecodeOrigin(jsonOrigin.bytecodePosition, jsonOrigin.phase,
|
||||
jsonOrigin.reducer);
|
||||
}
|
||||
}
|
||||
|
||||
let sourcePosition: SourcePosition = null;
|
||||
if (node.sourcePosition) {
|
||||
const scriptOffset = node.sourcePosition.scriptOffset;
|
||||
const inliningId = node.sourcePosition.inliningId;
|
||||
const sourcePositionJson = node.sourcePosition;
|
||||
if (sourcePositionJson) {
|
||||
const scriptOffset = sourcePositionJson.scriptOffset;
|
||||
const inliningId = sourcePositionJson.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);
|
||||
let origin: NodeOrigin | BytecodeOrigin = null;
|
||||
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(originJson.bytecodePosition, originJson.phase,
|
||||
originJson.reducer);
|
||||
const inliningId = sourcePosition ? sourcePosition.inliningId : -1;
|
||||
bytecodePosition = new BytecodePosition(originJson.bytecodePosition, inliningId);
|
||||
}
|
||||
}
|
||||
|
||||
const label = new NodeLabel(node.id, node.label, node.title, node.live, node.properties,
|
||||
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;
|
||||
|
@ -6,36 +6,36 @@ import { camelize, sortUnique } from "./common/util";
|
||||
import { PhaseType } from "./phases/phase";
|
||||
import { GraphPhase } from "./phases/graph-phase/graph-phase";
|
||||
import { DisassemblyPhase } from "./phases/disassembly-phase";
|
||||
import { BytecodePosition, InliningPosition, SourcePosition } from "./position";
|
||||
import { InstructionsPhase } from "./phases/instructions-phase";
|
||||
import { SchedulePhase } from "./phases/schedule-phase";
|
||||
import { SequencePhase } from "./phases/sequence-phase";
|
||||
import { BytecodeOrigin } from "./origin";
|
||||
import { BytecodeSource, BytecodeSourceData, Source } from "./source";
|
||||
import { NodeLabel } from "./node-label";
|
||||
import { TurboshaftCustomDataPhase } from "./phases/turboshaft-custom-data-phase";
|
||||
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||
import { GraphNode } from "./phases/graph-phase/graph-node";
|
||||
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
|
||||
import { BytecodePosition, InliningPosition, PositionsContainer, SourcePosition } from "./position";
|
||||
import { NodeOrigin } from "./origin";
|
||||
|
||||
export type GenericPosition = SourcePosition | BytecodePosition;
|
||||
export type DynamicPhase = GraphPhase | TurboshaftGraphPhase | SchedulePhase | SequencePhase;
|
||||
export type GenericPhase = GraphPhase | TurboshaftGraphPhase | TurboshaftCustomDataPhase
|
||||
| DisassemblyPhase | InstructionsPhase | SchedulePhase | SequencePhase;
|
||||
|
||||
export class SourceResolver {
|
||||
nodePositionMap: Array<GenericPosition>;
|
||||
sources: Array<Source>;
|
||||
bytecodeSources: Map<number, BytecodeSource>;
|
||||
inlinings: Array<InliningPosition>;
|
||||
inliningsMap: Map<string, InliningPosition>;
|
||||
positionToNodes: Map<string, Array<string>>;
|
||||
phases: Array<GenericPhase>;
|
||||
phaseNames: Map<string, number>;
|
||||
disassemblyPhase: DisassemblyPhase;
|
||||
instructionsPhase: InstructionsPhase;
|
||||
linePositionMap: Map<string, Array<GenericPosition>>;
|
||||
finalNodeOrigins: Array<NodeOrigin>;
|
||||
instructionsPhase: InstructionsPhase;
|
||||
positions: PositionsContainer;
|
||||
|
||||
constructor() {
|
||||
// Maps node ids to source positions.
|
||||
this.nodePositionMap = new Array<GenericPosition>();
|
||||
// Maps source ids to source objects.
|
||||
this.sources = new Array<Source>();
|
||||
// Maps bytecode source ids to bytecode source objects.
|
||||
@ -44,15 +44,14 @@ export class SourceResolver {
|
||||
this.inlinings = new Array<InliningPosition>();
|
||||
// Maps source position keys to inlinings.
|
||||
this.inliningsMap = new Map<string, InliningPosition>();
|
||||
// Maps source position keys to node ids.
|
||||
this.positionToNodes = new Map<string, Array<string>>();
|
||||
// Maps phase ids to phases.
|
||||
this.phases = new Array<GenericPhase>();
|
||||
// Maps phase names to phaseIds.
|
||||
this.phaseNames = new Map<string, number>();
|
||||
this.instructionsPhase = new InstructionsPhase();
|
||||
// Maps line numbers to source positions
|
||||
this.linePositionMap = new Map<string, Array<GenericPosition>>();
|
||||
// Maps node ids to node origin
|
||||
this.finalNodeOrigins = new Array<NodeOrigin>();
|
||||
}
|
||||
|
||||
public getMainFunction(jsonObj): Source {
|
||||
@ -60,12 +59,10 @@ export class SourceResolver {
|
||||
// Backwards compatibility.
|
||||
if (typeof fncJson === "string") {
|
||||
return new Source(null, null, jsonObj.source, -1, true,
|
||||
new Array<SourcePosition>(), jsonObj.sourcePosition,
|
||||
jsonObj.sourcePosition + jsonObj.source.length);
|
||||
jsonObj.sourcePosition, jsonObj.sourcePosition + jsonObj.source.length);
|
||||
}
|
||||
return new Source(fncJson.sourceName, fncJson.functionName, fncJson.sourceText,
|
||||
fncJson.sourceId, false, new Array<SourcePosition>(), fncJson.startPosition,
|
||||
fncJson.endPosition);
|
||||
fncJson.sourceId, false, fncJson.startPosition, fncJson.endPosition);
|
||||
}
|
||||
|
||||
public setInlinings(inliningsJson): void {
|
||||
@ -88,8 +85,7 @@ export class SourceResolver {
|
||||
if (sourcesJson) {
|
||||
for (const [sourceId, source] of Object.entries<Source>(sourcesJson)) {
|
||||
const src = new Source(source.sourceName, source.functionName, source.sourceText,
|
||||
source.sourceId, source.backwardsCompatibility, new Array<SourcePosition>(),
|
||||
source.startPosition, source.endPosition);
|
||||
source.sourceId, source.backwardsCompatibility, source.startPosition, source.endPosition);
|
||||
this.sources[sourceId] = src;
|
||||
}
|
||||
}
|
||||
@ -115,41 +111,20 @@ export class SourceResolver {
|
||||
}
|
||||
}
|
||||
|
||||
public setNodePositionMap(mapJson): void {
|
||||
if (!mapJson) return;
|
||||
if (typeof mapJson[0] !== "object") {
|
||||
const alternativeMap = new Map<string, SourcePosition>();
|
||||
for (const [nodeId, scriptOffset] of Object.entries<number>(mapJson)) {
|
||||
alternativeMap[nodeId] = new SourcePosition(scriptOffset, -1);
|
||||
}
|
||||
mapJson = alternativeMap;
|
||||
}
|
||||
|
||||
for (const [nodeId, sourcePosition] of Object.entries<SourcePosition>(mapJson)) {
|
||||
if (sourcePosition === undefined) {
|
||||
console.warn(`Undefined source position for node id ${nodeId}`);
|
||||
}
|
||||
const inlining = this.inlinings[sourcePosition.inliningId];
|
||||
const sp = new SourcePosition(sourcePosition.scriptOffset, sourcePosition.inliningId);
|
||||
if (inlining) this.sources[inlining.sourceId].sourcePositions.push(sp);
|
||||
this.nodePositionMap[nodeId] = sp;
|
||||
const key = sp.toString();
|
||||
if (!this.positionToNodes.has(key)) {
|
||||
this.positionToNodes.set(key, new Array<string>());
|
||||
}
|
||||
this.positionToNodes.get(key).push(nodeId);
|
||||
}
|
||||
|
||||
for (const [, source] of Object.entries<Source>(this.sources)) {
|
||||
source.sourcePositions = sortUnique(source.sourcePositions,
|
||||
(a, b) => a.lessOrEquals(b),
|
||||
(a, b) => a.equals(b));
|
||||
public setFinalNodeOrigins(nodeOriginsJson): void {
|
||||
if (!nodeOriginsJson) return;
|
||||
for (const [nodeId, nodeOrigin] of Object.entries<NodeOrigin>(nodeOriginsJson)) {
|
||||
this.finalNodeOrigins[nodeId] = new NodeOrigin(nodeOrigin.nodeId, null, nodeOrigin.phase,
|
||||
nodeOrigin.reducer);
|
||||
}
|
||||
}
|
||||
|
||||
public parsePhases(phasesJson): void {
|
||||
const nodeLabelMap = new Array<NodeLabel>();
|
||||
const instructionsPhase = new InstructionsPhase();
|
||||
const selectedDynamicPhases = new Array<DynamicPhase>();
|
||||
const nodeMap = new Array<GraphNode | TurboshaftGraphNode>();
|
||||
let lastTurboshaftGraphPhase: TurboshaftGraphPhase = null;
|
||||
let lastGraphPhase: GraphPhase | TurboshaftGraphPhase = null;
|
||||
for (const [, genericPhase] of Object.entries<GenericPhase>(phasesJson)) {
|
||||
switch (genericPhase.type) {
|
||||
case PhaseType.Disassembly:
|
||||
@ -163,44 +138,58 @@ export class SourceResolver {
|
||||
const schedulePhase = new SchedulePhase(castedSchedule.name, castedSchedule.data);
|
||||
this.phaseNames.set(schedulePhase.name, this.phases.length);
|
||||
this.phases.push(schedulePhase);
|
||||
selectedDynamicPhases.push(schedulePhase);
|
||||
if (lastGraphPhase instanceof GraphPhase) {
|
||||
schedulePhase.positions = lastGraphPhase.positions;
|
||||
} else {
|
||||
const oldIdToNewIdMap = this.getOldIdToNewIdMap(this.phases.length - 1);
|
||||
schedulePhase.positions.merge(lastGraphPhase.data.nodes, oldIdToNewIdMap);
|
||||
}
|
||||
schedulePhase.instructionsPhase = instructionsPhase;
|
||||
break;
|
||||
case PhaseType.Sequence:
|
||||
const castedSequence = camelize(genericPhase) as SequencePhase;
|
||||
const sequencePhase = new SequencePhase(castedSequence.name, castedSequence.blocks,
|
||||
castedSequence.registerAllocation);
|
||||
const prevPhase = this.getDynamicPhase(this.phases.length - 1);
|
||||
sequencePhase.positions = prevPhase.positions;
|
||||
sequencePhase.instructionsPhase = prevPhase.instructionsPhase;
|
||||
this.phaseNames.set(sequencePhase.name, this.phases.length);
|
||||
this.phases.push(sequencePhase);
|
||||
break;
|
||||
case PhaseType.Instructions:
|
||||
const castedInstructions = genericPhase as InstructionsPhase;
|
||||
if (this.instructionsPhase.name === "") {
|
||||
this.instructionsPhase.name = castedInstructions.name;
|
||||
if (instructionsPhase.name === "") {
|
||||
instructionsPhase.name = castedInstructions.name;
|
||||
} else {
|
||||
this.instructionsPhase.name += `, ${castedInstructions.name}`;
|
||||
instructionsPhase.name += `, ${castedInstructions.name}`;
|
||||
}
|
||||
this.instructionsPhase.parseNodeIdToInstructionRangeFromJSON(castedInstructions
|
||||
instructionsPhase.parseNodeIdToInstructionRangeFromJSON(castedInstructions
|
||||
?.nodeIdToInstructionRange);
|
||||
this.instructionsPhase.parseBlockIdToInstructionRangeFromJSON(castedInstructions
|
||||
instructionsPhase.parseBlockIdToInstructionRangeFromJSON(castedInstructions
|
||||
?.blockIdToInstructionRange);
|
||||
this.instructionsPhase.parseInstructionOffsetToPCOffsetFromJSON(castedInstructions
|
||||
instructionsPhase.parseInstructionOffsetToPCOffsetFromJSON(castedInstructions
|
||||
?.instructionOffsetToPCOffset);
|
||||
this.instructionsPhase.parseCodeOffsetsInfoFromJSON(castedInstructions
|
||||
?.codeOffsetsInfo);
|
||||
instructionsPhase.parseCodeOffsetsInfoFromJSON(castedInstructions?.codeOffsetsInfo);
|
||||
break;
|
||||
case PhaseType.Graph:
|
||||
const castedGraph = genericPhase as GraphPhase;
|
||||
const graphPhase = new GraphPhase(castedGraph.name, 0, castedGraph.data, nodeLabelMap);
|
||||
this.recordOrigins(graphPhase);
|
||||
const graphPhase = new GraphPhase(castedGraph.name, castedGraph.data,
|
||||
nodeMap as Array<GraphNode>, this.sources, this.inlinings);
|
||||
this.phaseNames.set(graphPhase.name, this.phases.length);
|
||||
this.phases.push(graphPhase);
|
||||
selectedDynamicPhases.push(graphPhase);
|
||||
lastGraphPhase = graphPhase;
|
||||
break;
|
||||
case PhaseType.TurboshaftGraph:
|
||||
const castedTurboshaftGraph = genericPhase as TurboshaftGraphPhase;
|
||||
const turboshaftGraphPhase = new TurboshaftGraphPhase(castedTurboshaftGraph.name,
|
||||
castedTurboshaftGraph.data);
|
||||
castedTurboshaftGraph.data, nodeMap, this.sources, this.inlinings);
|
||||
this.phaseNames.set(turboshaftGraphPhase.name, this.phases.length);
|
||||
this.phases.push(turboshaftGraphPhase);
|
||||
selectedDynamicPhases.push(turboshaftGraphPhase);
|
||||
lastTurboshaftGraphPhase = turboshaftGraphPhase;
|
||||
lastGraphPhase = turboshaftGraphPhase;
|
||||
break;
|
||||
case PhaseType.TurboshaftCustomData:
|
||||
const castedCustomData = camelize(genericPhase) as TurboshaftCustomDataPhase;
|
||||
@ -212,12 +201,43 @@ export class SourceResolver {
|
||||
throw "Unsupported phase type";
|
||||
}
|
||||
}
|
||||
this.sortSourcePositions();
|
||||
this.instructionsPhase = instructionsPhase;
|
||||
if (!lastTurboshaftGraphPhase) {
|
||||
for (const phase of selectedDynamicPhases) {
|
||||
if (!phase.isDynamic()) continue;
|
||||
phase.instructionsPhase = instructionsPhase;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (instructionsPhase.name == "") return;
|
||||
// Adapting 'nodeIdToInstructionRange' array to fix Turboshaft's nodes recreation
|
||||
this.adaptInstructionsPhases(selectedDynamicPhases);
|
||||
}
|
||||
|
||||
public sourcePositionsToNodeIds(sourcePositions: Array<GenericPosition>): Set<string> {
|
||||
const nodeIds = new Set<string>();
|
||||
for (const sp of sourcePositions) {
|
||||
const nodeIdsForPosition = this.positionToNodes.get(sp.toString());
|
||||
for (const position of sourcePositions) {
|
||||
const key = position.toString();
|
||||
let nodeIdsForPosition: Array<string> = null;
|
||||
if (position instanceof SourcePosition) {
|
||||
nodeIdsForPosition = this.positions.sourcePositionToNodes.get(key);
|
||||
} else {
|
||||
// Wasm support
|
||||
nodeIdsForPosition = this.positions.bytecodePositionToNodes.get(key);
|
||||
}
|
||||
if (!nodeIdsForPosition) continue;
|
||||
for (const nodeId of nodeIdsForPosition) {
|
||||
nodeIds.add(nodeId);
|
||||
}
|
||||
}
|
||||
return nodeIds;
|
||||
}
|
||||
|
||||
public bytecodePositionsToNodeIds(bytecodePositions: Array<BytecodePosition>): Set<string> {
|
||||
const nodeIds = new Set<string>();
|
||||
for (const position of bytecodePositions) {
|
||||
const nodeIdsForPosition = this.positions.bytecodePositionToNodes.get(position.toString());
|
||||
if (!nodeIdsForPosition) continue;
|
||||
for (const nodeId of nodeIdsForPosition) {
|
||||
nodeIds.add(nodeId);
|
||||
@ -229,9 +249,17 @@ export class SourceResolver {
|
||||
public nodeIdsToSourcePositions(nodeIds: Iterable<string>): Array<GenericPosition> {
|
||||
const sourcePositions = new Map<string, GenericPosition>();
|
||||
for (const nodeId of nodeIds) {
|
||||
const position = this.nodePositionMap[nodeId];
|
||||
if (!position) continue;
|
||||
sourcePositions.set(position.toString(), position);
|
||||
const sourcePosition = this.positions.nodeIdToSourcePositionMap[nodeId];
|
||||
if (sourcePosition) {
|
||||
sourcePositions.set(sourcePosition.toString(), sourcePosition);
|
||||
}
|
||||
// Wasm support
|
||||
if (this.bytecodeSources.size == 0) {
|
||||
const bytecodePosition = this.positions.nodeIdToBytecodePositionMap[nodeId];
|
||||
if (bytecodePosition) {
|
||||
sourcePositions.set(bytecodePosition.toString(), bytecodePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
const sourcePositionArray = new Array<GenericPosition>();
|
||||
for (const sourcePosition of sourcePositions.values()) {
|
||||
@ -240,6 +268,16 @@ export class SourceResolver {
|
||||
return sourcePositionArray;
|
||||
}
|
||||
|
||||
public nodeIdsToBytecodePositions(nodeIds: Iterable<string>): Array<BytecodePosition> {
|
||||
const bytecodePositions = new Map<string, BytecodePosition>();
|
||||
for (const nodeId of nodeIds) {
|
||||
const position = this.positions.nodeIdToBytecodePositionMap[nodeId];
|
||||
if (!position) continue;
|
||||
bytecodePositions.set(position.toString(), position);
|
||||
}
|
||||
return Array.from(bytecodePositions.values());
|
||||
}
|
||||
|
||||
public translateToSourceId(sourceId: number, location?: SourcePosition): SourcePosition {
|
||||
for (const position of this.getInlineStack(location)) {
|
||||
const inlining = this.inlinings[position.inliningId];
|
||||
@ -287,6 +325,16 @@ export class SourceResolver {
|
||||
return this.phases[phaseId];
|
||||
}
|
||||
|
||||
public getGraphPhase(phaseId: number): GraphPhase | TurboshaftGraphPhase {
|
||||
const phase = this.phases[phaseId];
|
||||
return phase.isGraph() ? phase as GraphPhase | TurboshaftGraphPhase : null;
|
||||
}
|
||||
|
||||
public getDynamicPhase(phaseId: number): DynamicPhase {
|
||||
const phase = this.phases[phaseId];
|
||||
return phase.isDynamic() ? phase as DynamicPhase : null;
|
||||
}
|
||||
|
||||
public getPhaseNameById(phaseId: number): string {
|
||||
return this.getPhase(phaseId).name;
|
||||
}
|
||||
@ -320,7 +368,7 @@ export class SourceResolver {
|
||||
public setSourceLineToBytecodePosition(sourceLineToBytecodePositionJson): void {
|
||||
if (!sourceLineToBytecodePositionJson) return;
|
||||
sourceLineToBytecodePositionJson.forEach((position, idx) => {
|
||||
this.addAnyPositionToLine(idx, new BytecodePosition(position));
|
||||
this.addAnyPositionToLine(idx, new BytecodePosition(position, -1));
|
||||
});
|
||||
}
|
||||
|
||||
@ -340,22 +388,78 @@ export class SourceResolver {
|
||||
return inliningStack;
|
||||
}
|
||||
|
||||
private recordOrigins(graphPhase: GraphPhase): void {
|
||||
if (graphPhase.type !== PhaseType.Graph) return;
|
||||
for (const node of graphPhase.data.nodes) {
|
||||
graphPhase.highestNodeId = Math.max(graphPhase.highestNodeId, node.id);
|
||||
const origin = node.nodeLabel.origin;
|
||||
if (origin instanceof BytecodeOrigin) {
|
||||
const position = new BytecodePosition(origin.bytecodePosition);
|
||||
this.nodePositionMap[node.id] = position;
|
||||
const key = position.toString();
|
||||
if (!this.positionToNodes.has(key)) {
|
||||
this.positionToNodes.set(key, new Array<string>());
|
||||
private sortSourcePositions(): void {
|
||||
for (const source of Object.values<Source>(this.sources)) {
|
||||
source.sourcePositions = sortUnique(source.sourcePositions,
|
||||
(a, b) => a.lessOrEquals(b),
|
||||
(a, b) => a.equals(b));
|
||||
}
|
||||
const nodes = this.positionToNodes.get(key);
|
||||
const identifier = node.identifier();
|
||||
if (!nodes.includes(identifier)) nodes.push(identifier);
|
||||
}
|
||||
|
||||
private adaptInstructionsPhases(dynamicPhases: Array<DynamicPhase>): void {
|
||||
const seaOfNodesInstructions = new InstructionsPhase("sea of nodes");
|
||||
for (let phaseId = dynamicPhases.length - 2; phaseId >= 0; phaseId--) {
|
||||
const phase = dynamicPhases[phaseId];
|
||||
const prevPhase = dynamicPhases[phaseId + 1];
|
||||
if (phase.type == PhaseType.TurboshaftGraph && prevPhase.type == PhaseType.Schedule) {
|
||||
phase.instructionsPhase.merge(prevPhase.instructionsPhase);
|
||||
const oldIdToNewIdMap = this.getOldIdToNewIdMap(phaseId + 1);
|
||||
const maxNodeId = (phase as TurboshaftGraphPhase).highestNodeId;
|
||||
for (let nodeId = 0; nodeId <= maxNodeId; nodeId++) {
|
||||
const prevNodeId = oldIdToNewIdMap.has(nodeId) ? oldIdToNewIdMap.get(nodeId) : nodeId;
|
||||
phase.instructionsPhase.nodeIdToInstructionRange[nodeId] =
|
||||
prevPhase.instructionsPhase.getInstruction(prevNodeId);
|
||||
}
|
||||
} else if (phase.type == PhaseType.TurboshaftGraph &&
|
||||
prevPhase.type == PhaseType.TurboshaftGraph) {
|
||||
phase.instructionsPhase.merge(prevPhase.instructionsPhase);
|
||||
const maxNodeId = (phase as TurboshaftGraphPhase).highestNodeId;
|
||||
const originIdToNodesMap = (prevPhase as TurboshaftGraphPhase).originIdToNodesMap;
|
||||
for (let nodeId = 0; nodeId <= maxNodeId; nodeId++) {
|
||||
const nodes = originIdToNodesMap.get(String(nodeId));
|
||||
const prevNodeId = nodes?.length > 0 ? nodes[0]?.id : nodeId;
|
||||
phase.instructionsPhase.nodeIdToInstructionRange[nodeId] =
|
||||
prevPhase.instructionsPhase.getInstruction(prevNodeId);
|
||||
}
|
||||
} else if (phase.type == PhaseType.Schedule && prevPhase.type == PhaseType.TurboshaftGraph) {
|
||||
seaOfNodesInstructions.merge(prevPhase.instructionsPhase);
|
||||
phase.instructionsPhase = seaOfNodesInstructions;
|
||||
const originIdToNodesMap = (prevPhase as TurboshaftGraphPhase).originIdToNodesMap;
|
||||
for (const [originId, nodes] of originIdToNodesMap.entries()) {
|
||||
if (!originId || nodes.length == 0) continue;
|
||||
phase.instructionsPhase.nodeIdToInstructionRange[originId] =
|
||||
prevPhase.instructionsPhase.getInstruction(nodes[0].id);
|
||||
}
|
||||
} else if (phase.type == PhaseType.Graph && prevPhase.type == PhaseType.Graph) {
|
||||
phase.instructionsPhase = seaOfNodesInstructions;
|
||||
const prevGraphPhase = prevPhase as GraphPhase;
|
||||
for (const [originId, nodes] of prevGraphPhase.originIdToNodesMap.entries()) {
|
||||
if (!originId || nodes.length == 0) continue;
|
||||
for (const node of nodes) {
|
||||
if (!phase.instructionsPhase.nodeIdToInstructionRange[originId]) {
|
||||
if (!prevPhase.instructionsPhase.nodeIdToInstructionRange[node.id]) continue;
|
||||
phase.instructionsPhase.nodeIdToInstructionRange[originId] =
|
||||
prevPhase.instructionsPhase.getInstruction(node.id);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
phase.instructionsPhase = seaOfNodesInstructions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getOldIdToNewIdMap(phaseId: number): Map<number, number> {
|
||||
// This function works with final node origins (we can have overwriting for Turboshaft IR)
|
||||
const oldIdToNewIdMap = new Map<number, number>();
|
||||
for (const [newId, nodeOrigin] of this.finalNodeOrigins.entries()) {
|
||||
if (!nodeOrigin) continue;
|
||||
if (nodeOrigin.phase === this.phases[phaseId].name) {
|
||||
oldIdToNewIdMap.set(nodeOrigin.nodeId, newId);
|
||||
}
|
||||
}
|
||||
return oldIdToNewIdMap;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
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);
|
||||
svgNodeCustomData
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user