Commit d4258eb1 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbolizer] Convert Turbolizer to TypeScript

Change-Id: I2be450c6498ce863d5e36acf02db643788e6c8bf
Reviewed-on: https://chromium-review.googlesource.com/1068045
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53400}
parent 3e572613
......@@ -100,3 +100,5 @@ turbo*.dot
turbo*.json
v8.ignition_dispatches_table.json
/Default/
node_modules
tools/turbolizer/build
......@@ -8,8 +8,13 @@ source code, Turbofan IR graphs, scheduled IR nodes and generated assembly code.
Turbolizer consumes .json files that are generated per-function by d8 by passing
the '--trace-turbo' command-line flag.
Host the turbolizer locally by starting a web server that serves the contents of
the turbolizer directory, e.g.:
Turbolizer is build using npm:
npm i
npm run-script build
Afterwards, turbolizer can be hosted locally by starting a web server that serve
the contents of the turbolizer directory, e.g.:
cd src/tools/turbolizer
python -m SimpleHTTPServer 8000
......@@ -60,3 +65,11 @@ script command must be piped to a file for uploading to turbolizer.
There are many options that can be added to the first command, for example '-e'
can be used to specify the counting of specific events (default: cycles), as
well as '--cpu' to specify which CPU to sample.
Turbolizer build process
------------------------
Turbolizer is currently migrating to TypeScript. The typescript sources reside in
tools/turbolizer/src, and the typescript compiler will put the JavaScript output
into tools/turbolizer/build/. The index.html file is set up to load the JavaScript
from that directory.
// Copyright 2014 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.
var MINIMUM_EDGE_SEPARATION = 20;
function isEdgeInitiallyVisible(target, index, source, type) {
return type == "control" && (target.cfg || source.cfg);
}
var Edge = function(target, index, source, type) {
this.target = target;
this.source = source;
this.index = index;
this.type = type;
this.backEdgeNumber = 0;
this.visible = isEdgeInitiallyVisible(target, index, source, type);
};
Edge.prototype.stringID = function() {
return this.source.id + "," + this.index + "," + this.target.id;
};
Edge.prototype.isVisible = function() {
return this.visible && this.source.visible && this.target.visible;
};
Edge.prototype.getInputHorizontalPosition = function(graph) {
if (this.backEdgeNumber > 0) {
return graph.maxGraphNodeX + this.backEdgeNumber * MINIMUM_EDGE_SEPARATION;
}
var source = this.source;
var target = this.target;
var index = this.index;
var input_x = target.x + target.getInputX(index);
var inputApproach = target.getInputApproach(this.index);
var outputApproach = source.getOutputApproach(graph);
if (inputApproach > outputApproach) {
return input_x;
} else {
var inputOffset = MINIMUM_EDGE_SEPARATION * (index + 1);
return (target.x < source.x)
? (target.x + target.getTotalNodeWidth() + inputOffset)
: (target.x - inputOffset)
}
}
Edge.prototype.generatePath = function(graph) {
var target = this.target;
var source = this.source;
var input_x = target.x + target.getInputX(this.index);
var arrowheadHeight = 7;
var input_y = target.y - 2 * DEFAULT_NODE_BUBBLE_RADIUS - arrowheadHeight;
var output_x = source.x + source.getOutputX();
var output_y = source.y + graph.getNodeHeight(source) + DEFAULT_NODE_BUBBLE_RADIUS;
var inputApproach = target.getInputApproach(this.index);
var outputApproach = source.getOutputApproach(graph);
var horizontalPos = this.getInputHorizontalPosition(graph);
var result = "M" + output_x + "," + output_y +
"L" + output_x + "," + outputApproach +
"L" + horizontalPos + "," + outputApproach;
if (horizontalPos != input_x) {
result += "L" + horizontalPos + "," + inputApproach;
} else {
if (inputApproach < outputApproach) {
inputApproach = outputApproach;
}
}
result += "L" + input_x + "," + inputApproach +
"L" + input_x + "," + input_y;
return result;
}
Edge.prototype.isBackEdge = function() {
return this.target.hasBackEdges() && (this.target.rank < this.source.rank);
}
......@@ -59,23 +59,6 @@
</div>
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="monkey.js"></script>
<script src="util.js"></script>
<script src="lang-disassembly.js"></script>
<script src="node.js"></script>
<script src="edge.js"></script>
<script src="source-resolver.js"></script>
<script src="selection.js"></script>
<script src="selection-broker.js"></script>
<script src="constants.js"></script>
<script src="view.js"></script>
<script src="text-view.js"></script>
<script src="code-view.js"></script>
<script src="graph-layout.js"></script>
<script src="graph-view.js"></script>
<script src="schedule-view.js"></script>
<script src="disassembly-view.js"></script>
<script src="graphmultiview.js"></script>
<script src="turbo-visualizer.js"></script>
<script src="build/turbolizer.js"></script>
</body>
</html>
{
"name": "turbolizer",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/d3": {
"version": "3.5.40",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.40.tgz",
"integrity": "sha512-GrJeI6Yg6uCIYRtWlMGxDtgsKSs2aMW0edrO/GJk+O7y4iayn8lEoHAn50wkjb9voRq9JimhCN/FIXLzAxkCoA=="
},
"typescript": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz",
"integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==",
"dev": true
}
}
}
{
"name": "turbolizer",
"version": "0.1.0",
"description": "Visualization tool for V8 TurboFan IR graphs",
"main": "index.html",
"scripts": {
"build": "tsc; echo Done. Note that typescript errors are currently ignored."
},
"author": "The V8 team",
"license": "MIT",
"dependencies": {
"@types/d3": "^3.5.40"
},
"repository": {
"type": "git",
"url": "https://github.com/v8/v8.git"
},
"devDependencies": {
"typescript": "^2.8.3"
}
}
......@@ -4,13 +4,21 @@
"use strict";
enum CodeMode {
MAIN_SOURCE = "main function",
INLINED_SOURCE = "inlined function"
};
class CodeView extends View {
static get MAIN_SOURCE() {
return "main function";
}
static get INLINED_SOURCE() {
return "inlined function";
}
broker: SelectionBroker;
source: Source;
sourceResolver: SourceResolver;
codeMode: CodeMode;
lineToSourcePositions: Map<string, Array<SourcePosition>>;
sourcePositionToHtmlElement: Map<string, HTMLElement>;
showAdditionalInliningPosition: boolean;
selectionHandler: SelectionHandler;
selection: MySelection;
createViewElement() {
const sourceContainer = document.createElement("div");
......@@ -18,10 +26,9 @@ class CodeView extends View {
return sourceContainer;
}
constructor(parentId, broker, sourceResolver, sourceFunction, codeMode) {
constructor(parentId, broker, sourceResolver, sourceFunction, codeMode: CodeMode) {
super(parentId);
let view = this;
view.mouseDown = false;
view.broker = broker;
view.source = null;
view.sourceResolver = sourceResolver;
......@@ -62,7 +69,7 @@ class CodeView extends View {
view.updateSelection();
},
};
view.selection = new Selection(sourcePositionToStringKey);
view.selection = new MySelection(sourcePositionToStringKey);
broker.addSourcePositionHandler(selectionHandler);
this.selectionHandler = selectionHandler;
this.initializeCode();
......@@ -85,12 +92,12 @@ class CodeView extends View {
}
getHtmlElementForSourcePosition(sourcePosition) {
const key = sourcePositionToStringKey(lineNumber);
const key = sourcePositionToStringKey(sourcePosition);
return this.sourcePositionToHtmlElement.get(key);
}
updateSelection(scrollIntoView) {
const mkVisible = new ViewElements(this.divNode.parentNode);
updateSelection(scrollIntoView: boolean = false) : void {
const mkVisible = new ViewElements(this.divNode.parentNode as HTMLElement);
for (const [sp, el] of this.sourcePositionToHtmlElement.entries()) {
const isSelected = this.selection.isKeySelected(sp);
mkVisible.consider(el, isSelected);
......@@ -100,7 +107,6 @@ class CodeView extends View {
}
initializeContent(data, rememberedSelection) {
this.data = data;
}
getCodeHtmlElementName() {
......@@ -111,9 +117,9 @@ class CodeView extends View {
return `source-pre-${this.source.sourceId}-header`;
}
getHtmlCodeLines() {
const lineListDiv = this.divNode.querySelector(`#${this.getCodeHtmlElementName()} ol`).childNodes;
return lineListDiv;
getHtmlCodeLines(): NodeListOf<HTMLElement> {
const ordereList = this.divNode.querySelector(`#${this.getCodeHtmlElementName()} ol`);
return ordereList.childNodes as NodeListOf<HTMLElement>;
}
onSelectLine(lineNumber, doClear) {
......@@ -138,7 +144,7 @@ class CodeView extends View {
const sourceText = source.sourceText;
if (!sourceText) return;
const sourceContainer = view.divNode;
if (this.codeMode == CodeView.MAIN_SOURCE) {
if (this.codeMode == CodeMode.MAIN_SOURCE) {
sourceContainer.classList.add("main-source");
} else {
sourceContainer.classList.add("inlined-source");
......@@ -154,8 +160,8 @@ class CodeView extends View {
codeModeDiv.classList.add("code-mode");
codeModeDiv.innerHTML = `${this.codeMode}`;
codeHeader.appendChild(codeModeDiv);
var clearDiv = document.createElement("div");
clearDiv.style = "clear:both;"
const clearDiv = document.createElement("div");
clearDiv.style.clear = "both";
codeHeader.appendChild(clearDiv);
sourceContainer.appendChild(codeHeader);
var codePre = document.createElement("pre");
......@@ -184,7 +190,7 @@ class CodeView extends View {
view.selectionHandler.clear();
}
const base = source.startPosition;
const base:number = source.startPosition;
let current = 0;
const lineListDiv = this.getHtmlCodeLines();
let newlineAdjust = 0;
......@@ -193,7 +199,7 @@ class CodeView extends View {
const lineNumber = i + 1;
const currentLineElement = lineListDiv[i];
currentLineElement.id = "li" + i;
currentLineElement.dataset.lineNumber = lineNumber;
currentLineElement.dataset.lineNumber = "" + lineNumber;
const spans = currentLineElement.childNodes;
for (let j = 0; j < spans.length; ++j) {
const currentSpan = spans[j];
......@@ -258,4 +264,5 @@ class CodeView extends View {
}
deleteContent() { }
detachSelection() { return null; }
}
......@@ -2,25 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var MAX_RANK_SENTINEL = 0;
var GRAPH_MARGIN = 250;
var WIDTH = 'width';
var HEIGHT = 'height';
var VISIBILITY = 'visibility';
var SOURCE_PANE_ID = 'left';
var SOURCE_COLLAPSE_ID = 'source-shrink';
var SOURCE_EXPAND_ID = 'source-expand';
var INTERMEDIATE_PANE_ID = 'middle';
var GRAPH_PANE_ID = 'graph';
var SCHEDULE_PANE_ID = 'schedule';
var GENERATED_PANE_ID = 'right';
var DISASSEMBLY_PANE_ID = 'disassembly';
var DISASSEMBLY_COLLAPSE_ID = 'disassembly-shrink';
var DISASSEMBLY_EXPAND_ID = 'disassembly-expand';
var COLLAPSE_PANE_BUTTON_VISIBLE = 'button-input';
var COLLAPSE_PANE_BUTTON_INVISIBLE = 'button-input-invisible';
var UNICODE_BLOCK = '&#9611;';
var PROF_COLS = [
const MAX_RANK_SENTINEL = 0;
const GRAPH_MARGIN = 250;
const WIDTH = 'width';
const HEIGHT = 'height';
const VISIBILITY = 'visibility';
const SOURCE_PANE_ID = 'left';
const SOURCE_COLLAPSE_ID = 'source-shrink';
const SOURCE_EXPAND_ID = 'source-expand';
const INTERMEDIATE_PANE_ID = 'middle';
const GRAPH_PANE_ID = 'graph';
const SCHEDULE_PANE_ID = 'schedule';
const GENERATED_PANE_ID = 'right';
const DISASSEMBLY_PANE_ID = 'disassembly';
const DISASSEMBLY_COLLAPSE_ID = 'disassembly-shrink';
const DISASSEMBLY_EXPAND_ID = 'disassembly-expand';
const COLLAPSE_PANE_BUTTON_VISIBLE = 'button-input';
const COLLAPSE_PANE_BUTTON_INVISIBLE = 'button-input-invisible';
const UNICODE_BLOCK = '&#9611;';
const PROF_COLS = [
{ perc: 0, col: { r: 255, g: 255, b: 255 } },
{ perc: 0.5, col: { r: 255, g: 255, b: 128 } },
{ perc: 5, col: { r: 255, g: 128, b: 0 } },
......
......@@ -5,6 +5,11 @@
"use strict";
class DisassemblyView extends TextView {
SOURCE_POSITION_HEADER_REGEX: any;
addr_event_counts: any;
total_event_counts: any;
max_event_counts: any;
pos_lines: Array<any>;
createViewElement() {
const pane = document.createElement('div');
......@@ -50,15 +55,13 @@ class DisassemblyView extends TextView {
};
let POSITION_STYLE = {
css: 'com',
location: function (text) {
view.pos_start = Number(text);
}
};
let OPCODE_STYLE = {
css: 'kwd',
};
const BLOCK_HEADER_STYLE = {
css: ['com', 'block'],
block_id: null,
blockId: function (text) {
let matches = /\d+/.exec(text);
if (!matches) return undefined;
......@@ -80,6 +83,7 @@ class DisassemblyView extends TextView {
};
const SOURCE_POSITION_HEADER_STYLE = {
css: 'com',
currentSourcePosition: null,
sourcePosition: function (text) {
let matches = view.SOURCE_POSITION_HEADER_REGEX.exec(text);
if (!matches) return undefined;
......@@ -131,14 +135,8 @@ class DisassemblyView extends TextView {
view.setPatterns(patterns);
}
initializeContent(data, rememberedSelection) {
this.data = data;
super.initializeContent(data, rememberedSelection);
}
initializeCode(sourceText, sourcePosition) {
let view = this;
view.pos_start = -1;
view.addr_event_counts = null;
view.total_event_counts = null;
view.max_event_counts = null;
......@@ -242,4 +240,6 @@ class DisassemblyView extends TextView {
}
return fragments;
}
detachSelection() { return null; }
}
// Copyright 2014 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.
var MINIMUM_EDGE_SEPARATION = 20;
function isEdgeInitiallyVisible(target, index, source, type) {
return type == "control" && (target.cfg || source.cfg);
}
class Edge {
target: GNode;
source: GNode;
index: number;
type: String;
backEdgeNumber: number;
visible: boolean;
constructor(target: GNode, index: number, source: GNode, type: string) {
this.target = target;
this.source = source;
this.index = index;
this.type = type;
this.backEdgeNumber = 0;
this.visible = isEdgeInitiallyVisible(target, index, source, type);
}
stringID() {
return this.source.id + "," + this.index + "," + this.target.id;
};
isVisible() {
return this.visible && this.source.visible && this.target.visible;
};
getInputHorizontalPosition(graph) {
if (this.backEdgeNumber > 0) {
return graph.maxGraphNodeX + this.backEdgeNumber * MINIMUM_EDGE_SEPARATION;
}
var source = this.source;
var target = this.target;
var index = this.index;
var input_x = target.x + target.getInputX(index);
var inputApproach = target.getInputApproach(this.index);
var outputApproach = source.getOutputApproach(graph);
if (inputApproach > outputApproach) {
return input_x;
} else {
var inputOffset = MINIMUM_EDGE_SEPARATION * (index + 1);
return (target.x < source.x)
? (target.x + target.getTotalNodeWidth() + inputOffset)
: (target.x - inputOffset)
}
}
generatePath(graph) {
var target = this.target;
var source = this.source;
var input_x = target.x + target.getInputX(this.index);
var arrowheadHeight = 7;
var input_y = target.y - 2 * DEFAULT_NODE_BUBBLE_RADIUS - arrowheadHeight;
var output_x = source.x + source.getOutputX();
var output_y = source.y + graph.getNodeHeight(source) + DEFAULT_NODE_BUBBLE_RADIUS;
var inputApproach = target.getInputApproach(this.index);
var outputApproach = source.getOutputApproach(graph);
var horizontalPos = this.getInputHorizontalPosition(graph);
var result = "M" + output_x + "," + output_y +
"L" + output_x + "," + outputApproach +
"L" + horizontalPos + "," + outputApproach;
if (horizontalPos != input_x) {
result += "L" + horizontalPos + "," + inputApproach;
} else {
if (inputApproach < outputApproach) {
inputApproach = outputApproach;
}
}
result += "L" + input_x + "," + inputApproach +
"L" + input_x + "," + input_y;
return result;
}
isBackEdge() {
return this.target.hasBackEdges() && (this.target.rank < this.source.rank);
}
}
\ No newline at end of file
......@@ -8,22 +8,52 @@ function nodeToStringKey(n) {
return "" + n.id;
}
interface GraphState {
showTypes: boolean;
selection: MySelection;
mouseDownNode: any;
justDragged: boolean,
justScaleTransGraph: boolean,
lastKeyDown: number,
hideDead: boolean
}
class GraphView extends View {
divElement: d3.Selection<any>;
svg: d3.Selection<any>;
showPhaseByName: (string) => void;
state: GraphState;
nodes: Array<GNode>;
edges: Array<any>;
selectionHandler: NodeSelectionHandler;
graphElement: d3.Selection<any>;
visibleNodes: d3.Selection<GNode>;
visibleEdges: d3.Selection<Edge>;
minGraphX: number;
maxGraphX: number;
minGraphY: number;
maxGraphY: number;
maxGraphNodeX: number;
drag: d3.behavior.Drag<{}>;
dragSvg: d3.behavior.Zoom<{}>;
nodeMap: Array<any>;
visibleBubbles: d3.Selection<any>;
transitionTimout: number;
createViewElement() {
const pane = document.createElement('div');
pane.setAttribute('id', "graph");
return pane;
}
constructor(id, broker, showPhaseByName) {
constructor(id, broker, showPhaseByName: (string) => void) {
super(id);
var graph = this;
this.showPhaseByName = showPhaseByName
var svg = this.divElement.append("svg").attr('version', '1.1')
this.showPhaseByName = showPhaseByName;
this.divElement = d3.select(this.divNode);
const svg = this.divElement.append("svg").attr('version', '1.1')
.attr("width", "100%")
.attr("height", "100%");
svg.on("mousedown", function (d) { graph.svgMouseDown.call(graph, d); });
svg.on("mouseup", function (d) { graph.svgMouseUp.call(graph, d); });
graph.svg = svg;
......@@ -86,9 +116,9 @@ class GraphView extends View {
};
broker.addNodeHandler(this.selectionHandler);
graph.state.selection = new Selection(nodeToStringKey);
graph.state.selection = new MySelection(nodeToStringKey);
var defs = svg.append('svg:defs');
const defs = svg.append('svg:defs');
defs.append('svg:marker')
.attr('id', 'end-arrow')
.attr('viewBox', '0 -4 8 8')
......@@ -103,7 +133,7 @@ class GraphView extends View {
graph.visibleEdges = this.graphElement.append("g");
graph.visibleNodes = this.graphElement.append("g");
graph.drag = d3.behavior.drag()
graph.drag = d3.behavior.drag<GNode>()
.origin(function (d) {
return { x: d.x, y: d.y };
})
......@@ -240,7 +270,7 @@ class GraphView extends View {
};
measureText(text) {
var textMeasure = document.getElementById('text-measure');
const textMeasure = document.getElementById('text-measure');
textMeasure.textContent = text;
return {
width: textMeasure.getBBox().width,
......@@ -250,10 +280,10 @@ class GraphView extends View {
createGraph(data, rememberedSelection) {
var g = this;
g.nodes = data.nodes;
g.nodes = [];
g.nodeMap = [];
g.nodes.forEach(function (n, i) {
n.__proto__ = Node;
data.nodes.forEach(function (n, i) {
n.__proto__ = GNode.prototype;
n.visible = false;
n.x = 0;
n.y = 0;
......@@ -276,6 +306,7 @@ class GraphView extends View {
NODE_INPUT_WIDTH);
var innerheight = Math.max(n.labelbbox.height, n.typebbox.height);
n.normalheight = innerheight + 20;
g.nodes.push(n);
});
g.edges = [];
data.edges.forEach(function (e, i) {
......@@ -408,7 +439,7 @@ class GraphView extends View {
}
}
selectAllNodes(inEdges, filter) {
selectAllNodes() {
var graph = this;
if (!d3.event.shiftKey) {
graph.state.selection.clear();
......@@ -480,8 +511,8 @@ class GraphView extends View {
graph.toggleTypes();
}
searchInputAction(graph, searchBar) {
if (d3.event.keyCode == 13) {
searchInputAction(graph, searchBar, e:KeyboardEvent) {
if (e.keyCode == 13) {
graph.selectionHandler.clear();
var query = searchBar.value;
window.sessionStorage.setItem("lastSearch", query);
......@@ -497,8 +528,8 @@ class GraphView extends View {
const selection = graph.nodes.filter(
function (n, i) {
if ((d3.event.ctrlKey || n.visible) && filterFunction(n)) {
if (d3.event.ctrlKey) n.visible = true;
if ((e.ctrlKey || n.visible) && filterFunction(n)) {
if (e.ctrlKey) n.visible = true;
return true;
}
return false;
......@@ -510,11 +541,7 @@ class GraphView extends View {
searchBar.blur();
graph.viewSelection();
}
d3.event.stopPropagation();
}
svgMouseDown() {
this.state.graphMouseDown = true;
e.stopPropagation();
}
svgMouseUp() {
......@@ -530,7 +557,6 @@ class GraphView extends View {
}
}
state.mouseDownNode = null;
state.graphMouseDown = false;
}
svgKeyDown() {
......@@ -715,7 +741,7 @@ class GraphView extends View {
});
// select existing nodes
var filteredNodes = graph.nodes.filter(function (n) {
const filteredNodes = graph.nodes.filter(function (n) {
return n.visible;
});
let selNodes = graph.visibleNodes.selectAll("g").data(filteredNodes, function (d) {
......@@ -897,7 +923,6 @@ class GraphView extends View {
return edge.generatePath(graph);
});
graph.svg.style.height = '100%';
redetermineGraphBoundingBox(this);
}
......@@ -956,9 +981,9 @@ class GraphView extends View {
return translate;
}
translateClipped(translate, scale, transition) {
translateClipped(translate, scale, transition?: boolean): void{
var graph = this;
var graphNode = this.graphElement.node();
var graphNode = this.graphElement.node() as SVGElement;
var translate = this.getVisibleTranslation(translate, scale);
if (transition) {
graphNode.classList.add('visible-transition');
......
......@@ -5,6 +5,13 @@
"use strict";
class GraphMultiView extends View {
sourceResolver: SourceResolver;
selectionBroker: SelectionBroker;
graph: GraphView;
schedule: ScheduleView;
selectMenu: HTMLSelectElement;
currentPhaseView: View;
createViewElement() {
const pane = document.createElement('div');
pane.setAttribute('id', "multiview");
......@@ -23,12 +30,13 @@ class GraphMultiView extends View {
function handleSearch(e) {
if (this.currentPhaseView) {
this.currentPhaseView.searchInputAction(this.currentPhaseView, this)
this.currentPhaseView.searchInputAction(this.currentPhaseView, this, e)
}
}
d3.select("#search-input").on("keyup", handleSearch);
d3.select("#search-input").attr("value", window.sessionStorage.getItem("lastSearch") || "");
this.selectMenu = document.getElementById('display-selector');
const searchInput = document.getElementById("search-input");
searchInput.addEventListener("keyup", handleSearch);
searchInput.setAttribute("value", window.sessionStorage.getItem("lastSearch") || "");
this.selectMenu = (<HTMLSelectElement>document.getElementById('display-selector'));
}
initializeSelect() {
......@@ -39,8 +47,8 @@ class GraphMultiView extends View {
optionElement.text = phase.name;
view.selectMenu.add(optionElement);
});
view.selectMenu.onchange = function () {
window.sessionStorage.setItem("lastSelectedPhase", this.selectedIndex);
this.selectMenu.onchange = function (this:HTMLSelectElement) {
window.sessionStorage.setItem("lastSelectedPhase", this.selectedIndex.toString());
view.displayPhase(view.sourceResolver.getPhase(this.selectedIndex));
}
}
......@@ -67,7 +75,7 @@ class GraphMultiView extends View {
displayPhaseView(view, data) {
const rememberedSelection = this.hideCurrentPhase();
view.show(data, rememberedSelection);
d3.select("#middle").classed("scrollable", view.isScrollable());
document.getElementById("middle").classList.toggle("scrollable", view.isScrollable());
this.currentPhaseView = view;
}
......@@ -94,4 +102,8 @@ class GraphMultiView extends View {
deleteContent() {
this.hideCurrentPhase();
}
detachSelection() {
return null;
}
}
......@@ -2,30 +2,49 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var TYPE_HEIGHT = 25;
var DEFAULT_NODE_BUBBLE_RADIUS = 12;
var NODE_INPUT_WIDTH = 50;
var MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS;
var MINIMUM_NODE_OUTPUT_APPROACH = 15;
const TYPE_HEIGHT = 25;
const DEFAULT_NODE_BUBBLE_RADIUS = 12;
const NODE_INPUT_WIDTH = 50;
const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS;
const MINIMUM_NODE_OUTPUT_APPROACH = 15;
function isNodeInitiallyVisible(node) {
return node.cfg;
}
var Node = {
isControl: function() {
class GNode {
control: boolean;
opcode: string;
live: boolean;
inputs: Array<any>;
width: number;
properties: string;
title: string;
label: string;
origin: NodeOrigin;
outputs: Array<any>;
outputApproach: number;
type: string;
id: number;
x: number;
y: number;
visible: boolean;
rank: number;
opinfo: string;
isControl() {
return this.control;
},
isInput: function() {
}
isInput() {
return this.opcode == 'Parameter' || this.opcode.endsWith('Constant');
},
isLive: function() {
}
isLive() {
return this.live !== false;
},
isJavaScript: function() {
}
isJavaScript() {
return this.opcode.startsWith('JS');
},
isSimplified: function() {
}
isSimplified() {
if (this.isJavaScript()) return false;
return this.opcode.endsWith('Phi') ||
this.opcode.startsWith('Boolean') ||
......@@ -39,16 +58,16 @@ var Node = {
(this.opcode == 'AnyToBoolean') ||
(this.opcode.startsWith('Load') && this.opcode.length > 4) ||
(this.opcode.startsWith('Store') && this.opcode.length > 5);
},
isMachine: function() {
}
isMachine() {
return !(this.isControl() || this.isInput() ||
this.isJavaScript() || this.isSimplified());
},
getTotalNodeWidth: function() {
this.isJavaScript() || this.isSimplified());
}
getTotalNodeWidth() {
var inputWidth = this.inputs.length * NODE_INPUT_WIDTH;
return Math.max(inputWidth, this.width);
},
getTitle: function() {
}
getTitle() {
var propsString;
if (this.properties === undefined) {
propsString = "";
......@@ -62,29 +81,29 @@ var Node = {
title += `\nOrigin: #${this.origin.nodeId} in phase ${this.origin.phase}/${this.origin.reducer}`;
}
return title;
},
getDisplayLabel: function() {
}
getDisplayLabel() {
var result = this.id + ":" + this.label;
if (result.length > 40) {
return this.id + ":" + this.opcode;
} else {
} else {
return result;
}
},
getType: function() {
}
getType() {
return this.type;
},
getDisplayType: function() {
}
getDisplayType() {
var type_string = this.type;
if (type_string == undefined) return "";
if (type_string.length > 24) {
type_string = type_string.substr(0, 25) + "...";
}
return type_string;
},
deepestInputRank: function() {
}
deepestInputRank() {
var deepestRank = 0;
this.inputs.forEach(function(e) {
this.inputs.forEach(function (e) {
if (e.isVisible() && !e.isBackEdge()) {
if (e.source.rank > deepestRank) {
deepestRank = e.source.rank;
......@@ -92,17 +111,17 @@ var Node = {
}
});
return deepestRank;
},
areAnyOutputsVisible: function() {
}
areAnyOutputsVisible() {
var visibleCount = 0;
this.outputs.forEach(function(e) { if (e.isVisible()) ++visibleCount; });
this.outputs.forEach(function (e) { if (e.isVisible())++visibleCount; });
if (this.outputs.length == visibleCount) return 2;
if (visibleCount != 0) return 1;
return 0;
},
setOutputVisibility: function(v) {
}
setOutputVisibility(v) {
var result = false;
this.outputs.forEach(function(e) {
this.outputs.forEach(function (e) {
e.visible = v;
if (v) {
if (!e.target.visible) {
......@@ -112,8 +131,8 @@ var Node = {
}
});
return result;
},
setInputVisibility: function(i, v) {
}
setInputVisibility(i, v) {
var edge = this.inputs[i];
edge.visible = v;
if (v) {
......@@ -123,29 +142,26 @@ var Node = {
}
}
return false;
},
getInputApproach: function(index) {
}
getInputApproach(index) {
return this.y - MINIMUM_NODE_INPUT_APPROACH -
(index % 4) * MINIMUM_EDGE_SEPARATION - DEFAULT_NODE_BUBBLE_RADIUS
},
getOutputApproach: function(graph, index) {
}
getOutputApproach(graph) {
return this.y + this.outputApproach + graph.getNodeHeight(this) +
+ DEFAULT_NODE_BUBBLE_RADIUS;
},
getInputX: function(index) {
}
getInputX(index) {
var result = this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2) +
(index - this.inputs.length + 1) * NODE_INPUT_WIDTH;
(index - this.inputs.length + 1) * NODE_INPUT_WIDTH;
return result;
},
getOutputX: function() {
}
getOutputX() {
return this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2);
},
getFunctionRelativeSourcePosition: function(graph) {
return this.pos - graph.sourcePosition;
},
hasBackEdges: function() {
}
hasBackEdges() {
return (this.opcode == "Loop") ||
((this.opcode == "Phi" || this.opcode == "EffectPhi") &&
this.inputs[this.inputs.length - 1].source.opcode == "Loop");
this.inputs[this.inputs.length - 1].source.opcode == "Loop");
}
};
......@@ -5,6 +5,7 @@
"use strict";
class ScheduleView extends TextView {
schedule: Schedule;
createViewElement() {
const pane = document.createElement('div');
......@@ -41,7 +42,7 @@ class ScheduleView extends TextView {
elementForBlock(block) {
const view = this;
function createElement(tag, cls, content) {
function createElement(tag:string, cls: string|Array<string>, content?:string) {
const el = document.createElement(tag);
if (isIterable(cls)) {
for (const c of cls) el.classList.add(c);
......@@ -106,7 +107,7 @@ class ScheduleView extends TextView {
if (block.pred.length) schedule_block.appendChild(block_pred);
const nodes = createElement("div", "nodes");
for (const node of block.nodes) {
nodes.appendChild(createElementForNode(node, block.id));
nodes.appendChild(createElementForNode(node));
}
schedule_block.appendChild(nodes);
const block_succ = createElement("div", ["successor-list", "block-list", "comma-sep-list"]);
......@@ -144,8 +145,8 @@ class ScheduleView extends TextView {
return `${node.id}: ${node.label}(${node.inputs.join(", ")})`
}
searchInputAction(view, searchBar) {
d3.event.stopPropagation();
searchInputAction(view, searchBar, e) {
e.stopPropagation();
this.selectionHandler.clear();
const query = searchBar.value;
if (query.length == 0) return;
......
......@@ -3,6 +3,11 @@
// found in the LICENSE file.
class SelectionBroker {
sourceResolver: SourceResolver;
sourcePositionHandlers: Array<SelectionHandler>;
nodeHandlers: Array<NodeSelectionHandler>;
blockHandlers: Array<BlockSelectionHandler>;
constructor(sourceResolver) {
this.sourcePositionHandlers = [];
this.nodeHandlers = [];
......@@ -32,22 +37,22 @@ class SelectionBroker {
}
return true;
});
for (var b of this.sourcePositionHandlers) {
for (const b of this.sourcePositionHandlers) {
if (b != from) b.brokeredSourcePositionSelect(sourcePositions, selected);
}
const nodes = this.sourceResolver.sourcePositionsToNodeIds(sourcePositions);
for (var b of this.nodeHandlers) {
for (const b of this.nodeHandlers) {
if (b != from) b.brokeredNodeSelect(nodes, selected);
}
}
broadcastNodeSelect(from, nodes, selected) {
let broker = this;
for (var b of this.nodeHandlers) {
for (const b of this.nodeHandlers) {
if (b != from) b.brokeredNodeSelect(nodes, selected);
}
const sourcePositions = this.sourceResolver.nodeIdsToSourcePositions(nodes);
for (var b of this.sourcePositionHandlers) {
for (const b of this.sourcePositionHandlers) {
if (b != from) b.brokeredSourcePositionSelect(sourcePositions, selected);
}
}
......
// Copyright 2018 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.
interface SelectionHandler {
clear() : void;
select(nodeIds:any, selected:any) : void;
brokeredClear(): void;
brokeredSourcePositionSelect(sourcePositions:any, selected:any): void;
};
interface NodeSelectionHandler {
clear() : void;
select(nodeIds:any, selected:any) : void;
brokeredClear(): void;
brokeredNodeSelect(nodeIds:any, selected:any): void;
};
interface BlockSelectionHandler {
clear() : void;
select(nodeIds:any, selected:any) : void;
brokeredClear(): void;
brokeredBlockSelect(blockIds:any, selected:any): void;
};
......@@ -2,17 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
class Selection {
class MySelection {
selection: any;
stringKey: (o:any) => string;
constructor(stringKeyFnc) {
this.selection = new Map();
this.stringKey = stringKeyFnc;
}
isEmpty() {
isEmpty() : boolean {
return this.selection.size == 0;
}
clear() {
clear() : void {
this.selection = new Map();
}
......@@ -31,11 +34,11 @@ class Selection {
}
}
isSelected(i) {
isSelected(i): boolean {
return this.selection.has(this.stringKey(i));
}
isKeySelected(key) {
isKeySelected(key:string): boolean {
return this.selection.has(key);
}
......
......@@ -19,7 +19,43 @@ function sourcePositionToStringKey(sourcePosition) {
return "" + sourcePosition.inliningId + ":" + sourcePosition.scriptOffset;
}
interface SourcePosition {
scriptOffset: number;
inliningId: number;
}
interface Source {
sourcePositions: Array<SourcePosition>;
sourceName: string;
functionName: string;
sourceText: string;
sourceId: number;
startPosition?: number;
}
interface Inlining {
inliningPosition: SourcePosition;
sourceId: number;
}
interface Phase {
type: string;
name: string;
data: any;
}
interface Schedule {}
interface NodeOrigin {}
class SourceResolver {
nodePositionMap: Array<SourcePosition>;
sources: Array<Source>;
inlinings: Array<Inlining>;
inliningsMap: Map<string, Inlining>;
positionToNodes: Map<string, Array<string>>;
phases: Array<Phase>;
phaseNames: Map<string, number>;
disassemblyPhase: Phase;
constructor() {
// Maps node ids to source positions.
this.nodePositionMap = [];
......@@ -55,27 +91,27 @@ class SourceResolver {
setInlinings(inlinings) {
if (inlinings) {
for (const [inliningId, inlining] of Object.entries(inlinings)) {
for (const [inliningId, inlining] of Object.entries<Inlining>(inlinings)) {
this.inlinings[inliningId] = inlining;
this.inliningsMap.set(sourcePositionToStringKey(inlining.inliningPosition), inlining);
}
}
// This is a default entry for the script itself that helps
// keep other code more uniform.
this.inlinings[-1] = { sourceId: -1 };
this.inlinings[-1] = { sourceId: -1, inliningPosition: null };
}
setNodePositionMap(map) {
if (!map) return;
if (typeof map[0] != 'object') {
const alternativeMap = {};
for (const [nodeId, scriptOffset] of Object.entries(map)) {
for (const [nodeId, scriptOffset] of Object.entries<number>(map)) {
alternativeMap[nodeId] = { scriptOffset: scriptOffset, inliningId: -1 };
}
map = alternativeMap;
};
for (const [nodeId, sourcePosition] of Object.entries(map)) {
for (const [nodeId, sourcePosition] of Object.entries<SourcePosition>(map)) {
if (sourcePosition == undefined) {
console.log("Warning: undefined source position ", sourcePosition, " for nodeId ", nodeId);
}
......@@ -214,7 +250,7 @@ class SourceResolver {
}
parsePhases(phases) {
for (const [phaseId, phase] of Object.entries(phases)) {
for (const [phaseId, phase] of Object.entries<Phase>(phases)) {
if (phase.type == 'disassembly') {
this.disassemblyPhase = phase;
} else if (phase.type == 'schedule') {
......
......@@ -4,11 +4,26 @@
"use strict";
function anyToString(x) {
function anyToString(x:any): string {
return "" + x;
}
class TextView extends View {
abstract class TextView extends View {
selectionHandler: NodeSelectionHandler;
blockSelectionHandler: BlockSelectionHandler;
nodeSelectionHandler: NodeSelectionHandler;
sourcePositionSelectionHandler: SelectionHandler;
selection: MySelection;
blockSelection: MySelection;
sourcePositionSelection: MySelection;
textListNode: HTMLUListElement;
nodeIdToHtmlElementsMap: Map<string, Array<HTMLElement>>;
blockIdToHtmlElementsMap: Map<string, Array<HTMLElement>>;
sourcePositionToHtmlElementsMap: Map<string, Array<HTMLElement>>;
blockIdtoNodeIds: Map<string, Array<string>>;
nodeIdToBlockId: Array<string>;
patterns: any;
constructor(id, broker, patterns) {
super(id);
let view = this;
......@@ -19,9 +34,9 @@ class TextView extends View {
view.sourcePositionToHtmlElementsMap = new Map();
view.blockIdtoNodeIds = new Map();
view.nodeIdToBlockId = [];
view.selection = new Selection(anyToString);
view.blockSelection = new Selection(anyToString);
view.sourcePositionSelection = new Selection(sourcePositionToStringKey);
view.selection = new MySelection(anyToString);
view.blockSelection = new MySelection(anyToString);
view.sourcePositionSelection = new MySelection(sourcePositionToStringKey);
const selectionHandler = {
clear: function () {
view.selection.clear();
......@@ -103,7 +118,7 @@ class TextView extends View {
broker.addSourcePositionHandler(sourcePositionSelectionHandler);
}
addHtmlElementForNodeId(anyNodeId, htmlElement) {
addHtmlElementForNodeId(anyNodeId:any, htmlElement: HTMLElement) {
const nodeId = anyToString(anyNodeId);
if (!this.nodeIdToHtmlElementsMap.has(nodeId)) {
this.nodeIdToHtmlElementsMap.set(nodeId, []);
......@@ -146,9 +161,9 @@ class TextView extends View {
return blockIds;
}
updateSelection(scrollIntoView) {
updateSelection(scrollIntoView: boolean = false) {
if (this.divNode.parentNode == null) return;
const mkVisible = new ViewElements(this.divNode.parentNode);
const mkVisible = new ViewElements(this.divNode.parentNode as HTMLElement);
const view = this;
for (const [nodeId, elements] of this.nodeIdToHtmlElementsMap.entries()) {
const isSelected = view.selection.isSelected(nodeId);
......
......@@ -3,19 +3,24 @@
// found in the LICENSE file.
class Snapper {
constructor(resizer) {
let snapper = this;
resizer: Resizer;
sourceExpand: HTMLElement;
sourceCollapse: HTMLElement;
disassemblyExpand: HTMLElement;
disassemblyCollapse: HTMLElement;
constructor(resizer: Resizer) {
const snapper = this;
snapper.resizer = resizer;
snapper.sourceExpand = d3.select("#" + SOURCE_EXPAND_ID);
snapper.sourceCollapse = d3.select("#" + SOURCE_COLLAPSE_ID);
snapper.disassemblyExpand = d3.select("#" + DISASSEMBLY_EXPAND_ID);
snapper.disassemblyCollapse = d3.select("#" + DISASSEMBLY_COLLAPSE_ID);
snapper.sourceExpand = document.getElementById(SOURCE_EXPAND_ID);
snapper.sourceCollapse = document.getElementById(SOURCE_COLLAPSE_ID);
snapper.disassemblyExpand = document.getElementById(DISASSEMBLY_EXPAND_ID);
snapper.disassemblyCollapse = document.getElementById(DISASSEMBLY_COLLAPSE_ID);
d3.select("#source-collapse").on("click", function () {
document.getElementById("source-collapse").addEventListener("click", function () {
resizer.snapper.toggleSourceExpanded();
});
d3.select("#disassembly-collapse").on("click", function () {
document.getElementById("disassembly-collapse").addEventListener("click", function () {
resizer.snapper.toggleDisassemblyExpanded();
});
}
......@@ -30,18 +35,18 @@ class Snapper {
window.sessionStorage.setItem("expandedState-" + type, state);
}
toggleSourceExpanded() {
this.setSourceExpanded(!this.sourceExpand.classed("invisible"));
toggleSourceExpanded(): void {
this.setSourceExpanded(!this.sourceExpand.classList.contains("invisible"));
}
sourceExpandUpdate(newState) {
sourceExpandUpdate(newState: boolean) {
this.setLastExpandedState("source", newState);
this.sourceExpand.classed("invisible", newState);
this.sourceCollapse.classed("invisible", !newState);
this.sourceExpand.classList.toggle("invisible", newState);
this.sourceCollapse.classList.toggle("invisible", !newState);
}
setSourceExpanded(newState) {
if (this.sourceExpand.classed("invisible") === newState) return;
if (this.sourceExpand.classList.contains("invisible") === newState) return;
this.sourceExpandUpdate(newState);
let resizer = this.resizer;
if (newState) {
......@@ -55,17 +60,17 @@ class Snapper {
}
toggleDisassemblyExpanded() {
this.setDisassemblyExpanded(!this.disassemblyExpand.classed("invisible"));
this.setDisassemblyExpanded(!this.disassemblyExpand.classList.contains("invisible"));
}
disassemblyExpandUpdate(newState) {
this.setLastExpandedState("disassembly", newState);
this.disassemblyExpand.classed("invisible", newState);
this.disassemblyCollapse.classed("invisible", !newState);
this.disassemblyExpand.classList.toggle("invisible", newState);
this.disassemblyCollapse.classList.toggle("invisible", !newState);
}
setDisassemblyExpanded(newState) {
if (this.disassemblyExpand.classed("invisible") === newState) return;
if (this.disassemblyExpand.classList.contains("invisible") === newState) return;
this.disassemblyExpandUpdate(newState);
let resizer = this.resizer;
if (newState) {
......@@ -86,15 +91,30 @@ class Snapper {
}
class Resizer {
constructor(panes_updated_callback, dead_width) {
snapper: Snapper;
dead_width: number;
client_width: number;
left: HTMLElement;
right: HTMLElement;
middle: HTMLElement;
sep_left: number;
sep_right: number;
sep_left_snap: number;
sep_right_snap: number;
sep_width_offset: number;
panes_updated_callback: () => void;
resizer_right: d3.Selection<any>;
resizer_left: d3.Selection<any>;
constructor(panes_updated_callback: () => void, dead_width: number) {
let resizer = this;
resizer.snapper = new Snapper(resizer)
resizer.panes_updated_callback = panes_updated_callback;
resizer.dead_width = dead_width
resizer.client_width = d3.select("body").node().getBoundingClientRect().width;
resizer.left = d3.select("#" + SOURCE_PANE_ID);
resizer.middle = d3.select("#" + INTERMEDIATE_PANE_ID);
resizer.right = d3.select("#" + GENERATED_PANE_ID);
resizer.client_width = document.body.getBoundingClientRect().width;
resizer.left = document.getElementById(SOURCE_PANE_ID);
resizer.middle = document.getElementById(INTERMEDIATE_PANE_ID);
resizer.right = document.getElementById(GENERATED_PANE_ID);
resizer.resizer_left = d3.select('.resizer-left');
resizer.resizer_right = d3.select('.resizer-right');
resizer.sep_left = resizer.client_width / 3;
......@@ -150,9 +170,9 @@ class Resizer {
let right_snapped = this.sep_right >= this.client_width - 1;
this.resizer_left.classed("snapped", left_snapped);
this.resizer_right.classed("snapped", right_snapped);
this.left.style('width', this.sep_left + 'px');
this.middle.style('width', (this.sep_right - this.sep_left) + 'px');
this.right.style('width', (this.client_width - this.sep_right) + 'px');
this.left.style.width = this.sep_left + 'px';
this.middle.style.width = (this.sep_right - this.sep_left) + 'px';
this.right.style.width = (this.client_width - this.sep_right) + 'px';
this.resizer_left.style('left', this.sep_left + 'px');
this.resizer_right.style('right', (this.client_width - this.sep_right - 1) + 'px');
......@@ -161,13 +181,13 @@ class Resizer {
}
updateWidths() {
this.client_width = d3.select("body").node().getBoundingClientRect().width;
this.client_width = document.body.getBoundingClientRect().width;
this.sep_right = Math.min(this.sep_right, this.client_width);
this.sep_left = Math.min(Math.max(0, this.sep_left), this.sep_right);
}
}
document.onload = (function (d3) {
window.onload = function () {
"use strict";
var svg = null;
var multiview = null;
......@@ -215,12 +235,12 @@ document.onload = (function (d3) {
sourceResolver.setNodePositionMap(jsonObj.nodePositions);
sourceResolver.parsePhases(jsonObj.phases);
let sourceView = new CodeView(SOURCE_PANE_ID, selectionBroker, sourceResolver, fnc, CodeView.MAIN_SOURCE);
let sourceView = new CodeView(SOURCE_PANE_ID, selectionBroker, sourceResolver, fnc, CodeMode.MAIN_SOURCE);
sourceView.show(null, null);
sourceViews.push(sourceView);
sourceResolver.forEachSource((source) => {
let sourceView = new CodeView(SOURCE_PANE_ID, selectionBroker, sourceResolver, source, CodeView.INLINED_SOURCE);
let sourceView = new CodeView(SOURCE_PANE_ID, selectionBroker, sourceResolver, source, CodeMode.INLINED_SOURCE);
sourceView.show(null, null);
sourceViews.push(sourceView);
});
......@@ -236,7 +256,7 @@ document.onload = (function (d3) {
multiview.show(jsonObj);
} catch (err) {
if (window.confirm("Error: Exception during load of TurboFan JSON file:\n" +
"error: " + err.message + "\nDo you want to clear session storage?")) {
"error: " + err.message + "\nDo you want to clear session storage?")) {
window.sessionStorage.clear();
}
return;
......@@ -250,18 +270,14 @@ document.onload = (function (d3) {
d3.select("#upload").on("click",
() => document.getElementById("upload-helper").click());
d3.select("#upload-helper").on("change", function () {
if (window.File && window.FileReader && window.FileList) {
var uploadFile = this.files && this.files[0];
var filereader = new window.FileReader();
filereader.onload = function (e) {
var txtRes = e.target.result;
loadFile(txtRes);
};
if (uploadFile)
filereader.readAsText(uploadFile);
} else {
alert("Can't load graph");
}
var uploadFile = this.files && this.files[0];
var filereader = new FileReader();
filereader.onload = function (e) {
var txtRes = e.target.result;
loadFile(txtRes);
};
if (uploadFile)
filereader.readAsText(uploadFile);
});
}
......@@ -273,4 +289,4 @@ document.onload = (function (d3) {
resizer.updatePanes();
})(window.d3);
};
......@@ -18,7 +18,10 @@ function computeScrollTop(container, element) {
}
class ViewElements {
constructor(container) {
container: HTMLElement;
scrollTop: number;
constructor(container: HTMLElement) {
this.container = container;
this.scrollTop = undefined;
}
......@@ -81,12 +84,12 @@ function upperBound(a, value, compare, lookup) {
}
function sortUnique(arr, f, equal) {
function sortUnique<T>(arr: Array<T>, f: (a: T, b: T) => number, equal: (a: T, b: T) => boolean) {
if (arr.length == 0) return arr;
arr = arr.sort(f);
let ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (!equal(arr[i-1], arr[i])) {
if (!equal(arr[i - 1], arr[i])) {
ret.push(arr[i]);
}
}
......@@ -94,15 +97,14 @@ function sortUnique(arr, f, equal) {
}
// Partial application without binding the receiver
function partial(f) {
var arguments1 = Array.prototype.slice.call(arguments, 1);
return function() {
function partial(f, ...arguments1) {
return function (...arguments2) {
var arguments2 = Array.from(arguments);
f.apply(this, arguments1.concat(arguments2));
f.apply(this, [...arguments1, ...arguments2]);
}
}
function isIterable(obj) {
function isIterable(obj:any): obj is Iterable<any> {
return obj != null && obj != undefined
&& typeof obj != 'string' && typeof obj[Symbol.iterator] === 'function';
}
......@@ -4,28 +4,30 @@
"use strict";
class View {
abstract class View {
container: HTMLElement;
divNode: HTMLElement;
abstract initializeContent(data: any, rememberedSelection: Selection): void;
abstract createViewElement(): HTMLElement;
abstract deleteContent(): void;
abstract detachSelection(): Set<string>;
constructor(id) {
this.container = document.getElementById(id);
this.divNode = this.createViewElement();
this.divElement = d3.select(this.divNode);
}
isScrollable() {
isScrollable(): boolean {
return false;
}
show(data, rememberedSelection) {
this.container.appendChild(this.divElement.node());
show(data, rememberedSelection): void {
this.container.appendChild(this.divNode);
this.initializeContent(data, rememberedSelection);
}
hide() {
hide(): void {
this.deleteContent();
this.container.removeChild(this.divNode);
}
detachSelection() {
return null;
}
}
{
"compilerOptions": {
"outFile": "build/turbolizer.js",
"allowJs": true,
"target": "es2017"
},
"files": [
"src/monkey.ts",
"src/util.ts",
"src/lang-disassembly.ts",
"src/node.ts",
"src/edge.ts",
"src/source-resolver.ts",
"src/selection.ts",
"src/selection-broker.ts",
"src/selection-handler.ts",
"src/constants.ts",
"src/view.ts",
"src/text-view.ts",
"src/code-view.ts",
"src/graph-layout.ts",
"src/graph-view.ts",
"src/schedule-view.ts",
"src/disassembly-view.ts",
"src/graphmultiview.ts",
"src/turbo-visualizer.ts"
]
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment