Commit d3905561 authored by danno's avatar danno Committed by Commit bot

[turbolizer] Use locations rather than ranges everywhere

Also convert CodeView to a class and fix a host of selection
bugs in the process, as well as move the logic and data to
"enrich" location with one type of location data with location
data known globally to the whole graph in the selection broker.

Review-Url: https://codereview.chromium.org/2230083004
Cr-Commit-Position: refs/heads/master@{#38544}
parent 63516a8c
......@@ -2,177 +2,171 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var CodeView = function(divID, PR, sourceText, sourcePosition, broker) {
"use strict";
var view = this;
"use strict";
view.divElement = document.getElementById(divID);
view.broker = broker;
view.codeSelection = null;
view.allSpans = [];
class CodeView extends View {
constructor(divID, PR, sourceText, sourcePosition, broker) {
super(divID, broker, null, false);
let view = this;
view.PR = PR;
view.mouseDown = false;
view.broker = broker;
view.allSpans = [];
var selectionHandler = {
clear: function() {
broker.clear(selectionHandler);
},
select: function(items, selected) {
var handler = this;
var divElement = view.divElement;
var broker = view.broker;
for (let span of items) {
if (selected) {
span.classList.add("selected");
} else {
span.classList.remove("selected");
}
}
var ranges = [];
for (var span of items) {
ranges.push([span.start, span.end, null]);
}
broker.clear(selectionHandler);
broker.select(selectionHandler, ranges, selected);
},
selectionDifference: function(span1, inclusive1, span2, inclusive2) {
var pos1 = span1.start;
var pos2 = span2.start;
var result = [];
var lineListDiv = view.divElement.firstChild.firstChild.childNodes;
for (var i=0; i < lineListDiv.length; i++) {
var currentLineElement = lineListDiv[i];
var spans = currentLineElement.childNodes;
for (var j=0; j < spans.length; ++j) {
var currentSpan = spans[j];
if (currentSpan.start > pos1 || (inclusive1 && currentSpan.start == pos1)) {
if (currentSpan.start < pos2 || (inclusive2 && currentSpan.start == pos2)) {
result.push(currentSpan);
}
var selectionHandler = {
clear: function() { broker.clear(selectionHandler); },
select: function(items, selected) {
var handler = this;
var broker = view.broker;
for (let span of items) {
if (selected) {
span.classList.add("selected");
} else {
span.classList.remove("selected");
}
}
}
return result;
},
brokeredSelect: function(ranges, selected) {
var firstSelect = view.codeSelection.isEmpty();
for (var range of ranges) {
var start = range[0];
var end = range[1];
var lower = 0;
var upper = view.allSpans.length;
if (upper > 0) {
while ((upper - lower) > 1) {
var middle = Math.floor((upper + lower) / 2);
var lineStart = view.allSpans[middle].start;
if (lineStart < start) {
lower = middle;
} else if (lineStart > start) {
upper = middle;
} else {
lower = middle;
break;
var locations = [];
for (var span of items) {
locations.push({pos_start: span.start, pos_end: span.end});
}
broker.clear(selectionHandler);
broker.select(selectionHandler, locations, selected);
},
selectionDifference: function(span1, inclusive1, span2, inclusive2) {
var pos1 = span1.start;
var pos2 = span2.start;
var result = [];
var lineListDiv = view.divNode.firstChild.firstChild.childNodes;
for (var i = 0; i < lineListDiv.length; i++) {
var currentLineElement = lineListDiv[i];
var spans = currentLineElement.childNodes;
for (var j = 0; j < spans.length; ++j) {
var currentSpan = spans[j];
if (currentSpan.start > pos1 ||
(inclusive1 && currentSpan.start == pos1)) {
if (currentSpan.start < pos2 ||
(inclusive2 && currentSpan.start == pos2)) {
result.push(currentSpan);
}
}
}
var currentSpan = view.allSpans[lower];
var currentLineElement = currentSpan.parentNode;
if ((currentSpan.start <= start && start < currentSpan.end) ||
(currentSpan.start <= end && end < currentSpan.end)) {
if (firstSelect) {
makeContainerPosVisible(view.divElement, currentLineElement.offsetTop);
firstSelect = false;
}
return result;
},
brokeredSelect: function(locations, selected) {
let firstSelect = view.selection.isEmpty();
for (let location of locations) {
let start = location.pos_start;
let end = location.pos_end;
if (start && end) {
let lower = 0;
let upper = view.allSpans.length;
if (upper > 0) {
while ((upper - lower) > 1) {
var middle = Math.floor((upper + lower) / 2);
var lineStart = view.allSpans[middle].start;
if (lineStart < start) {
lower = middle;
} else if (lineStart > start) {
upper = middle;
} else {
lower = middle;
break;
}
}
var currentSpan = view.allSpans[lower];
var currentLineElement = currentSpan.parentNode;
if ((currentSpan.start <= start && start < currentSpan.end) ||
(currentSpan.start <= end && end < currentSpan.end)) {
if (firstSelect) {
makeContainerPosVisible(
view.divNode, currentLineElement.offsetTop);
firstSelect = false;
}
view.selection.select(currentSpan, selected);
}
}
view.codeSelection.select(currentSpan, selected);
}
}
}
},
brokeredClear: function() {
view.codeSelection.clear();
},
};
view.codeSelection = new Selection(selectionHandler);
broker.addSelectionHandler(selectionHandler);
},
brokeredClear: function() { view.selection.clear(); },
};
view.selection = new Selection(selectionHandler);
broker.addSelectionHandler(selectionHandler);
var mouseDown = false;
this.handleSpanMouseDown = function(e) {
e.stopPropagation();
if (!e.shiftKey) {
view.codeSelection.clear();
view.handleSpanMouseDown = function(e) {
e.stopPropagation();
if (!e.shiftKey) {
view.selection.clear();
}
view.selection.select(this, true);
view.mouseDown = true;
}
view.codeSelection.select(this, true);
mouseDown = true;
}
this.handleSpanMouseMove = function(e) {
if (mouseDown) {
view.codeSelection.extendTo(this);
view.handleSpanMouseMove = function(e) {
if (view.mouseDown) {
view.selection.extendTo(this);
}
}
}
this.handleCodeMouseDown = function(e) {
view.codeSelection.clear();
}
view.handleCodeMouseDown = function(e) { view.selection.clear(); }
document.addEventListener('mouseup', function(e){
mouseDown = false;
}, false);
document.addEventListener('mouseup', function(e) {
view.mouseDown = false;
}, false);
this.initializeCode(sourceText, sourcePosition);
}
view.initializeCode(sourceText, sourcePosition);
}
CodeView.prototype.initializeCode = function(sourceText, sourcePosition) {
var view = this;
if (sourceText == "") {
var newHtml = "<pre class=\"prettyprint\"</pre>";
view.divElement.innerHTML = newHtml;
} else {
var newHtml = "<pre class=\"prettyprint linenums\">"
+ sourceText + "</pre>";
view.divElement.innerHTML = newHtml;
try {
// Wrap in try to work when offline.
PR.prettyPrint();
} catch (e) {
}
initializeContent(data, rememberedSelection) { this.data = data; }
initializeCode(sourceText, sourcePosition) {
var view = this;
if (sourceText == "") {
var newHtml = "<pre class=\"prettyprint\"</pre>";
view.divNode.innerHTML = newHtml;
} else {
var newHtml =
"<pre class=\"prettyprint linenums\">" + sourceText + "</pre>";
view.divNode.innerHTML = newHtml;
try {
// Wrap in try to work when offline.
view.PR.prettyPrint();
} catch (e) {
}
view.divElement.onmousedown = this.handleCodeMouseDown;
view.divNode.onmousedown = this.handleCodeMouseDown;
var base = sourcePosition;
var current = 0;
var lineListDiv = view.divElement.firstChild.firstChild.childNodes;
for (i=0; i < lineListDiv.length; i++) {
var currentLineElement = lineListDiv[i];
currentLineElement.id = "li" + i;
var pos = base + current;
currentLineElement.pos = pos;
var spans = currentLineElement.childNodes;
for (j=0; j < spans.length; ++j) {
var currentSpan = spans[j];
if (currentSpan.nodeType == 1) {
currentSpan.start = pos;
currentSpan.end = pos + currentSpan.textContent.length;
currentSpan.onmousedown = this.handleSpanMouseDown;
currentSpan.onmousemove = this.handleSpanMouseMove;
view.allSpans.push(currentSpan);
var base = sourcePosition;
var current = 0;
var lineListDiv = view.divNode.firstChild.firstChild.childNodes;
for (let i = 0; i < lineListDiv.length; i++) {
var currentLineElement = lineListDiv[i];
currentLineElement.id = "li" + i;
var pos = base + current;
currentLineElement.pos = pos;
var spans = currentLineElement.childNodes;
for (let j = 0; j < spans.length; ++j) {
var currentSpan = spans[j];
if (currentSpan.nodeType == 1) {
currentSpan.start = pos;
currentSpan.end = pos + currentSpan.textContent.length;
currentSpan.onmousedown = this.handleSpanMouseDown;
currentSpan.onmousemove = this.handleSpanMouseMove;
view.allSpans.push(currentSpan);
}
current += currentSpan.textContent.length;
pos = base + current;
}
while ((current < sourceText.length) &&
(sourceText[current] == '\n' || sourceText[current] == '\r')) {
++current;
}
current += currentSpan.textContent.length;
pos = base + current;
}
while ((current < sourceText.length) && (
sourceText[current] == '\n' ||
sourceText[current] == '\r')) {
++current;
}
}
}
this.resizeToParent();
}
view.resizeToParent();
}
CodeView.prototype.resizeToParent = function() {
var view = this;
var documentElement = document.documentElement;
var y = view.divElement.parentNode.clientHeight || documentElement.clientHeight;
view.divElement.style.height = y + "px";
deleteContent() {}
}
......@@ -5,7 +5,7 @@
"use strict";
class DisassemblyView extends TextView {
constructor(id, broker, sortedPositionList) {
constructor(id, broker) {
super(id, broker, null, false);
let view = this;
......@@ -40,9 +40,16 @@ class DisassemblyView extends TextView {
let OPCODE_STYLE = {
css: 'kwd',
location: function(text) {
return {
address: ADDRESS_STYLE.last_address
};
if (BLOCK_HEADER_STYLE.block_id != undefined) {
return {
address: ADDRESS_STYLE.last_address,
block_id: BLOCK_HEADER_STYLE.block_id
};
} else {
return {
address: ADDRESS_STYLE.last_address
};
}
}
};
const BLOCK_HEADER_STYLE = {
......
......@@ -34,7 +34,7 @@ class GraphView extends View {
broker.clear(selectionHandler);
},
select: function(items, selected) {
var ranges = [];
var locations = [];
for (var d of items) {
if (selected) {
d.classList.add("selected");
......@@ -42,22 +42,22 @@ class GraphView extends View {
d.classList.remove("selected");
}
var data = d.__data__;
ranges.push([data.pos, data.pos + 1, data.id]);
locations.push({ pos_start: data.pos, pos_end: data.pos + 1, node_id: data.id});
}
broker.select(selectionHandler, ranges, selected);
broker.select(selectionHandler, locations, selected);
},
selectionDifference: function(span1, inclusive1, span2, inclusive2) {
// Should not be called
},
brokeredSelect: function(ranges, selected) {
brokeredSelect: function(locations, selected) {
var test = [].entries().next();
var selection = graph.nodes
.filter(function(n) {
var pos = n.pos;
for (var range of ranges) {
var start = range[0];
var end = range[1];
var id = range[2];
for (var location of locations) {
var start = location.pos_start;
var end = location.pos_end;
var id = location.node_id;
if (end != undefined) {
if (pos >= start && pos < end) {
return true;
......@@ -240,6 +240,7 @@ class GraphView extends View {
if (rememberedSelection != null) {
this.attachSelection(rememberedSelection);
this.connectVisibleSelectedNodes();
this.viewSelection();
}
this.updateGraphVisibility();
}
......
......@@ -5,7 +5,7 @@
"use strict";
class ScheduleView extends TextView {
constructor(id, broker, nodePositionMap) {
constructor(id, broker) {
super(id, broker, null, false);
let view = this;
let BLOCK_STYLE = {
......@@ -103,7 +103,6 @@ class ScheduleView extends TextView {
]
];
this.setPatterns(patterns);
this.setNodePositionMap(nodePositionMap);
}
initializeContent(data, rememberedSelection) {
......@@ -113,14 +112,16 @@ class ScheduleView extends TextView {
for (var id of rememberedSelection) {
locations.push({ node_id : id });
}
this.selectLocations(locations, true, false);
this.selectLocations(locations, true, true);
}
detachSelection() {
var selection = this.selection.detachSelection();
var s = new Set();
for (var i of selection) {
s.add(i.location.node_id);
if (i.location.node_id != undefined && i.location.node_id > 0) {
s.add(i.location.node_id);
}
};
return s;
}
......
......@@ -6,25 +6,78 @@ var SelectionBroker = function() {
this.brokers = [];
this.dispatching = false;
this.lastDispatchingHandler = null;
this.nodePositionMap = [];
this.sortedPositionList = [];
this.positionNodeMap = [];
};
SelectionBroker.prototype.addSelectionHandler = function(handler) {
this.brokers.push(handler);
}
SelectionBroker.prototype.select = function(from, ranges, selected) {
if (!this.dispatching) {
this.lastDispatchingHandler = from;
SelectionBroker.prototype.setNodePositionMap = function(map) {
let broker = this;
if (!map) return;
broker.nodePositionMap = map;
broker.positionNodeMap = [];
broker.sortedPositionList = [];
let next = 0;
for (let i in broker.nodePositionMap) {
broker.sortedPositionList[next] = Number(broker.nodePositionMap[i]);
broker.positionNodeMap[next++] = i;
}
broker.sortedPositionList = sortUnique(broker.sortedPositionList,
function(a,b) { return a - b; });
this.positionNodeMap.sort(function(a,b) {
let result = broker.nodePositionMap[a] - broker.nodePositionMap[b];
if (result != 0) return result;
return a - b;
});
}
SelectionBroker.prototype.select = function(from, locations, selected) {
let broker = this;
if (!broker.dispatching) {
broker.lastDispatchingHandler = from;
try {
this.dispatching = true;
broker.dispatching = true;
let enrichLocations = function(locations) {
result = [];
for (let location of locations) {
let newLocation = {};
if (location.pos_start != undefined) {
newLocation.pos_start = location.pos_start;
}
if (location.pos_end != undefined) {
newLocation.pos_end = location.pos_end;
}
if (location.node_id != undefined) {
newLocation.node_id = location.node_id;
}
if (location.block_id != undefined) {
newLocation.block_id = location.block_id;
}
if (newLocation.pos_start == undefined &&
newLocation.pos_end == undefined &&
newLocation.node_id != undefined) {
if (broker.nodePositionMap && broker.nodePositionMap[location.node_id]) {
newLocation.pos_start = broker.nodePositionMap[location.node_id];
newLocation.pos_end = location.pos_start + 1;
}
}
result.push(newLocation);
}
return result;
}
locations = enrichLocations(locations);
for (var b of this.brokers) {
if (b != from) {
b.brokeredSelect(ranges, selected);
b.brokeredSelect(locations, selected);
}
}
}
finally {
this.dispatching = false;
broker.dispatching = false;
}
}
}
......
......@@ -40,6 +40,7 @@ Selection.prototype.select = function(s, isSelected) {
}
this.selection.add(i);
}
handler.select(this.selection, true);
} else {
let unselectSet = new Set();
for (let i of s) {
......@@ -48,8 +49,8 @@ Selection.prototype.select = function(s, isSelected) {
this.selection.delete(i);
}
}
handler.select(unselectSet, false);
}
handler.select(this.selection, isSelected);
}
......
......@@ -8,9 +8,7 @@ class TextView extends View {
constructor(id, broker, patterns, allowSpanSelection) {
super(id, broker);
let view = this;
view.sortedPositionList = [];
view.nodePositionMap = [];
view.positionNodeMap = [];
view.hide();
view.textListNode = view.divNode.getElementsByTagName('ul')[0];
view.fillerSvgElement = view.divElement.append("svg").attr('version','1.1').attr("width", "0");
view.patterns = patterns;
......@@ -29,13 +27,12 @@ class TextView extends View {
}
}
broker.clear(selectionHandler);
broker.select(selectionHandler, view.getRanges(items), selected);
broker.select(selectionHandler, view.getLocations(items), selected);
},
selectionDifference: function(span1, inclusive1, span2, inclusive2) {
return null;
},
brokeredSelect: function(ranges, selected) {
let locations = view.rangesToLocations(ranges);
brokeredSelect: function(locations, selected) {
view.selectLocations(locations, selected, true);
},
brokeredClear: function() {
......@@ -58,36 +55,6 @@ class TextView extends View {
}
}
rangeToLocation(range) {
return range;
}
rangesToLocations(ranges) {
let view = this;
let nodes = new Set();
let result = [];
for (let range of ranges) {
let start = range[0];
let end = range[1];
let block_id = range[3];
let location = { pos_start: start, pos_end: end, block_id: block_id };
if (range[2] !== null && range[2] != -1) {
location.node_id = range[2];
if (range[0] == -1 && range[1] == -1) {
location.pos_start = view.nodePositionMap[location.node_id];
location.pos_end = location.pos_start + 1;
}
} else {
if (range[0] != undefined) {
location.pos_start = range[0];
location.pos_end = range[1];
}
}
result.push(location);
}
return result;
}
sameLocation(l1, l2) {
let view = this;
if (l1.block_id != undefined && l2.block_id != undefined &&
......@@ -102,7 +69,7 @@ class TextView extends View {
let node1 = l1.node_id;
let node2 = l2.node_id;
if (node1 === undefined && node2 == undefined) {
if (node1 === undefined || node2 == undefined) {
if (l1.pos_start === undefined || l2.pos_start == undefined) {
return false;
}
......@@ -116,56 +83,9 @@ class TextView extends View {
}
}
if (node1 === undefined) {
let lower = lowerBound(view.positionNodeMap, l1.pos_start, undefined, function(a, b) {
var node = a[b];
return view.nodePositionMap[node];
} );
while (++lower < view.positionNodeMap.length &&
view.nodePositionMap[view.positionNodeMap[lower]] < l1.pos_end) {
if (view.positionNodeMap[lower] == node2) {
return true;
}
}
return false;
}
if (node2 === undefined) {
let lower = lowerBound(view.positionNodeMap, l2.pos_start, undefined, function(a, b) {
var node = a[b];
return view.nodePositionMap[node];
} );
while (++lower < view.positionNodeMap.length &&
view.nodePositionMap[view.positionNodeMap[lower]] < l2.pos_end) {
if (view.positionNodeMap[lower] == node1) {
return true;
}
}
return false;
}
return l1.node_id == l2.node_id;
}
setNodePositionMap(map) {
let view = this;
view.nodePositionMap = map;
view.positionNodeMap = [];
view.sortedPositionList = [];
let next = 0;
for (let i in view.nodePositionMap) {
view.sortedPositionList[next] = Number(view.nodePositionMap[i]);
view.positionNodeMap[next++] = i;
}
view.sortedPositionList = sortUnique(view.sortedPositionList,
function(a,b) { return a - b; });
this.positionNodeMap.sort(function(a,b) {
let result = view.nodePositionMap[a] - view.nodePositionMap[b];
if (result != 0) return result;
return a - b;
});
}
selectLocations(locations, selected, makeVisible) {
let view = this;
let s = new Set();
......@@ -180,39 +100,12 @@ class TextView extends View {
view.selectCommon(s, selected, makeVisible);
}
getRanges(items) {
getLocations(items) {
let result = [];
let lastObject = null;
for (let i of items) {
if (i.location) {
let location = i.location;
let start = -1;
let end = -1;
let node_id = -1;
let block_id = -1;
if (location.node_id !== undefined) {
node_id = location.node_id;
}
if (location.block_id !== undefined) {
block_id = location.block_id;
}
if (location.pos_start !== undefined) {
start = location.pos_start;
end = location.pos_end;
} else {
if (this.nodePositionMap && this.nodePositionMap[node_id]) {
start = this.nodePositionMap[node_id];
end = start + 1;
}
}
if (lastObject == null ||
(lastObject[2] != node_id ||
lastObject[0] != start ||
lastObject[1] != end ||
lastObject[3] != block_id)) {
lastObject = [start, end, node_id, block_id];
result.push(lastObject);
}
result.push(i.location);
}
}
return result;
......@@ -304,13 +197,13 @@ class TextView extends View {
}
}
} else if (typeof s[Symbol.iterator] === 'function') {
for (let i of s) {
if (firstSelect) {
if (firstSelect) {
for (let i of s) {
makeContainerPosVisible(view.parentNode, i.offsetTop);
firstSelect = false;
break;
}
view.selection.select(i, selected);
}
view.selection.select(s, selected);
} else {
if (firstSelect) {
makeContainerPosVisible(view.parentNode, s.offsetTop);
......
......@@ -170,9 +170,10 @@ document.onload = (function(d3){
hideCurrentPhase();
selectionBroker.setNodePositionMap(jsonObj.nodePositions);
sourceView.initializeCode(jsonObj.source, jsonObj.sourcePosition);
disassemblyView.initializeCode(jsonObj.source, jsonObj.sourcePosition);
schedule.setNodePositionMap(jsonObj.nodePositions);
disassemblyView.initializeCode(jsonObj.source);
var selectMenu = document.getElementById('display-selector');
var disassemblyPhase = null;
......@@ -195,7 +196,6 @@ document.onload = (function(d3){
eventMenu.add(optionElement, null);
}
disassemblyView.initializePerfProfile(jsonObj.eventCounts);
disassemblyView.setNodePositionMap(jsonObj.nodePositions);
disassemblyView.show(disassemblyPhase.data, null);
var initialPhaseIndex = +window.sessionStorage.getItem("lastSelectedPhase");
......
......@@ -9,7 +9,6 @@ class View {
this.divElement = d3.select("#" + id);
this.divNode = this.divElement[0][0];
this.parentNode = this.divNode.parentNode;
this.hide();
}
isScrollable() {
......
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