// 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,
'\$1');
currentLine = '' +
matches[1] + '' + restOfLine;
clickHandler = 'onclick=\'Sodium.asmClick(this)\' ';
} else if (whiteSpaceLineFinder.exec(currentLine)) {
currentLine = "
";
}
newSource += '
' + currentLine + ''; 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 = "
" + 'function ' + code.name + source + ""; 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 }; })();