410 lines
14 KiB
JavaScript
410 lines
14 KiB
JavaScript
|
// Copyright 2013 the V8 project authors. All rights reserved.
|
||
|
// Redistribution and use in source and binary forms, with or without
|
||
|
// modification, are permitted provided that the following conditions are
|
||
|
// met:
|
||
|
//
|
||
|
// * Redistributions of source code must retain the above copyright
|
||
|
// notice, this list of conditions and the following disclaimer.
|
||
|
// * Redistributions in binary form must reproduce the above
|
||
|
// copyright notice, this list of conditions and the following
|
||
|
// disclaimer in the documentation and/or other materials provided
|
||
|
// with the distribution.
|
||
|
// * Neither the name of Google Inc. nor the names of its
|
||
|
// contributors may be used to endorse or promote products derived
|
||
|
// from this software without specific prior written permission.
|
||
|
//
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
var Sodium = (function() {
|
||
|
"use strict";
|
||
|
|
||
|
var kinds = ["FUNCTION", "OPTIMIZED_FUNCTION", "STUB", "BUILTIN",
|
||
|
"LOAD_IC", "KEYED_LOAD_IC", "CALL_IC", "KEYED_CALL_IC",
|
||
|
"STORE_IC", "KEYED_STORE_IC", "BINARY_OP_IC", "COMPARE_IC",
|
||
|
"COMPARE_NIL_IC", "TO_BOOLEAN_IC"];
|
||
|
var kindsWithSource = {
|
||
|
'FUNCTION': true,
|
||
|
'OPTIMIZED_FUNCTION': true
|
||
|
};
|
||
|
|
||
|
var addressRegEx = "0x[0-9a-f]{8,16}";
|
||
|
var nameFinder = new RegExp("^name = (.+)$");
|
||
|
var kindFinder = new RegExp("^kind = (.+)$");
|
||
|
var firstPositionFinder = new RegExp("^source_position = (\\d+)$");
|
||
|
var separatorFilter = new RegExp("^--- (.)+ ---$");
|
||
|
var rawSourceFilter = new RegExp("^--- Raw source ---$");
|
||
|
var codeEndFinder = new RegExp("^--- End code ---$");
|
||
|
var whiteSpaceLineFinder = new RegExp("^\\W*$");
|
||
|
var instructionBeginFinder =
|
||
|
new RegExp("^Instructions\\W+\\(size = \\d+\\)");
|
||
|
var instructionFinder =
|
||
|
new RegExp("^\(" + addressRegEx + "\)\(\\W+\\d+\\W+.+\)");
|
||
|
var positionFinder =
|
||
|
new RegExp("^(" + addressRegEx + ")\\W+position\\W+\\((\\d+)\\)");
|
||
|
var addressFinder = new RegExp("\(" + addressRegEx + "\)");
|
||
|
var addressReplacer = new RegExp("\(" + addressRegEx + "\)", "gi");
|
||
|
|
||
|
var fileContent = "";
|
||
|
var selectedFunctionKind = "";
|
||
|
var currentFunctionKind = "";
|
||
|
|
||
|
var currentFunctionName = "";
|
||
|
var firstSourcePosition = 0;
|
||
|
var startAddress = "";
|
||
|
var readingSource = false;
|
||
|
var readingAsm = false;
|
||
|
var sourceBegin = -1;
|
||
|
var sourceEnd = -1;
|
||
|
var asmBegin = -1;
|
||
|
var asmEnd = -1;
|
||
|
var codeObjects = [];
|
||
|
var selectedAsm = null;
|
||
|
var selectedSource = null;
|
||
|
var selectedSourceClass = "";
|
||
|
|
||
|
function Code(name, kind, sourceBegin, sourceEnd, asmBegin, asmEnd,
|
||
|
firstSourcePosition, startAddress) {
|
||
|
this.name = name;
|
||
|
this.kind = kind;
|
||
|
this.sourceBegin = sourceBegin;
|
||
|
this.sourceEnd = sourceEnd;
|
||
|
this.asmBegin = asmBegin;
|
||
|
this.asmEnd = asmEnd;
|
||
|
this.firstSourcePosition = firstSourcePosition;
|
||
|
this.startAddress = startAddress;
|
||
|
}
|
||
|
|
||
|
function getCurrentCodeObject() {
|
||
|
var functionSelect = document.getElementById('function-selector-id');
|
||
|
return functionSelect.options[functionSelect.selectedIndex].codeObject;
|
||
|
}
|
||
|
|
||
|
function getCurrentSourceText() {
|
||
|
var code = getCurrentCodeObject();
|
||
|
if (code.sourceBegin == -1 || code.sourceEnd == -1) return "";
|
||
|
return fileContent.substring(code.sourceBegin, code.sourceEnd);
|
||
|
}
|
||
|
|
||
|
function getCurrentAsmText() {
|
||
|
var code = getCurrentCodeObject();
|
||
|
if (code.asmBegin == -1 || code.asmEnd == -1) return "";
|
||
|
return fileContent.substring(code.asmBegin, code.asmEnd);
|
||
|
}
|
||
|
|
||
|
function setKindByIndex(index) {
|
||
|
selectedFunctionKind = kinds[index];
|
||
|
}
|
||
|
|
||
|
function processLine(text, begin, end) {
|
||
|
var line = text.substring(begin, end);
|
||
|
if (readingSource) {
|
||
|
if (separatorFilter.exec(line) != null) {
|
||
|
readingSource = false;
|
||
|
} else {
|
||
|
if (sourceBegin == -1) {
|
||
|
sourceBegin = begin;
|
||
|
}
|
||
|
sourceEnd = end;
|
||
|
}
|
||
|
} else {
|
||
|
if (readingAsm) {
|
||
|
if (codeEndFinder.exec(line) != null) {
|
||
|
readingAsm = false;
|
||
|
asmEnd = begin;
|
||
|
var newCode =
|
||
|
new Code(currentFunctionName, currentFunctionKind,
|
||
|
sourceBegin, sourceEnd, asmBegin, asmEnd,
|
||
|
firstSourcePosition, startAddress);
|
||
|
codeObjects.push(newCode);
|
||
|
currentFunctionKind = null;
|
||
|
} else {
|
||
|
if (asmBegin == -1) {
|
||
|
matches = instructionBeginFinder.exec(line);
|
||
|
if (matches != null) {
|
||
|
asmBegin = begin;
|
||
|
}
|
||
|
}
|
||
|
if (startAddress == "") {
|
||
|
matches = instructionFinder.exec(line);
|
||
|
if (matches != null) {
|
||
|
startAddress = matches[1];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
var matches = kindFinder.exec(line);
|
||
|
if (matches != null) {
|
||
|
currentFunctionKind = matches[1];
|
||
|
if (!kindsWithSource[currentFunctionKind]) {
|
||
|
sourceBegin = -1;
|
||
|
sourceEnd = -1;
|
||
|
}
|
||
|
} else if (currentFunctionKind != null) {
|
||
|
matches = nameFinder.exec(line);
|
||
|
if (matches != null) {
|
||
|
readingAsm = true;
|
||
|
asmBegin = -1;
|
||
|
currentFunctionName = matches[1];
|
||
|
}
|
||
|
} else if (rawSourceFilter.exec(line) != null) {
|
||
|
readingSource = true;
|
||
|
sourceBegin = -1;
|
||
|
} else {
|
||
|
var matches = firstPositionFinder.exec(line);
|
||
|
if (matches != null) {
|
||
|
firstSourcePosition = parseInt(matches[1]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function processLines(source, size, processLine) {
|
||
|
var firstChar = 0;
|
||
|
for (var x = 0; x < size; x++) {
|
||
|
var curChar = source[x];
|
||
|
if (curChar == '\n' || curChar == '\r') {
|
||
|
processLine(source, firstChar, x);
|
||
|
firstChar = x + 1;
|
||
|
}
|
||
|
}
|
||
|
if (firstChar != size - 1) {
|
||
|
processLine(source, firstChar, size - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function processFileContent() {
|
||
|
document.getElementById('source-text-pre').innerHTML = '';
|
||
|
sourceBegin = -1;
|
||
|
codeObjects = [];
|
||
|
processLines(fileContent, fileContent.length, processLine);
|
||
|
var functionSelectElement = document.getElementById('function-selector-id');
|
||
|
functionSelectElement.innerHTML = '';
|
||
|
var length = codeObjects.length;
|
||
|
for (var i = 0; i < codeObjects.length; ++i) {
|
||
|
var code = codeObjects[i];
|
||
|
if (code.kind == selectedFunctionKind) {
|
||
|
var optionElement = document.createElement("option");
|
||
|
optionElement.codeObject = code;
|
||
|
optionElement.text = code.name;
|
||
|
functionSelectElement.add(optionElement, null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function asmClick(element) {
|
||
|
if (element == selectedAsm) return;
|
||
|
if (selectedAsm != null) {
|
||
|
selectedAsm.classList.remove('highlight-yellow');
|
||
|
}
|
||
|
selectedAsm = element;
|
||
|
selectedAsm.classList.add('highlight-yellow');
|
||
|
|
||
|
var pc = element.firstChild.innerText;
|
||
|
var sourceLine = null;
|
||
|
if (addressFinder.exec(pc) != null) {
|
||
|
var position = findSourcePosition(pc);
|
||
|
var line = findSourceLine(position);
|
||
|
sourceLine = document.getElementById('source-line-' + line);
|
||
|
var sourceLineTop = sourceLine.offsetTop;
|
||
|
makeSourcePosVisible(sourceLineTop);
|
||
|
}
|
||
|
if (selectedSource == sourceLine) return;
|
||
|
if (selectedSource != null) {
|
||
|
selectedSource.classList.remove('highlight-yellow');
|
||
|
selectedSource.classList.add(selectedSourceClass);
|
||
|
}
|
||
|
if (sourceLine != null) {
|
||
|
selectedSourceClass = sourceLine.classList[0];
|
||
|
sourceLine.classList.remove(selectedSourceClass);
|
||
|
sourceLine.classList.add('highlight-yellow');
|
||
|
}
|
||
|
selectedSource = sourceLine;
|
||
|
}
|
||
|
|
||
|
function makeContainerPosVisible(container, newTop) {
|
||
|
var height = container.offsetHeight;
|
||
|
var margin = Math.floor(height / 4);
|
||
|
if (newTop < container.scrollTop + margin) {
|
||
|
newTop -= margin;
|
||
|
if (newTop < 0) newTop = 0;
|
||
|
container.scrollTop = newTop;
|
||
|
return;
|
||
|
}
|
||
|
if (newTop > (container.scrollTop + 3 * margin)) {
|
||
|
newTop = newTop - 3 * margin;
|
||
|
container.scrollTop = newTop;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function makeAsmPosVisible(newTop) {
|
||
|
var asmContainer = document.getElementById('asm-container');
|
||
|
makeContainerPosVisible(asmContainer, newTop);
|
||
|
}
|
||
|
|
||
|
function makeSourcePosVisible(newTop) {
|
||
|
var sourceContainer = document.getElementById('source-container');
|
||
|
makeContainerPosVisible(sourceContainer, newTop);
|
||
|
}
|
||
|
|
||
|
function addressClick(element, event) {
|
||
|
event.stopPropagation();
|
||
|
var asmLineId = 'address-' + element.innerText;
|
||
|
var asmLineElement = document.getElementById(asmLineId);
|
||
|
if (asmLineElement != null) {
|
||
|
var asmLineTop = asmLineElement.parentNode.offsetTop;
|
||
|
makeAsmPosVisible(asmLineTop);
|
||
|
asmLineElement.classList.add('highlight-flash-blue');
|
||
|
window.setTimeout(function() {
|
||
|
asmLineElement.classList.remove('highlight-flash-blue');
|
||
|
}, 1500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function prepareAsm(originalSource) {
|
||
|
var newSource = "";
|
||
|
var lineNumber = 1;
|
||
|
var functionProcessLine = function(text, begin, end) {
|
||
|
var currentLine = text.substring(begin, end);
|
||
|
var matches = instructionFinder.exec(currentLine);
|
||
|
var clickHandler = "";
|
||
|
if (matches != null) {
|
||
|
var restOfLine = matches[2];
|
||
|
restOfLine = restOfLine.replace(
|
||
|
addressReplacer,
|
||
|
'<span class="hover-underline" ' +
|
||
|
'onclick="Sodium.addressClick(this, event);">\$1</span>');
|
||
|
currentLine = '<span id="address-' + matches[1] + '" >' +
|
||
|
matches[1] + '</span>' + restOfLine;
|
||
|
clickHandler = 'onclick=\'Sodium.asmClick(this)\' ';
|
||
|
} else if (whiteSpaceLineFinder.exec(currentLine)) {
|
||
|
currentLine = "<br>";
|
||
|
}
|
||
|
newSource += '<pre style=\'margin-bottom: -12px;\' ' + clickHandler + '>' +
|
||
|
currentLine + '</pre>';
|
||
|
lineNumber++;
|
||
|
}
|
||
|
processLines(originalSource, originalSource.length, functionProcessLine);
|
||
|
return newSource;
|
||
|
}
|
||
|
|
||
|
function findSourcePosition(pcToSearch) {
|
||
|
var position = 0;
|
||
|
var distance = 0x7FFFFFFF;
|
||
|
var pcToSearchOffset = parseInt(pcToSearch);
|
||
|
var processOneLine = function(text, begin, end) {
|
||
|
var currentLine = text.substring(begin, end);
|
||
|
var matches = positionFinder.exec(currentLine);
|
||
|
if (matches != null) {
|
||
|
var pcOffset = parseInt(matches[1]);
|
||
|
if (pcOffset <= pcToSearchOffset) {
|
||
|
var dist = pcToSearchOffset - pcOffset;
|
||
|
var pos = parseInt(matches[2]);
|
||
|
if ((dist < distance) || (dist == distance && pos > position)) {
|
||
|
position = pos;
|
||
|
distance = dist;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
var asmText = getCurrentAsmText();
|
||
|
processLines(asmText, asmText.length, processOneLine);
|
||
|
var code = getCurrentCodeObject();
|
||
|
if (position == 0) return 0;
|
||
|
return position - code.firstSourcePosition;
|
||
|
}
|
||
|
|
||
|
function findSourceLine(position) {
|
||
|
if (position == 0) return 1;
|
||
|
var line = 0;
|
||
|
var processOneLine = function(text, begin, end) {
|
||
|
if (begin < position) {
|
||
|
line++;
|
||
|
}
|
||
|
}
|
||
|
var sourceText = getCurrentSourceText();
|
||
|
processLines(sourceText, sourceText.length, processOneLine);
|
||
|
return line;
|
||
|
}
|
||
|
|
||
|
function functionChangedHandler() {
|
||
|
var functionSelect = document.getElementById('function-selector-id');
|
||
|
var source = getCurrentSourceText();
|
||
|
var sourceDivElement = document.getElementById('source-text');
|
||
|
var code = getCurrentCodeObject();
|
||
|
var newHtml = "<pre class=\"prettyprint linenums\" id=\"source-text\">"
|
||
|
+ 'function ' + code.name + source + "</pre>";
|
||
|
sourceDivElement.innerHTML = newHtml;
|
||
|
try {
|
||
|
// Wrap in try to work when offline.
|
||
|
PR.prettyPrint();
|
||
|
} catch (e) {
|
||
|
}
|
||
|
var sourceLineContainer = sourceDivElement.firstChild.firstChild;
|
||
|
var lineCount = sourceLineContainer.childElementCount;
|
||
|
var current = sourceLineContainer.firstChild;
|
||
|
for (var i = 1; i < lineCount; ++i) {
|
||
|
current.id = "source-line-" + i;
|
||
|
current = current.nextElementSibling;
|
||
|
}
|
||
|
|
||
|
var asm = getCurrentAsmText();
|
||
|
document.getElementById('asm-text').innerHTML = prepareAsm(asm);
|
||
|
}
|
||
|
|
||
|
function kindChangedHandler(element) {
|
||
|
setKindByIndex(element.selectedIndex);
|
||
|
processFileContent();
|
||
|
functionChangedHandler();
|
||
|
}
|
||
|
|
||
|
function readLog(evt) {
|
||
|
//Retrieve the first (and only!) File from the FileList object
|
||
|
var f = evt.target.files[0];
|
||
|
if (f) {
|
||
|
var r = new FileReader();
|
||
|
r.onload = function(e) {
|
||
|
var file = evt.target.files[0];
|
||
|
currentFunctionKind = "";
|
||
|
fileContent = e.target.result;
|
||
|
processFileContent();
|
||
|
functionChangedHandler();
|
||
|
}
|
||
|
r.readAsText(f);
|
||
|
} else {
|
||
|
alert("Failed to load file");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function buildFunctionKindSelector(kindSelectElement) {
|
||
|
for (var x = 0; x < kinds.length; ++x) {
|
||
|
var optionElement = document.createElement("option");
|
||
|
optionElement.value = x;
|
||
|
optionElement.text = kinds[x];
|
||
|
kindSelectElement.add(optionElement, null);
|
||
|
}
|
||
|
kindSelectElement.selectedIndex = 1;
|
||
|
setKindByIndex(1);
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
buildFunctionKindSelector: buildFunctionKindSelector,
|
||
|
kindChangedHandler: kindChangedHandler,
|
||
|
functionChangedHandler: functionChangedHandler,
|
||
|
asmClick: asmClick,
|
||
|
addressClick: addressClick,
|
||
|
readLog: readLog
|
||
|
};
|
||
|
|
||
|
})();
|