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 var sourceDivElement = document.getElementById('source-text'); | |
188 sourceDivElement.innerHTML = "<pre/>"; | |
189 sourceBegin = -1; | |
190 codeObjects = []; | |
191 processLines(fileContent, fileContent.length, processLine); | |
192 var functionSelectElement = document.getElementById('function-selector-id'); | |
193 functionSelectElement.innerHTML = | |
194 "<select id=\"function-selector-id\"></select>"; | |
195 var length = codeObjects.length; | |
196 for (var i = 0; i < codeObjects.length; ++i) { | |
197 var code = codeObjects[i]; | |
198 if (code.kind == selectedFunctionKind) { | |
199 var optionElement = document.createElement("option"); | |
200 optionElement.codeObject = code; | |
201 optionElement.text = code.name; | |
202 functionSelectElement.add(optionElement, null); | |
203 } | |
204 } | |
205 } | |
206 | |
207 function asmClick(element) { | |
208 if (element == selectedAsm) return; | |
209 if (selectedAsm != null) { | |
210 selectedAsm.classList.remove('highlight-yellow'); | |
211 } | |
212 selectedAsm = element; | |
213 selectedAsm.classList.add('highlight-yellow'); | |
214 | |
215 var pc = element.firstChild.innerText; | |
216 var sourceLine = null; | |
217 if (addressFinder.exec(pc) != null) { | |
218 var position = findSourcePosition(pc); | |
219 var line = findSourceLine(position); | |
220 sourceLine = document.getElementById('source-line-' + line); | |
221 var sourceLineTop = sourceLine.offsetTop; | |
222 makeSourcePosVisible(sourceLineTop); | |
223 } | |
224 if (selectedSource == sourceLine) return; | |
225 if (selectedSource != null) { | |
226 selectedSource.classList.remove('highlight-yellow'); | |
227 selectedSource.classList.add(selectedSourceClass); | |
228 } | |
229 if (sourceLine != null) { | |
230 selectedSourceClass = sourceLine.classList[0]; | |
231 sourceLine.classList.remove(selectedSourceClass); | |
232 sourceLine.classList.add('highlight-yellow'); | |
233 } | |
234 selectedSource = sourceLine; | |
235 } | |
236 | |
237 function makeContainerPosVisible(container, newTop) { | |
238 var height = container.offsetHeight; | |
239 var margin = Math.floor(height / 4); | |
240 if (newTop < container.scrollTop + margin) { | |
241 newTop -= margin; | |
242 if (newTop < 0) newTop = 0; | |
243 container.scrollTop = newTop; | |
244 return; | |
245 } | |
246 if (newTop > (container.scrollTop + 3 * margin)) { | |
247 newTop = newTop - 3 * margin; | |
248 container.scrollTop = newTop; | |
249 } | |
250 } | |
251 | |
252 function makeAsmPosVisible(newTop) { | |
253 var asmContainer = document.getElementById('asm-container'); | |
254 makeContainerPosVisible(asmContainer, newTop); | |
255 } | |
256 | |
257 function makeSourcePosVisible(newTop) { | |
258 var sourceContainer = document.getElementById('source-container'); | |
259 makeContainerPosVisible(sourceContainer, newTop); | |
260 } | |
261 | |
262 function addressClick(element, event) { | |
263 event.stopPropagation(); | |
264 var asmLineId = 'address-' + element.innerText; | |
265 var asmLineElement = document.getElementById(asmLineId); | |
266 if (asmLineElement != null) { | |
267 var asmLineTop = asmLineElement.parentNode.offsetTop; | |
268 makeAsmPosVisible(asmLineTop); | |
269 asmLineElement.classList.add('highlight-flash-blue'); | |
270 window.setTimeout(function() { | |
271 asmLineElement.classList.remove('highlight-flash-blue'); | |
272 }, 1500); | |
273 } | |
274 } | |
275 | |
276 function prepareAsm(originalSource) { | |
277 var newSource = ""; | |
278 var lineNumber = 1; | |
279 var functionProcessLine = function(text, begin, end) { | |
280 var currentLine = text.substring(begin, end); | |
281 var matches = instructionFinder.exec(currentLine); | |
282 var clickHandler = ""; | |
283 if (matches != null) { | |
284 var restOfLine = matches[2]; | |
285 restOfLine = restOfLine.replace( | |
286 addressReplacer, | |
287 '<span class="hover-underline" ' + | |
288 'onclick="Sodium.addressClick(this, event);">\$1</span>'); | |
289 currentLine = '<span id="address-' + matches[1] + '" >' + | |
290 matches[1] + '</span>' + restOfLine; | |
291 clickHandler = 'onclick=\'Sodium.asmClick(this)\' '; | |
292 } else if (whiteSpaceLineFinder.exec(currentLine)) { | |
293 currentLine = "<br>"; | |
294 } | |
295 newSource += '<pre style=\'margin-bottom: -12px;\' ' + clickHandler + '>' + | |
296 currentLine + '</pre>'; | |
297 lineNumber++; | |
298 } | |
299 processLines(originalSource, originalSource.length, functionProcessLine); | |
300 return newSource; | |
301 } | |
302 | |
303 function findSourcePosition(pcToSearch) { | |
304 var position = 0; | |
305 var distance = 0x7FFFFFFF; | |
306 var pcToSearchOffset = parseInt(pcToSearch); | |
307 var processOneLine = function(text, begin, end) { | |
308 var currentLine = text.substring(begin, end); | |
309 var matches = positionFinder.exec(currentLine); | |
310 if (matches != null) { | |
311 var pcOffset = parseInt(matches[1]); | |
312 if (pcOffset <= pcToSearchOffset) { | |
313 var dist = pcToSearchOffset - pcOffset; | |
314 var pos = parseInt(matches[2]); | |
315 if ((dist < distance) || (dist == distance && pos > position)) { | |
316 position = pos; | |
317 distance = dist; | |
318 } | |
319 } | |
320 } | |
321 } | |
322 var asmText = getCurrentAsmText(); | |
323 processLines(asmText, asmText.length, processOneLine); | |
324 var code = getCurrentCodeObject(); | |
325 if (position == 0) return 0; | |
326 return position - code.firstSourcePosition; | |
327 } | |
328 | |
329 function findSourceLine(position) { | |
330 if (position == 0) return 1; | |
331 var line = 0; | |
332 var processOneLine = function(text, begin, end) { | |
333 if (begin < position) { | |
334 line++; | |
335 } | |
336 } | |
337 var sourceText = getCurrentSourceText(); | |
338 processLines(sourceText, sourceText.length, processOneLine); | |
339 return line; | |
340 } | |
341 | |
342 function functionChangedHandler() { | |
343 var functionSelect = document.getElementById('function-selector-id'); | |
344 var source = getCurrentSourceText(); | |
345 var sourceDivElement = document.getElementById('source-text'); | |
346 var code = getCurrentCodeObject(); | |
347 var newHtml = "<pre class=\"prettyprint linenums\" id=\"source-text\">" + | |
348 + 'function ' + code.name + source + "</pre>"; | |
349 sourceDivElement.innerHTML = newHtml; | |
350 try { | |
351 // Wrap in try to work when offline. | |
352 PR.prettyPrint(); | |
353 } catch (e) { | |
354 } | |
355 var sourceLineContainer = sourceDivElement.firstChild.firstChild; | |
356 var lineCount = sourceLineContainer.childElementCount; | |
357 var current = sourceLineContainer.firstChild; | |
358 for (var i = 1; i < lineCount; ++i) { | |
359 current.id = "source-line-" + i; | |
360 current = current.nextElementSibling; | |
361 } | |
362 | |
363 var asmDivElement = document.getElementById('asm-container'); | |
364 var asm = getCurrentAsmText(); | |
365 newHtml = prepareAsm(asm); | |
366 asmDivElement.innerHTML = '<div id=\"asm-text\">' + newHtml + '</div>'; | |
Dmitry Lomov (no reviews)
2013/10/18 16:07:53
Use createElement here
danno
2013/10/19 17:11:40
Done.
| |
367 } | |
368 | |
369 function kindChangedHandler(element) { | |
370 setKindByIndex(element.selectedIndex); | |
371 processFileContent(); | |
372 functionChangedHandler(); | |
373 } | |
374 | |
375 function readLog(evt) { | |
376 //Retrieve the first (and only!) File from the FileList object | |
377 var f = evt.target.files[0]; | |
378 if (f) { | |
379 var r = new FileReader(); | |
380 r.onload = function(e) { | |
381 var file = evt.target.files[0]; | |
382 currentFunctionKind = ""; | |
383 fileContent = e.target.result; | |
384 processFileContent(); | |
385 functionChangedHandler(); | |
386 } | |
387 r.readAsText(f); | |
388 } else { | |
389 alert("Failed to load file"); | |
390 } | |
391 } | |
392 | |
393 function buildFunctionKindSelector() { | |
394 document.getElementById('log-file-id').addEventListener('change', | |
Dmitry Lomov (no reviews)
2013/10/18 16:07:53
Consider replacing getElementById with parameters
danno
2013/10/19 17:11:40
Done.
| |
395 readLog, false); | |
396 var kindSelectElement = document.getElementById('kind-selector-id'); | |
397 for (var x = 0; x < kinds.length; ++x) { | |
398 var optionElement = document.createElement("option"); | |
399 optionElement.value = x; | |
400 optionElement.text = kinds[x]; | |
401 kindSelectElement.add(optionElement, null); | |
402 } | |
403 kindSelectElement.selectedIndex = 1; | |
404 setKindByIndex(1); | |
405 } | |
406 | |
407 return { | |
408 buildFunctionKindSelector: buildFunctionKindSelector, | |
409 kindChangedHandler: kindChangedHandler, | |
410 functionChangedHandler: functionChangedHandler, | |
411 asmClick: asmClick, | |
412 addressClick: addressClick | |
413 }; | |
414 | |
415 })(); | |
OLD | NEW |