[turbolizer] Schedule view/phase refactoring
Bug: v8:7327 Change-Id: I45085b4b2dcb76948e39e79fcf15711deb531541 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3764441 Commit-Queue: Danylo Boiko <danielboyko02@gmail.com> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Cr-Commit-Position: refs/heads/main@{#81824}
This commit is contained in:
parent
9230a278e1
commit
a1bdea56ca
@ -92,6 +92,20 @@ export class InstructionsPhase extends Phase {
|
|||||||
return this.blockIdToInstructionRange[blockId] ?? [-1, -1];
|
return this.blockIdToInstructionRange[blockId] ?? [-1, -1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getInstructionMarker(start: number, end: number): [string, string] {
|
||||||
|
if (start != end) {
|
||||||
|
return ["⊙", `This node generated instructions in range [${start},${end}). ` +
|
||||||
|
`This is currently unreliable for constants.`];
|
||||||
|
}
|
||||||
|
if (start != -1) {
|
||||||
|
return ["·", `The instruction selector did not generate instructions ` +
|
||||||
|
`for this node, but processed the node at instruction ${start}. ` +
|
||||||
|
`This usually means that this node was folded into another node; ` +
|
||||||
|
`the highlighted machine code is a guess.`];
|
||||||
|
}
|
||||||
|
return ["", `This not is not in the final schedule.`];
|
||||||
|
}
|
||||||
|
|
||||||
public getInstructionKindForPCOffset(offset: number): InstructionKind {
|
public getInstructionKindForPCOffset(offset: number): InstructionKind {
|
||||||
if (this.codeOffsetsInfo) {
|
if (this.codeOffsetsInfo) {
|
||||||
if (offset >= this.codeOffsetsInfo.deoptimizationExits) {
|
if (offset >= this.codeOffsetsInfo.deoptimizationExits) {
|
||||||
|
@ -5,22 +5,16 @@
|
|||||||
import { Phase, PhaseType } from "./phase";
|
import { Phase, PhaseType } from "./phase";
|
||||||
|
|
||||||
export class SchedulePhase extends Phase {
|
export class SchedulePhase extends Phase {
|
||||||
data: string;
|
data: ScheduleData;
|
||||||
schedule: { currentBlock, blocks: Array<any>, nodes: Array<any> };
|
|
||||||
|
|
||||||
constructor(name: string, data: string) {
|
constructor(name: string, dataJson) {
|
||||||
super(name, PhaseType.Schedule);
|
super(name, PhaseType.Schedule);
|
||||||
this.data = data;
|
this.data = new ScheduleData();
|
||||||
this.schedule = {
|
this.parseScheduleFromJSON(dataJson);
|
||||||
currentBlock: undefined,
|
|
||||||
blocks: new Array<any>(),
|
|
||||||
nodes: new Array<any>()
|
|
||||||
};
|
|
||||||
this.parseScheduleFromJSON(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseScheduleFromJSON(scheduleDataJson): void {
|
private parseScheduleFromJSON(dataJson): void {
|
||||||
const lines = scheduleDataJson.split(/[\n]/);
|
const lines = dataJson.split(/[\n]/);
|
||||||
nextLine:
|
nextLine:
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
for (const rule of this.parsingRules) {
|
for (const rule of this.parsingRules) {
|
||||||
@ -37,46 +31,39 @@ export class SchedulePhase extends Phase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createNode = match => {
|
private createNode = match => {
|
||||||
let inputs = [];
|
let inputs = new Array<number>();
|
||||||
if (match.groups.args) {
|
if (match.groups.args) {
|
||||||
const nodeIdsString = match.groups.args.replace(/\s/g, '');
|
const nodeIdsString = match.groups.args.replace(/\s/g, "");
|
||||||
const nodeIdStrings = nodeIdsString.split(',');
|
const nodeIdStrings = nodeIdsString.split(",");
|
||||||
inputs = nodeIdStrings.map(n => Number.parseInt(n, 10));
|
inputs = nodeIdStrings.map(n => Number.parseInt(n, 10));
|
||||||
}
|
}
|
||||||
const node = {
|
const nodeId = Number.parseInt(match.groups.id, 10);
|
||||||
id: Number.parseInt(match.groups.id, 10),
|
const node = new ScheduleNode(nodeId, match.groups.label, inputs);
|
||||||
label: match.groups.label,
|
|
||||||
inputs: inputs
|
|
||||||
};
|
|
||||||
if (match.groups.blocks) {
|
if (match.groups.blocks) {
|
||||||
const nodeIdsString = match.groups.blocks.replace(/\s/g, '').replace(/B/g, '');
|
const nodeIdsString = match.groups.blocks.replace(/\s/g, "").replace(/B/g, "");
|
||||||
const nodeIdStrings = nodeIdsString.split(',');
|
const nodeIdStrings = nodeIdsString.split(",");
|
||||||
this.schedule.currentBlock.succ = nodeIdStrings.map(n => Number.parseInt(n, 10));
|
this.data.lastBlock().successors = nodeIdStrings.map(n => Number.parseInt(n, 10));
|
||||||
}
|
}
|
||||||
this.schedule.nodes[node.id] = node;
|
this.data.nodes[node.id] = node;
|
||||||
this.schedule.currentBlock.nodes.push(node);
|
this.data.lastBlock().nodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createBlock = match => {
|
private createBlock = match => {
|
||||||
let predecessors = [];
|
let predecessors = new Array<number>();
|
||||||
if (match.groups.in) {
|
if (match.groups.in) {
|
||||||
const blockIdsString = match.groups.in.replace(/\s/g, '').replace(/B/g, '');
|
const blockIdsString = match.groups.in.replace(/\s/g, "").replace(/B/g, "");
|
||||||
const blockIdStrings = blockIdsString.split(',');
|
const blockIdStrings = blockIdsString.split(",");
|
||||||
predecessors = blockIdStrings.map(n => Number.parseInt(n, 10));
|
predecessors = blockIdStrings.map(n => Number.parseInt(n, 10));
|
||||||
}
|
}
|
||||||
const block = {
|
const blockId = Number.parseInt(match.groups.id, 10);
|
||||||
id: Number.parseInt(match.groups.id, 10),
|
const block = new ScheduleBlock(blockId, match.groups.deferred !== undefined,
|
||||||
isDeferred: match.groups.deferred != undefined,
|
predecessors.sort());
|
||||||
pred: predecessors.sort(),
|
this.data.blocks[block.id] = block;
|
||||||
succ: [],
|
|
||||||
nodes: []
|
|
||||||
};
|
|
||||||
this.schedule.blocks[block.id] = block;
|
|
||||||
this.schedule.currentBlock = block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setGotoSuccessor = match => {
|
private setGotoSuccessor = match => {
|
||||||
this.schedule.currentBlock.succ = [Number.parseInt(match.groups.successor.replace(/\s/g, ''), 10)];
|
this.data.lastBlock().successors =
|
||||||
|
[Number.parseInt(match.groups.successor.replace(/\s/g, ""), 10)];
|
||||||
}
|
}
|
||||||
|
|
||||||
private parsingRules = [
|
private parsingRules = [
|
||||||
@ -98,3 +85,50 @@ export class SchedulePhase extends Phase {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ScheduleNode {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
inputs: Array<number>;
|
||||||
|
|
||||||
|
constructor(id: number, label: string, inputs: Array<number>) {
|
||||||
|
this.id = id;
|
||||||
|
this.label = label;
|
||||||
|
this.inputs = inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(): string {
|
||||||
|
return `${this.id}: ${this.label}(${this.inputs.join(", ")})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ScheduleBlock {
|
||||||
|
id: number;
|
||||||
|
deferred: boolean;
|
||||||
|
predecessors: Array<number>;
|
||||||
|
successors: Array<number>;
|
||||||
|
nodes: Array<ScheduleNode>;
|
||||||
|
|
||||||
|
constructor(id: number, deferred: boolean, predecessors: Array<number>) {
|
||||||
|
this.id = id;
|
||||||
|
this.deferred = deferred;
|
||||||
|
this.predecessors = predecessors;
|
||||||
|
this.successors = new Array<number>();
|
||||||
|
this.nodes = new Array<ScheduleNode>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ScheduleData {
|
||||||
|
nodes: Array<ScheduleNode>;
|
||||||
|
blocks: Array<ScheduleBlock>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.nodes = new Array<ScheduleNode>();
|
||||||
|
this.blocks = new Array<ScheduleBlock>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public lastBlock(): ScheduleBlock {
|
||||||
|
if (this.blocks.length == 0) return null;
|
||||||
|
return this.blocks[this.blocks.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,13 +12,14 @@ export interface ClearableHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeSelectionHandler {
|
export interface NodeSelectionHandler {
|
||||||
select(nodes: Iterable<TurboshaftGraphNode | GraphNode | string>, selected: boolean): void;
|
select(nodes: Iterable<TurboshaftGraphNode | GraphNode | string | number>, selected: boolean):
|
||||||
|
void;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
brokeredNodeSelect(nodeIds: Set<string>, selected: boolean): void;
|
brokeredNodeSelect(nodeIds: Set<string>, selected: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlockSelectionHandler {
|
export interface BlockSelectionHandler {
|
||||||
select(blocks: Iterable<TurboshaftGraphBlock | string>, selected: boolean): void;
|
select(blocks: Iterable<TurboshaftGraphBlock | string | number>, selected: boolean): void;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
brokeredBlockSelect(blockIds: Array<string>, selected: boolean): void;
|
brokeredBlockSelect(blockIds: Array<string>, selected: boolean): void;
|
||||||
}
|
}
|
||||||
|
@ -2,32 +2,38 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import * as C from "./../common/constants";
|
||||||
|
import { storageSetItem } from "../common/util";
|
||||||
import { TextView } from "./text-view";
|
import { TextView } from "./text-view";
|
||||||
import { SchedulePhase } from "../phases/schedule-phase";
|
|
||||||
import { SelectionStorage } from "../selection/selection-storage";
|
import { SelectionStorage } from "../selection/selection-storage";
|
||||||
|
import { SelectionBroker } from "../selection/selection-broker";
|
||||||
|
import { ScheduleBlock, ScheduleNode, SchedulePhase } from "../phases/schedule-phase";
|
||||||
|
|
||||||
export class ScheduleView extends TextView {
|
export class ScheduleView extends TextView {
|
||||||
schedule: SchedulePhase;
|
schedule: SchedulePhase;
|
||||||
|
|
||||||
createViewElement() {
|
constructor(parent: HTMLElement, broker: SelectionBroker) {
|
||||||
const pane = document.createElement('div');
|
super(parent, broker);
|
||||||
pane.setAttribute('id', "schedule");
|
this.sourceResolver = broker.sourceResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createViewElement(): HTMLDivElement {
|
||||||
|
const pane = document.createElement("div");
|
||||||
|
pane.setAttribute("id", C.SCHEDULE_PANE_ID);
|
||||||
pane.classList.add("scrollable");
|
pane.classList.add("scrollable");
|
||||||
pane.setAttribute("tabindex", "0");
|
pane.setAttribute("tabindex", "0");
|
||||||
return pane;
|
return pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(parentId, broker) {
|
public initializeContent(schedule: SchedulePhase, rememberedSelection: SelectionStorage): void {
|
||||||
super(parentId, broker);
|
this.divNode.innerHTML = "";
|
||||||
this.sourceResolver = broker.sourceResolver;
|
this.schedule = schedule;
|
||||||
}
|
this.addBlocks(schedule.data.blocks);
|
||||||
|
this.show();
|
||||||
private attachSelection(adaptedSelection: SelectionStorage): void {
|
if (rememberedSelection) {
|
||||||
if (!(adaptedSelection instanceof SelectionStorage)) return;
|
const adaptedSelection = this.adaptSelection(rememberedSelection);
|
||||||
this.nodeSelectionHandler.clear();
|
this.attachSelection(adaptedSelection);
|
||||||
this.blockSelectionHandler.clear();
|
}
|
||||||
this.nodeSelectionHandler.select(adaptedSelection.adaptedNodes, true);
|
|
||||||
this.blockSelectionHandler.select(adaptedSelection.adaptedBocks, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public detachSelection(): SelectionStorage {
|
public detachSelection(): SelectionStorage {
|
||||||
@ -41,157 +47,134 @@ export class ScheduleView extends TextView {
|
|||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public initializeContent(schedule: SchedulePhase, rememberedSelection: SelectionStorage): void {
|
public searchInputAction(searchBar: HTMLInputElement, e: KeyboardEvent, onlyVisible: boolean):
|
||||||
this.divNode.innerHTML = "";
|
void {
|
||||||
this.schedule = schedule;
|
|
||||||
this.addBlocks(schedule.schedule.blocks);
|
|
||||||
this.show();
|
|
||||||
if (rememberedSelection) {
|
|
||||||
const adaptedSelection = this.adaptSelection(rememberedSelection);
|
|
||||||
this.attachSelection(adaptedSelection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createElementFromString(htmlString) {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.innerHTML = htmlString.trim();
|
|
||||||
return div.firstChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
elementForBlock(block) {
|
|
||||||
const view = this;
|
|
||||||
function createElement(tag: string, cls: string, content?: string) {
|
|
||||||
const el = document.createElement(tag);
|
|
||||||
el.className = cls;
|
|
||||||
if (content != undefined) el.innerHTML = content;
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mkNodeLinkHandler(nodeId) {
|
|
||||||
return function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (!e.shiftKey) {
|
|
||||||
view.nodeSelectionHandler.clear();
|
|
||||||
}
|
|
||||||
view.nodeSelectionHandler.select([nodeId], true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMarker(start, end) {
|
|
||||||
if (start != end) {
|
|
||||||
return ["⊙", `This node generated instructions in range [${start},${end}). ` +
|
|
||||||
`This is currently unreliable for constants.`];
|
|
||||||
}
|
|
||||||
if (start != -1) {
|
|
||||||
return ["·", `The instruction selector did not generate instructions ` +
|
|
||||||
`for this node, but processed the node at instruction ${start}. ` +
|
|
||||||
`This usually means that this node was folded into another node; ` +
|
|
||||||
`the highlighted machine code is a guess.`];
|
|
||||||
}
|
|
||||||
return ["", `This not is not in the final schedule.`];
|
|
||||||
}
|
|
||||||
|
|
||||||
function createElementForNode(node) {
|
|
||||||
const nodeEl = createElement("div", "node");
|
|
||||||
|
|
||||||
const [start, end] = view.sourceResolver.instructionsPhase.getInstruction(node.id);
|
|
||||||
const [marker, tooltip] = getMarker(start, end);
|
|
||||||
const instrMarker = createElement("div", "instr-marker com", marker);
|
|
||||||
instrMarker.setAttribute("title", tooltip);
|
|
||||||
instrMarker.onclick = mkNodeLinkHandler(node.id);
|
|
||||||
nodeEl.appendChild(instrMarker);
|
|
||||||
|
|
||||||
const nodeId = createElement("div", "node-id tag clickable", node.id);
|
|
||||||
nodeId.onclick = mkNodeLinkHandler(node.id);
|
|
||||||
view.addHtmlElementForNodeId(node.id, nodeId);
|
|
||||||
nodeEl.appendChild(nodeId);
|
|
||||||
const nodeLabel = createElement("div", "node-label", node.label);
|
|
||||||
nodeEl.appendChild(nodeLabel);
|
|
||||||
if (node.inputs.length > 0) {
|
|
||||||
const nodeParameters = createElement("div", "parameter-list comma-sep-list");
|
|
||||||
for (const param of node.inputs) {
|
|
||||||
const paramEl = createElement("div", "parameter tag clickable", param);
|
|
||||||
nodeParameters.appendChild(paramEl);
|
|
||||||
paramEl.onclick = mkNodeLinkHandler(param);
|
|
||||||
view.addHtmlElementForNodeId(param, paramEl);
|
|
||||||
}
|
|
||||||
nodeEl.appendChild(nodeParameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mkBlockLinkHandler(blockId) {
|
|
||||||
return function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (!e.shiftKey) {
|
|
||||||
view.blockSelectionHandler.clear();
|
|
||||||
}
|
|
||||||
view.blockSelectionHandler.select(["" + blockId], true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const scheduleBlock = createElement("div", "schedule-block");
|
|
||||||
scheduleBlock.classList.toggle("deferred", block.isDeferred);
|
|
||||||
|
|
||||||
const [start, end] = view.sourceResolver.instructionsPhase
|
|
||||||
.getInstructionRangeForBlock(block.id);
|
|
||||||
const instrMarker = createElement("div", "instr-marker com", "⊙");
|
|
||||||
instrMarker.setAttribute("title", `Instructions range for this block is [${start}, ${end})`);
|
|
||||||
instrMarker.onclick = mkBlockLinkHandler(block.id);
|
|
||||||
scheduleBlock.appendChild(instrMarker);
|
|
||||||
|
|
||||||
const blockId = createElement("div", "block-id com clickable", block.id);
|
|
||||||
blockId.onclick = mkBlockLinkHandler(block.id);
|
|
||||||
scheduleBlock.appendChild(blockId);
|
|
||||||
const blockPred = createElement("div", "predecessor-list block-list comma-sep-list");
|
|
||||||
for (const pred of block.pred) {
|
|
||||||
const predEl = createElement("div", "block-id com clickable", pred);
|
|
||||||
predEl.onclick = mkBlockLinkHandler(pred);
|
|
||||||
blockPred.appendChild(predEl);
|
|
||||||
}
|
|
||||||
if (block.pred.length) scheduleBlock.appendChild(blockPred);
|
|
||||||
const nodes = createElement("div", "nodes");
|
|
||||||
for (const node of block.nodes) {
|
|
||||||
nodes.appendChild(createElementForNode(node));
|
|
||||||
}
|
|
||||||
scheduleBlock.appendChild(nodes);
|
|
||||||
const blockSucc = createElement("div", "successor-list block-list comma-sep-list");
|
|
||||||
for (const succ of block.succ) {
|
|
||||||
const succEl = createElement("div", "block-id com clickable", succ);
|
|
||||||
succEl.onclick = mkBlockLinkHandler(succ);
|
|
||||||
blockSucc.appendChild(succEl);
|
|
||||||
}
|
|
||||||
if (block.succ.length) scheduleBlock.appendChild(blockSucc);
|
|
||||||
this.addHtmlElementForBlockId(block.id, scheduleBlock);
|
|
||||||
return scheduleBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
addBlocks(blocks) {
|
|
||||||
for (const block of blocks) {
|
|
||||||
const blockEl = this.elementForBlock(block);
|
|
||||||
this.divNode.appendChild(blockEl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lineString(node) {
|
|
||||||
return `${node.id}: ${node.label}(${node.inputs.join(", ")})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
searchInputAction(searchBar, e, onlyVisible) {
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.nodeSelectionHandler.clear();
|
this.nodeSelectionHandler.clear();
|
||||||
const query = searchBar.value;
|
const query = searchBar.value;
|
||||||
if (query.length == 0) return;
|
if (query.length == 0) return;
|
||||||
const select = [];
|
const select = new Array<number>();
|
||||||
window.sessionStorage.setItem("lastSearch", query);
|
storageSetItem("lastSearch", query);
|
||||||
const reg = new RegExp(query);
|
const reg = new RegExp(query);
|
||||||
for (const node of this.schedule.schedule.nodes) {
|
for (const node of this.schedule.data.nodes) {
|
||||||
if (node === undefined) continue;
|
if (node === undefined) continue;
|
||||||
if (reg.exec(this.lineString(node)) != null) {
|
if (reg.exec(node.toString()) !== null) {
|
||||||
select.push(node.id);
|
select.push(node.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.nodeSelectionHandler.select(select, true);
|
this.nodeSelectionHandler.select(select, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addBlocks(blocks: Array<ScheduleBlock>) {
|
||||||
|
for (const block of blocks) {
|
||||||
|
const blockEl = this.createElementForBlock(block);
|
||||||
|
this.divNode.appendChild(blockEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private attachSelection(adaptedSelection: SelectionStorage): void {
|
||||||
|
if (!(adaptedSelection instanceof SelectionStorage)) return;
|
||||||
|
this.nodeSelectionHandler.clear();
|
||||||
|
this.blockSelectionHandler.clear();
|
||||||
|
this.nodeSelectionHandler.select(adaptedSelection.adaptedNodes, true);
|
||||||
|
this.blockSelectionHandler.select(adaptedSelection.adaptedBocks, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createElementForBlock(block: ScheduleBlock): HTMLElement {
|
||||||
|
const scheduleBlock = this.createElement("div", "schedule-block");
|
||||||
|
scheduleBlock.classList.toggle("deferred", block.deferred);
|
||||||
|
|
||||||
|
const [start, end] = this.sourceResolver.instructionsPhase
|
||||||
|
.getInstructionRangeForBlock(block.id);
|
||||||
|
const instrMarker = this.createElement("div", "instr-marker com", "⊙");
|
||||||
|
instrMarker.setAttribute("title", `Instructions range for this block is [${start}, ${end})`);
|
||||||
|
instrMarker.onclick = this.mkBlockLinkHandler(block.id);
|
||||||
|
scheduleBlock.appendChild(instrMarker);
|
||||||
|
|
||||||
|
const blockId = this.createElement("div", "block-id com clickable", String(block.id));
|
||||||
|
blockId.onclick = this.mkBlockLinkHandler(block.id);
|
||||||
|
scheduleBlock.appendChild(blockId);
|
||||||
|
const blockPred = this.createElement("div", "predecessor-list block-list comma-sep-list");
|
||||||
|
for (const pred of block.predecessors) {
|
||||||
|
const predEl = this.createElement("div", "block-id com clickable", String(pred));
|
||||||
|
predEl.onclick = this.mkBlockLinkHandler(pred);
|
||||||
|
blockPred.appendChild(predEl);
|
||||||
|
}
|
||||||
|
if (block.predecessors.length) scheduleBlock.appendChild(blockPred);
|
||||||
|
const nodes = this.createElement("div", "nodes");
|
||||||
|
for (const node of block.nodes) {
|
||||||
|
nodes.appendChild(this.createElementForNode(node));
|
||||||
|
}
|
||||||
|
scheduleBlock.appendChild(nodes);
|
||||||
|
|
||||||
|
const blockSucc = this.createElement("div", "successor-list block-list comma-sep-list");
|
||||||
|
for (const succ of block.successors) {
|
||||||
|
const succEl = this.createElement("div", "block-id com clickable", String(succ));
|
||||||
|
succEl.onclick = this.mkBlockLinkHandler(succ);
|
||||||
|
blockSucc.appendChild(succEl);
|
||||||
|
}
|
||||||
|
if (block.successors.length) scheduleBlock.appendChild(blockSucc);
|
||||||
|
this.addHtmlElementForBlockId(block.id, scheduleBlock);
|
||||||
|
return scheduleBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createElementForNode(node: ScheduleNode): HTMLElement {
|
||||||
|
const nodeEl = this.createElement("div", "node");
|
||||||
|
|
||||||
|
const [start, end] = this.sourceResolver.instructionsPhase.getInstruction(node.id);
|
||||||
|
const [marker, tooltip] = this.sourceResolver.instructionsPhase
|
||||||
|
.getInstructionMarker(start, end);
|
||||||
|
const instrMarker = this.createElement("div", "instr-marker com", marker);
|
||||||
|
instrMarker.setAttribute("title", tooltip);
|
||||||
|
instrMarker.onclick = this.mkNodeLinkHandler(node.id);
|
||||||
|
nodeEl.appendChild(instrMarker);
|
||||||
|
|
||||||
|
const nodeIdEl = this.createElement("div", "node-id tag clickable", String(node.id));
|
||||||
|
nodeIdEl.onclick = this.mkNodeLinkHandler(node.id);
|
||||||
|
this.addHtmlElementForNodeId(node.id, nodeIdEl);
|
||||||
|
nodeEl.appendChild(nodeIdEl);
|
||||||
|
const nodeLabel = this.createElement("div", "node-label", node.label);
|
||||||
|
nodeEl.appendChild(nodeLabel);
|
||||||
|
if (node.inputs.length > 0) {
|
||||||
|
const nodeParameters = this.createElement("div", "parameter-list comma-sep-list");
|
||||||
|
for (const param of node.inputs) {
|
||||||
|
const paramEl = this.createElement("div", "parameter tag clickable", String(param));
|
||||||
|
nodeParameters.appendChild(paramEl);
|
||||||
|
paramEl.onclick = this.mkNodeLinkHandler(param);
|
||||||
|
this.addHtmlElementForNodeId(param, paramEl);
|
||||||
|
}
|
||||||
|
nodeEl.appendChild(nodeParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private mkBlockLinkHandler(blockId: number): (e: MouseEvent) => void {
|
||||||
|
const view = this;
|
||||||
|
return function (e: MouseEvent) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!e.shiftKey) {
|
||||||
|
view.blockSelectionHandler.clear();
|
||||||
|
}
|
||||||
|
view.blockSelectionHandler.select([blockId], true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private mkNodeLinkHandler(nodeId: number): (e: MouseEvent) => void {
|
||||||
|
const view = this;
|
||||||
|
return function (e: MouseEvent) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!e.shiftKey) {
|
||||||
|
view.nodeSelectionHandler.clear();
|
||||||
|
}
|
||||||
|
view.nodeSelectionHandler.select([nodeId], true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private createElement(tag: string, cls: string, content?: string) {
|
||||||
|
const el = document.createElement(tag);
|
||||||
|
el.className = cls;
|
||||||
|
if (content !== undefined) el.innerHTML = content;
|
||||||
|
return el;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ export abstract class TextView extends PhaseView {
|
|||||||
nodeIdToBlockId: Array<string>;
|
nodeIdToBlockId: Array<string>;
|
||||||
patterns: Array<Array<any>>;
|
patterns: Array<Array<any>>;
|
||||||
|
|
||||||
constructor(parent: HTMLDivElement, broker: SelectionBroker) {
|
constructor(parent: HTMLElement, broker: SelectionBroker) {
|
||||||
super(parent);
|
super(parent);
|
||||||
this.broker = broker;
|
this.broker = broker;
|
||||||
this.sourceResolver = broker.sourceResolver;
|
this.sourceResolver = broker.sourceResolver;
|
||||||
@ -69,7 +69,7 @@ export abstract class TextView extends PhaseView {
|
|||||||
|
|
||||||
public initializeContent(genericPhase: GenericTextPhase, _): void {
|
public initializeContent(genericPhase: GenericTextPhase, _): void {
|
||||||
this.clearText();
|
this.clearText();
|
||||||
if (!(genericPhase instanceof SequencePhase)) {
|
if (genericPhase instanceof DisassemblyPhase) {
|
||||||
this.processText(genericPhase.data);
|
this.processText(genericPhase.data);
|
||||||
}
|
}
|
||||||
this.show();
|
this.show();
|
||||||
@ -207,7 +207,7 @@ export abstract class TextView extends PhaseView {
|
|||||||
private initializeNodeSelectionHandler(): NodeSelectionHandler & ClearableHandler {
|
private initializeNodeSelectionHandler(): NodeSelectionHandler & ClearableHandler {
|
||||||
const view = this;
|
const view = this;
|
||||||
return {
|
return {
|
||||||
select: function (nodeIds: Array<string>, selected: boolean) {
|
select: function (nodeIds: Array<string | number>, selected: boolean) {
|
||||||
view.nodeSelection.select(nodeIds, selected);
|
view.nodeSelection.select(nodeIds, selected);
|
||||||
view.updateSelection();
|
view.updateSelection();
|
||||||
view.broker.broadcastNodeSelect(this, view.nodeSelection.selectedKeys(), selected);
|
view.broker.broadcastNodeSelect(this, view.nodeSelection.selectedKeys(), selected);
|
||||||
|
Loading…
Reference in New Issue
Block a user