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