OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are |
| 4 // met: |
| 5 // |
| 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided |
| 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. |
| 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 |
| 28 var Sodium = (function() { |
| 29 "use strict"; |
| 30 |
| 31 var kinds = ["FUNCTION", "OPTIMIZED_FUNCTION", "STUB", "BUILTIN", |
| 32 "LOAD_IC", "KEYED_LOAD_IC", "CALL_IC", "KEYED_CALL_IC", |
| 33 "STORE_IC", "KEYED_STORE_IC", "BINARY_OP_IC", "COMPARE_IC", |
| 34 "COMPARE_NIL_IC", "TO_BOOLEAN_IC"]; |
| 35 var kindsWithSource = { |
| 36 'FUNCTION': true, |
| 37 'OPTIMIZED_FUNCTION': true |
| 38 }; |
| 39 |
| 40 var addressRegEx = "0x[0-9a-f]{8,16}"; |
| 41 var nameFinder = new RegExp("^name = (.+)$"); |
| 42 var kindFinder = new RegExp("^kind = (.+)$"); |
| 43 var firstPositionFinder = new RegExp("^source_position = (\\d+)$"); |
| 44 var separatorFilter = new RegExp("^--- (.)+ ---$"); |
| 45 var rawSourceFilter = new RegExp("^--- Raw source ---$"); |
| 46 var codeEndFinder = new RegExp("^--- End code ---$"); |
| 47 var whiteSpaceLineFinder = new RegExp("^\\W*$"); |
| 48 var instructionBeginFinder = |
| 49 new RegExp("^Instructions\\W+\\(size = \\d+\\)"); |
| 50 var instructionFinder = |
| 51 new RegExp("^\(" + addressRegEx + "\)\(\\W+\\d+\\W+.+\)"); |
| 52 var positionFinder = |
| 53 new RegExp("^(" + addressRegEx + ")\\W+position\\W+\\((\\d+)\\)"); |
| 54 var addressFinder = new RegExp("\(" + addressRegEx + "\)"); |
| 55 var addressReplacer = new RegExp("\(" + addressRegEx + "\)", "gi"); |
| 56 |
| 57 var fileContent = ""; |
| 58 var selectedFunctionKind = ""; |
| 59 var currentFunctionKind = ""; |
| 60 |
| 61 var currentFunctionName = ""; |
| 62 var firstSourcePosition = 0; |
| 63 var startAddress = ""; |
| 64 var readingSource = false; |
| 65 var readingAsm = false; |
| 66 var sourceBegin = -1; |
| 67 var sourceEnd = -1; |
| 68 var asmBegin = -1; |
| 69 var asmEnd = -1; |
| 70 var codeObjects = []; |
| 71 var selectedAsm = null; |
| 72 var selectedSource = null; |
| 73 var selectedSourceClass = ""; |
| 74 |
| 75 function Code(name, kind, sourceBegin, sourceEnd, asmBegin, asmEnd, |
| 76 firstSourcePosition, startAddress) { |
| 77 this.name = name; |
| 78 this.kind = kind; |
| 79 this.sourceBegin = sourceBegin; |
| 80 this.sourceEnd = sourceEnd; |
| 81 this.asmBegin = asmBegin; |
| 82 this.asmEnd = asmEnd; |
| 83 this.firstSourcePosition = firstSourcePosition; |
| 84 this.startAddress = startAddress; |
| 85 } |
| 86 |
| 87 function getCurrentCodeObject() { |
| 88 var functionSelect = document.getElementById('function-selector-id'); |
| 89 return functionSelect.options[functionSelect.selectedIndex].codeObject; |
| 90 } |
| 91 |
| 92 function getCurrentSourceText() { |
| 93 var code = getCurrentCodeObject(); |
| 94 if (code.sourceBegin == -1 || code.sourceEnd == -1) return ""; |
| 95 return fileContent.substring(code.sourceBegin, code.sourceEnd); |
| 96 } |
| 97 |
| 98 function getCurrentAsmText() { |
| 99 var code = getCurrentCodeObject(); |
| 100 if (code.asmBegin == -1 || code.asmEnd == -1) return ""; |
| 101 return fileContent.substring(code.asmBegin, code.asmEnd); |
| 102 } |
| 103 |
| 104 function setKindByIndex(index) { |
| 105 selectedFunctionKind = kinds[index]; |
| 106 } |
| 107 |
| 108 function processLine(text, begin, end) { |
| 109 var line = text.substring(begin, end); |
| 110 if (readingSource) { |
| 111 if (separatorFilter.exec(line) != null) { |
| 112 readingSource = false; |
| 113 } else { |
| 114 if (sourceBegin == -1) { |
| 115 sourceBegin = begin; |
| 116 } |
| 117 sourceEnd = end; |
| 118 } |
| 119 } else { |
| 120 if (readingAsm) { |
| 121 if (codeEndFinder.exec(line) != null) { |
| 122 readingAsm = false; |
| 123 asmEnd = begin; |
| 124 var newCode = |
| 125 new Code(currentFunctionName, currentFunctionKind, |
| 126 sourceBegin, sourceEnd, asmBegin, asmEnd, |
| 127 firstSourcePosition, startAddress); |
| 128 codeObjects.push(newCode); |
| 129 currentFunctionKind = null; |
| 130 } else { |
| 131 if (asmBegin == -1) { |
| 132 matches = instructionBeginFinder.exec(line); |
| 133 if (matches != null) { |
| 134 asmBegin = begin; |
| 135 } |
| 136 } |
| 137 if (startAddress == "") { |
| 138 matches = instructionFinder.exec(line); |
| 139 if (matches != null) { |
| 140 startAddress = matches[1]; |
| 141 } |
| 142 } |
| 143 } |
| 144 } else { |
| 145 var matches = kindFinder.exec(line); |
| 146 if (matches != null) { |
| 147 currentFunctionKind = matches[1]; |
| 148 if (!kindsWithSource[currentFunctionKind]) { |
| 149 sourceBegin = -1; |
| 150 sourceEnd = -1; |
| 151 } |
| 152 } else if (currentFunctionKind != null) { |
| 153 matches = nameFinder.exec(line); |
| 154 if (matches != null) { |
| 155 readingAsm = true; |
| 156 asmBegin = -1; |
| 157 currentFunctionName = matches[1]; |
| 158 } |
| 159 } else if (rawSourceFilter.exec(line) != null) { |
| 160 readingSource = true; |
| 161 sourceBegin = -1; |
| 162 } else { |
| 163 var matches = firstPositionFinder.exec(line); |
| 164 if (matches != null) { |
| 165 firstSourcePosition = parseInt(matches[1]); |
| 166 } |
| 167 } |
| 168 } |
| 169 } |
| 170 } |
| 171 |
| 172 function processLines(source, size, processLine) { |
| 173 var firstChar = 0; |
| 174 for (var x = 0; x < size; x++) { |
| 175 var curChar = source[x]; |
| 176 if (curChar == '\n' || curChar == '\r') { |
| 177 processLine(source, firstChar, x); |
| 178 firstChar = x + 1; |
| 179 } |
| 180 } |
| 181 if (firstChar != size - 1) { |
| 182 processLine(source, firstChar, size - 1); |
| 183 } |
| 184 } |
| 185 |
| 186 function processFileContent() { |
| 187 document.getElementById('source-text-pre').innerHTML = ''; |
| 188 sourceBegin = -1; |
| 189 codeObjects = []; |
| 190 processLines(fileContent, fileContent.length, processLine); |
| 191 var functionSelectElement = document.getElementById('function-selector-id'); |
| 192 functionSelectElement.innerHTML = ''; |
| 193 var length = codeObjects.length; |
| 194 for (var i = 0; i < codeObjects.length; ++i) { |
| 195 var code = codeObjects[i]; |
| 196 if (code.kind == selectedFunctionKind) { |
| 197 var optionElement = document.createElement("option"); |
| 198 optionElement.codeObject = code; |
| 199 optionElement.text = code.name; |
| 200 functionSelectElement.add(optionElement, null); |
| 201 } |
| 202 } |
| 203 } |
| 204 |
| 205 function asmClick(element) { |
| 206 if (element == selectedAsm) return; |
| 207 if (selectedAsm != null) { |
| 208 selectedAsm.classList.remove('highlight-yellow'); |
| 209 } |
| 210 selectedAsm = element; |
| 211 selectedAsm.classList.add('highlight-yellow'); |
| 212 |
| 213 var pc = element.firstChild.innerText; |
| 214 var sourceLine = null; |
| 215 if (addressFinder.exec(pc) != null) { |
| 216 var position = findSourcePosition(pc); |
| 217 var line = findSourceLine(position); |
| 218 sourceLine = document.getElementById('source-line-' + line); |
| 219 var sourceLineTop = sourceLine.offsetTop; |
| 220 makeSourcePosVisible(sourceLineTop); |
| 221 } |
| 222 if (selectedSource == sourceLine) return; |
| 223 if (selectedSource != null) { |
| 224 selectedSource.classList.remove('highlight-yellow'); |
| 225 selectedSource.classList.add(selectedSourceClass); |
| 226 } |
| 227 if (sourceLine != null) { |
| 228 selectedSourceClass = sourceLine.classList[0]; |
| 229 sourceLine.classList.remove(selectedSourceClass); |
| 230 sourceLine.classList.add('highlight-yellow'); |
| 231 } |
| 232 selectedSource = sourceLine; |
| 233 } |
| 234 |
| 235 function makeContainerPosVisible(container, newTop) { |
| 236 var height = container.offsetHeight; |
| 237 var margin = Math.floor(height / 4); |
| 238 if (newTop < container.scrollTop + margin) { |
| 239 newTop -= margin; |
| 240 if (newTop < 0) newTop = 0; |
| 241 container.scrollTop = newTop; |
| 242 return; |
| 243 } |
| 244 if (newTop > (container.scrollTop + 3 * margin)) { |
| 245 newTop = newTop - 3 * margin; |
| 246 container.scrollTop = newTop; |
| 247 } |
| 248 } |
| 249 |
| 250 function makeAsmPosVisible(newTop) { |
| 251 var asmContainer = document.getElementById('asm-container'); |
| 252 makeContainerPosVisible(asmContainer, newTop); |
| 253 } |
| 254 |
| 255 function makeSourcePosVisible(newTop) { |
| 256 var sourceContainer = document.getElementById('source-container'); |
| 257 makeContainerPosVisible(sourceContainer, newTop); |
| 258 } |
| 259 |
| 260 function addressClick(element, event) { |
| 261 event.stopPropagation(); |
| 262 var asmLineId = 'address-' + element.innerText; |
| 263 var asmLineElement = document.getElementById(asmLineId); |
| 264 if (asmLineElement != null) { |
| 265 var asmLineTop = asmLineElement.parentNode.offsetTop; |
| 266 makeAsmPosVisible(asmLineTop); |
| 267 asmLineElement.classList.add('highlight-flash-blue'); |
| 268 window.setTimeout(function() { |
| 269 asmLineElement.classList.remove('highlight-flash-blue'); |
| 270 }, 1500); |
| 271 } |
| 272 } |
| 273 |
| 274 function prepareAsm(originalSource) { |
| 275 var newSource = ""; |
| 276 var lineNumber = 1; |
| 277 var functionProcessLine = function(text, begin, end) { |
| 278 var currentLine = text.substring(begin, end); |
| 279 var matches = instructionFinder.exec(currentLine); |
| 280 var clickHandler = ""; |
| 281 if (matches != null) { |
| 282 var restOfLine = matches[2]; |
| 283 restOfLine = restOfLine.replace( |
| 284 addressReplacer, |
| 285 '<span class="hover-underline" ' + |
| 286 'onclick="Sodium.addressClick(this, event);">\$1</span>'); |
| 287 currentLine = '<span id="address-' + matches[1] + '" >' + |
| 288 matches[1] + '</span>' + restOfLine; |
| 289 clickHandler = 'onclick=\'Sodium.asmClick(this)\' '; |
| 290 } else if (whiteSpaceLineFinder.exec(currentLine)) { |
| 291 currentLine = "<br>"; |
| 292 } |
| 293 newSource += '<pre style=\'margin-bottom: -12px;\' ' + clickHandler + '>'
+ |
| 294 currentLine + '</pre>'; |
| 295 lineNumber++; |
| 296 } |
| 297 processLines(originalSource, originalSource.length, functionProcessLine); |
| 298 return newSource; |
| 299 } |
| 300 |
| 301 function findSourcePosition(pcToSearch) { |
| 302 var position = 0; |
| 303 var distance = 0x7FFFFFFF; |
| 304 var pcToSearchOffset = parseInt(pcToSearch); |
| 305 var processOneLine = function(text, begin, end) { |
| 306 var currentLine = text.substring(begin, end); |
| 307 var matches = positionFinder.exec(currentLine); |
| 308 if (matches != null) { |
| 309 var pcOffset = parseInt(matches[1]); |
| 310 if (pcOffset <= pcToSearchOffset) { |
| 311 var dist = pcToSearchOffset - pcOffset; |
| 312 var pos = parseInt(matches[2]); |
| 313 if ((dist < distance) || (dist == distance && pos > position)) { |
| 314 position = pos; |
| 315 distance = dist; |
| 316 } |
| 317 } |
| 318 } |
| 319 } |
| 320 var asmText = getCurrentAsmText(); |
| 321 processLines(asmText, asmText.length, processOneLine); |
| 322 var code = getCurrentCodeObject(); |
| 323 if (position == 0) return 0; |
| 324 return position - code.firstSourcePosition; |
| 325 } |
| 326 |
| 327 function findSourceLine(position) { |
| 328 if (position == 0) return 1; |
| 329 var line = 0; |
| 330 var processOneLine = function(text, begin, end) { |
| 331 if (begin < position) { |
| 332 line++; |
| 333 } |
| 334 } |
| 335 var sourceText = getCurrentSourceText(); |
| 336 processLines(sourceText, sourceText.length, processOneLine); |
| 337 return line; |
| 338 } |
| 339 |
| 340 function functionChangedHandler() { |
| 341 var functionSelect = document.getElementById('function-selector-id'); |
| 342 var source = getCurrentSourceText(); |
| 343 var sourceDivElement = document.getElementById('source-text'); |
| 344 var code = getCurrentCodeObject(); |
| 345 var newHtml = "<pre class=\"prettyprint linenums\" id=\"source-text\">" |
| 346 + 'function ' + code.name + source + "</pre>"; |
| 347 sourceDivElement.innerHTML = newHtml; |
| 348 try { |
| 349 // Wrap in try to work when offline. |
| 350 PR.prettyPrint(); |
| 351 } catch (e) { |
| 352 } |
| 353 var sourceLineContainer = sourceDivElement.firstChild.firstChild; |
| 354 var lineCount = sourceLineContainer.childElementCount; |
| 355 var current = sourceLineContainer.firstChild; |
| 356 for (var i = 1; i < lineCount; ++i) { |
| 357 current.id = "source-line-" + i; |
| 358 current = current.nextElementSibling; |
| 359 } |
| 360 |
| 361 var asm = getCurrentAsmText(); |
| 362 document.getElementById('asm-text').innerHTML = prepareAsm(asm); |
| 363 } |
| 364 |
| 365 function kindChangedHandler(element) { |
| 366 setKindByIndex(element.selectedIndex); |
| 367 processFileContent(); |
| 368 functionChangedHandler(); |
| 369 } |
| 370 |
| 371 function readLog(evt) { |
| 372 //Retrieve the first (and only!) File from the FileList object |
| 373 var f = evt.target.files[0]; |
| 374 if (f) { |
| 375 var r = new FileReader(); |
| 376 r.onload = function(e) { |
| 377 var file = evt.target.files[0]; |
| 378 currentFunctionKind = ""; |
| 379 fileContent = e.target.result; |
| 380 processFileContent(); |
| 381 functionChangedHandler(); |
| 382 } |
| 383 r.readAsText(f); |
| 384 } else { |
| 385 alert("Failed to load file"); |
| 386 } |
| 387 } |
| 388 |
| 389 function buildFunctionKindSelector(kindSelectElement) { |
| 390 for (var x = 0; x < kinds.length; ++x) { |
| 391 var optionElement = document.createElement("option"); |
| 392 optionElement.value = x; |
| 393 optionElement.text = kinds[x]; |
| 394 kindSelectElement.add(optionElement, null); |
| 395 } |
| 396 kindSelectElement.selectedIndex = 1; |
| 397 setKindByIndex(1); |
| 398 } |
| 399 |
| 400 return { |
| 401 buildFunctionKindSelector: buildFunctionKindSelector, |
| 402 kindChangedHandler: kindChangedHandler, |
| 403 functionChangedHandler: functionChangedHandler, |
| 404 asmClick: asmClick, |
| 405 addressClick: addressClick, |
| 406 readLog: readLog |
| 407 }; |
| 408 |
| 409 })(); |
OLD | NEW |