1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {Schedule,SourceResolver} from "./source-resolver.js"
6import {isIterable} from "./util.js"
7import {PhaseView} from "./view.js"
8import {TextView} from "./text-view.js"
9
10export class ScheduleView extends TextView implements PhaseView {
11  schedule: Schedule;
12  sourceResolver: SourceResolver;
13
14  createViewElement() {
15    const pane = document.createElement('div');
16    pane.setAttribute('id', "schedule");
17    return pane;
18  }
19
20  constructor(parentId, broker) {
21    super(parentId, broker, null);
22    this.sourceResolver = broker.sourceResolver;
23  }
24
25  attachSelection(s) {
26    const view = this;
27    if (!(s instanceof Set)) return;
28    view.selectionHandler.clear();
29    view.blockSelectionHandler.clear();
30    const selected = new Array();
31    for (const key of s) selected.push(key);
32    view.selectionHandler.select(selected, true);
33  }
34
35  detachSelection() {
36    this.blockSelection.clear();
37    return this.selection.detachSelection();
38  }
39
40  initializeContent(data, rememberedSelection) {
41    this.divNode.innerHTML = '';
42    this.schedule = data.schedule
43    this.addBlocks(data.schedule.blocks);
44    this.attachSelection(rememberedSelection);
45  }
46
47  createElementFromString(htmlString) {
48    var div = document.createElement('div');
49    div.innerHTML = htmlString.trim();
50    return div.firstChild;
51  }
52
53  elementForBlock(block) {
54    const view = this;
55    function createElement(tag: string, cls: string | Array<string>, content?: string) {
56      const el = document.createElement(tag);
57      if (isIterable(cls)) {
58        for (const c of cls) el.classList.add(c);
59      } else {
60        el.classList.add(cls);
61      }
62      if (content != undefined) el.innerHTML = content;
63      return el;
64    }
65
66    function mkNodeLinkHandler(nodeId) {
67      return function (e) {
68        e.stopPropagation();
69        if (!e.shiftKey) {
70          view.selectionHandler.clear();
71        }
72        view.selectionHandler.select([nodeId], true);
73      };
74    }
75
76    function getMarker(start, end) {
77      if (start != end) {
78        return ["&#8857;", `This node generated instructions in range [${start},${end}). ` +
79                           `This is currently unreliable for constants.`];
80      }
81      if (start != -1) {
82        return ["&#183;", `The instruction selector did not generate instructions ` +
83                          `for this node, but processed the node at instruction ${start}. ` +
84                          `This usually means that this node was folded into another node; ` +
85                          `the highlighted machine code is a guess.`];
86      }
87      return ["", `This not is not in the final schedule.`]
88    }
89
90    function createElementForNode(node) {
91      const nodeEl = createElement("div", "node");
92
93      const [start, end] = view.sourceResolver.getInstruction(node.id);
94      const [marker, tooltip] = getMarker(start, end);
95      const instrMarker = createElement("div", ["instr-marker", "com"], marker);
96      instrMarker.setAttribute("title", tooltip);
97      instrMarker.onclick = mkNodeLinkHandler(node.id);
98      nodeEl.appendChild(instrMarker);
99
100
101      const node_id = createElement("div", ["node-id", "tag", "clickable"], node.id);
102      node_id.onclick = mkNodeLinkHandler(node.id);
103      view.addHtmlElementForNodeId(node.id, node_id);
104      nodeEl.appendChild(node_id);
105      const node_label = createElement("div", "node-label", node.label);
106      nodeEl.appendChild(node_label);
107      if (node.inputs.length > 0) {
108        const node_parameters = createElement("div", ["parameter-list", "comma-sep-list"]);
109        for (const param of node.inputs) {
110          const paramEl = createElement("div", ["parameter", "tag", "clickable"], param);
111          node_parameters.appendChild(paramEl);
112          paramEl.onclick = mkNodeLinkHandler(param);
113          view.addHtmlElementForNodeId(param, paramEl);
114        }
115        nodeEl.appendChild(node_parameters);
116      }
117
118      return nodeEl;
119    }
120
121    function mkBlockLinkHandler(blockId) {
122      return function (e) {
123        e.stopPropagation();
124        if (!e.shiftKey) {
125          view.blockSelectionHandler.clear();
126        }
127        view.blockSelectionHandler.select(["" + blockId], true);
128      };
129    }
130
131    const schedule_block = createElement("div", "schedule-block");
132
133    const [start, end] = view.sourceResolver.getInstructionRangeForBlock(block.id);
134    const instrMarker = createElement("div", ["instr-marker", "com"], "&#8857;");
135    instrMarker.setAttribute("title", `Instructions range for this block is [${start}, ${end})`)
136    instrMarker.onclick = mkBlockLinkHandler(block.id);
137    schedule_block.appendChild(instrMarker);
138
139    const block_id = createElement("div", ["block-id", "com", "clickable"], block.id);
140    block_id.onclick = mkBlockLinkHandler(block.id);
141    schedule_block.appendChild(block_id);
142    const block_pred = createElement("div", ["predecessor-list", "block-list", "comma-sep-list"]);
143    for (const pred of block.pred) {
144      const predEl = createElement("div", ["block-id", "com", "clickable"], pred);
145      predEl.onclick = mkBlockLinkHandler(pred);
146      block_pred.appendChild(predEl);
147    }
148    if (block.pred.length) schedule_block.appendChild(block_pred);
149    const nodes = createElement("div", "nodes");
150    for (const node of block.nodes) {
151      nodes.appendChild(createElementForNode(node));
152    }
153    schedule_block.appendChild(nodes);
154    const block_succ = createElement("div", ["successor-list", "block-list", "comma-sep-list"]);
155    for (const succ of block.succ) {
156      const succEl = createElement("div", ["block-id", "com", "clickable"], succ);
157      succEl.onclick = mkBlockLinkHandler(succ);
158      block_succ.appendChild(succEl);
159    }
160    if (block.succ.length) schedule_block.appendChild(block_succ);
161    this.addHtmlElementForBlockId(block.id, schedule_block);
162    return schedule_block;
163  }
164
165  addBlocks(blocks) {
166    for (const block of blocks) {
167      const blockEl = this.elementForBlock(block);
168      this.divNode.appendChild(blockEl);
169    }
170  }
171
172  lineString(node) {
173    return `${node.id}: ${node.label}(${node.inputs.join(", ")})`
174  }
175
176  searchInputAction(searchBar, e) {
177    e.stopPropagation();
178    this.selectionHandler.clear();
179    const query = searchBar.value;
180    if (query.length == 0) return;
181    const select = [];
182    window.sessionStorage.setItem("lastSearch", query);
183    const reg = new RegExp(query);
184    for (const node of this.schedule.nodes) {
185      if (node === undefined) continue;
186      if (reg.exec(this.lineString(node)) != null) {
187        select.push(node.id)
188      }
189    }
190    this.selectionHandler.select(select, true);
191  }
192
193  onresize() { }
194}
195