| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import { Schedule, SourceResolver } from "../src/source-resolver"; |
| import { TextView } from "../src/text-view"; |
| |
| export class ScheduleView extends TextView { |
| schedule: Schedule; |
| sourceResolver: SourceResolver; |
| |
| createViewElement() { |
| const pane = document.createElement('div'); |
| pane.setAttribute('id', "schedule"); |
| pane.classList.add("scrollable"); |
| pane.setAttribute("tabindex", "0"); |
| return pane; |
| } |
| |
| constructor(parentId, broker) { |
| super(parentId, broker); |
| this.sourceResolver = broker.sourceResolver; |
| } |
| |
| attachSelection(s) { |
| const view = this; |
| if (!(s instanceof Set)) return; |
| view.selectionHandler.clear(); |
| view.blockSelectionHandler.clear(); |
| const selected = new Array(); |
| for (const key of s) selected.push(key); |
| view.selectionHandler.select(selected, true); |
| } |
| |
| detachSelection() { |
| this.blockSelection.clear(); |
| return this.selection.detachSelection(); |
| } |
| |
| initializeContent(data, rememberedSelection) { |
| this.divNode.innerHTML = ''; |
| this.schedule = data.schedule; |
| this.addBlocks(data.schedule.blocks); |
| this.attachSelection(rememberedSelection); |
| this.show(); |
| } |
| |
| 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.selectionHandler.clear(); |
| } |
| view.selectionHandler.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.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.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(); |
| this.selectionHandler.clear(); |
| const query = searchBar.value; |
| if (query.length == 0) return; |
| const select = []; |
| window.sessionStorage.setItem("lastSearch", query); |
| const reg = new RegExp(query); |
| for (const node of this.schedule.nodes) { |
| if (node === undefined) continue; |
| if (reg.exec(this.lineString(node)) != null) { |
| select.push(node.id); |
| } |
| } |
| this.selectionHandler.select(select, true); |
| } |
| } |