OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2010 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 "use strict"; | |
31 | |
32 (function () { | |
33 | |
34 var DebuggerScript = {}; | |
35 | |
36 DebuggerScript.PauseOnExceptionsState = { | |
37 DontPauseOnExceptions: 0, | |
38 PauseOnAllExceptions: 1, | |
39 PauseOnUncaughtExceptions: 2 | |
40 }; | |
41 | |
42 DebuggerScript.ScopeInfoDetails = { | |
43 AllScopes: 0, | |
44 FastAsyncScopes: 1, | |
45 NoScopes: 2 | |
46 }; | |
47 | |
48 DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.D
ontPauseOnExceptions; | |
49 Debug.clearBreakOnException(); | |
50 Debug.clearBreakOnUncaughtException(); | |
51 | |
52 DebuggerScript.getAfterCompileScript = function(eventData) | |
53 { | |
54 return DebuggerScript._formatScript(eventData.script_.script_); | |
55 } | |
56 | |
57 DebuggerScript.getFunctionScopes = function(fun) | |
58 { | |
59 var mirror = MakeMirror(fun); | |
60 if (!mirror.isFunction()) | |
61 return null; | |
62 var count = mirror.scopeCount(); | |
63 if (count == 0) | |
64 return null; | |
65 var result = []; | |
66 for (var i = 0; i < count; i++) { | |
67 var scopeDetails = mirror.scope(i).details(); | |
68 var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(),
scopeDetails.object()); | |
69 if (!scopeObject) | |
70 continue; | |
71 result.push({ | |
72 type: scopeDetails.type(), | |
73 object: scopeObject, | |
74 name: scopeDetails.name() | |
75 }); | |
76 } | |
77 return result; | |
78 } | |
79 | |
80 DebuggerScript.getGeneratorObjectDetails = function(object) | |
81 { | |
82 var mirror = MakeMirror(object, true /* transient */); | |
83 if (!mirror.isGenerator()) | |
84 return null; | |
85 var funcMirror = mirror.func(); | |
86 if (!funcMirror.resolved()) | |
87 return null; | |
88 var result = { | |
89 "function": funcMirror.value(), | |
90 "functionName": funcMirror.debugName(), | |
91 "status": mirror.status() | |
92 }; | |
93 var script = funcMirror.script(); | |
94 var location = mirror.sourceLocation() || funcMirror.sourceLocation(); | |
95 if (script && location) { | |
96 result["location"] = { | |
97 "scriptId": String(script.id()), | |
98 "lineNumber": location.line, | |
99 "columnNumber": location.column | |
100 }; | |
101 } | |
102 return result; | |
103 } | |
104 | |
105 DebuggerScript.getCollectionEntries = function(object) | |
106 { | |
107 var mirror = MakeMirror(object, true /* transient */); | |
108 if (mirror.isMap()) | |
109 return mirror.entries(); | |
110 if (mirror.isSet() || mirror.isIterator()) { | |
111 var result = []; | |
112 var values = mirror.isSet() ? mirror.values() : mirror.preview(); | |
113 for (var i = 0; i < values.length; ++i) | |
114 result.push({ value: values[i] }); | |
115 return result; | |
116 } | |
117 } | |
118 | |
119 DebuggerScript.setFunctionVariableValue = function(functionValue, scopeIndex, va
riableName, newValue) | |
120 { | |
121 var mirror = MakeMirror(functionValue); | |
122 if (!mirror.isFunction()) | |
123 throw new Error("Function value has incorrect type"); | |
124 return DebuggerScript._setScopeVariableValue(mirror, scopeIndex, variableNam
e, newValue); | |
125 } | |
126 | |
127 DebuggerScript._setScopeVariableValue = function(scopeHolder, scopeIndex, variab
leName, newValue) | |
128 { | |
129 var scopeMirror = scopeHolder.scope(scopeIndex); | |
130 if (!scopeMirror) | |
131 throw new Error("Incorrect scope index"); | |
132 scopeMirror.setVariableValue(variableName, newValue); | |
133 return undefined; | |
134 } | |
135 | |
136 DebuggerScript.getScripts = function(contextGroupId) | |
137 { | |
138 var result = []; | |
139 var scripts = Debug.scripts(); | |
140 var contextDataPrefix = null; | |
141 if (contextGroupId) | |
142 contextDataPrefix = contextGroupId + ","; | |
143 for (var i = 0; i < scripts.length; ++i) { | |
144 var script = scripts[i]; | |
145 if (contextDataPrefix) { | |
146 if (!script.context_data) | |
147 continue; | |
148 // Context data is a string in the following format: | |
149 // <id>,<contextId>,("page"|"injected"|"worker") | |
150 if (script.context_data.indexOf(contextDataPrefix) !== 0) | |
151 continue; | |
152 } | |
153 result.push(DebuggerScript._formatScript(script)); | |
154 } | |
155 return result; | |
156 } | |
157 | |
158 DebuggerScript._formatScript = function(script) | |
159 { | |
160 var lineEnds = script.line_ends; | |
161 var lineCount = lineEnds.length; | |
162 var endLine = script.line_offset + lineCount - 1; | |
163 var endColumn; | |
164 // V8 will not count last line if script source ends with \n. | |
165 if (script.source[script.source.length - 1] === '\n') { | |
166 endLine += 1; | |
167 endColumn = 0; | |
168 } else { | |
169 if (lineCount === 1) | |
170 endColumn = script.source.length + script.column_offset; | |
171 else | |
172 endColumn = script.source.length - (lineEnds[lineCount - 2] + 1); | |
173 } | |
174 | |
175 /** | |
176 * @return {number} | |
177 */ | |
178 function executionContextId() | |
179 { | |
180 var context_data = script.context_data; | |
181 if (!context_data) | |
182 return 0; | |
183 var firstComma = context_data.indexOf(","); | |
184 if (firstComma === -1) | |
185 return 0; | |
186 var secondComma = context_data.indexOf(",", firstComma + 1); | |
187 if (secondComma === -1) | |
188 return 0; | |
189 | |
190 return parseInt(context_data.substring(firstComma + 1, secondComma), 10)
|| 0; | |
191 } | |
192 | |
193 return { | |
194 id: script.id, | |
195 name: script.nameOrSourceURL(), | |
196 sourceURL: script.source_url, | |
197 sourceMappingURL: script.source_mapping_url, | |
198 source: script.source, | |
199 startLine: script.line_offset, | |
200 startColumn: script.column_offset, | |
201 endLine: endLine, | |
202 endColumn: endColumn, | |
203 executionContextId: executionContextId(), | |
204 isContentScript: !!script.context_data && script.context_data.endsWith("
,injected"), | |
205 isInternalScript: script.is_debugger_script | |
206 }; | |
207 } | |
208 | |
209 DebuggerScript.setBreakpoint = function(execState, info) | |
210 { | |
211 var positionAlignment = info.interstatementLocation ? Debug.BreakPositionAli
gnment.BreakPosition : Debug.BreakPositionAlignment.Statement; | |
212 var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber,
info.columnNumber, info.condition, undefined, positionAlignment); | |
213 | |
214 var locations = Debug.findBreakPointActualLocations(breakId); | |
215 if (!locations.length) | |
216 return undefined; | |
217 info.lineNumber = locations[0].line; | |
218 info.columnNumber = locations[0].column; | |
219 return breakId.toString(); | |
220 } | |
221 | |
222 DebuggerScript.removeBreakpoint = function(execState, info) | |
223 { | |
224 Debug.findBreakPoint(info.breakpointId, true); | |
225 } | |
226 | |
227 DebuggerScript.pauseOnExceptionsState = function() | |
228 { | |
229 return DebuggerScript._pauseOnExceptionsState; | |
230 } | |
231 | |
232 DebuggerScript.setPauseOnExceptionsState = function(newState) | |
233 { | |
234 DebuggerScript._pauseOnExceptionsState = newState; | |
235 | |
236 if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState) | |
237 Debug.setBreakOnException(); | |
238 else | |
239 Debug.clearBreakOnException(); | |
240 | |
241 if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newS
tate) | |
242 Debug.setBreakOnUncaughtException(); | |
243 else | |
244 Debug.clearBreakOnUncaughtException(); | |
245 } | |
246 | |
247 DebuggerScript.frameCount = function(execState) | |
248 { | |
249 return execState.frameCount(); | |
250 } | |
251 | |
252 DebuggerScript.currentCallFrame = function(execState, data) | |
253 { | |
254 var maximumLimit = data >> 2; | |
255 var scopeDetailsLevel = data & 3; | |
256 | |
257 var frameCount = execState.frameCount(); | |
258 if (maximumLimit && maximumLimit < frameCount) | |
259 frameCount = maximumLimit; | |
260 var topFrame = undefined; | |
261 for (var i = frameCount - 1; i >= 0; i--) { | |
262 var frameMirror = execState.frame(i); | |
263 topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFram
e, scopeDetailsLevel); | |
264 } | |
265 return topFrame; | |
266 } | |
267 | |
268 DebuggerScript.currentCallFrameByIndex = function(execState, index) | |
269 { | |
270 if (index < 0) | |
271 return undefined; | |
272 var frameCount = execState.frameCount(); | |
273 if (index >= frameCount) | |
274 return undefined; | |
275 return DebuggerScript._frameMirrorToJSCallFrame(execState.frame(index), unde
fined, DebuggerScript.ScopeInfoDetails.NoScopes); | |
276 } | |
277 | |
278 DebuggerScript.stepIntoStatement = function(execState) | |
279 { | |
280 execState.prepareStep(Debug.StepAction.StepIn); | |
281 } | |
282 | |
283 DebuggerScript.stepFrameStatement = function(execState) | |
284 { | |
285 execState.prepareStep(Debug.StepAction.StepFrame); | |
286 } | |
287 | |
288 DebuggerScript.stepOverStatement = function(execState, callFrame) | |
289 { | |
290 execState.prepareStep(Debug.StepAction.StepNext); | |
291 } | |
292 | |
293 DebuggerScript.stepOutOfFunction = function(execState, callFrame) | |
294 { | |
295 execState.prepareStep(Debug.StepAction.StepOut); | |
296 } | |
297 | |
298 DebuggerScript.clearStepping = function() | |
299 { | |
300 Debug.clearStepping(); | |
301 } | |
302 | |
303 // Returns array in form: | |
304 // [ 0, <v8_result_report> ] in case of success | |
305 // or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column
_number> ] in case of compile error, numbers are 1-based. | |
306 // or throws exception with message. | |
307 DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview) | |
308 { | |
309 var scripts = Debug.scripts(); | |
310 var scriptToEdit = null; | |
311 for (var i = 0; i < scripts.length; i++) { | |
312 if (scripts[i].id == scriptId) { | |
313 scriptToEdit = scripts[i]; | |
314 break; | |
315 } | |
316 } | |
317 if (!scriptToEdit) | |
318 throw("Script not found"); | |
319 | |
320 var changeLog = []; | |
321 try { | |
322 var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, pre
view, changeLog); | |
323 return [0, result.stack_modified]; | |
324 } catch (e) { | |
325 if (e instanceof Debug.LiveEdit.Failure && "details" in e) { | |
326 var details = e.details; | |
327 if (details.type === "liveedit_compile_error") { | |
328 var startPosition = details.position.start; | |
329 return [1, String(e), String(details.syntaxErrorMessage), Number
(startPosition.line), Number(startPosition.column)]; | |
330 } | |
331 } | |
332 throw e; | |
333 } | |
334 } | |
335 | |
336 DebuggerScript.clearBreakpoints = function(execState, info) | |
337 { | |
338 Debug.clearAllBreakPoints(); | |
339 } | |
340 | |
341 DebuggerScript.setBreakpointsActivated = function(execState, info) | |
342 { | |
343 Debug.debuggerFlags().breakPointsActive.setValue(info.enabled); | |
344 } | |
345 | |
346 DebuggerScript.getScriptSource = function(eventData) | |
347 { | |
348 return eventData.script().source(); | |
349 } | |
350 | |
351 DebuggerScript.setScriptSource = function(eventData, source) | |
352 { | |
353 if (eventData.script().data() === "injected-script") | |
354 return; | |
355 eventData.script().setSource(source); | |
356 } | |
357 | |
358 DebuggerScript.getScriptName = function(eventData) | |
359 { | |
360 return eventData.script().script_.nameOrSourceURL(); | |
361 } | |
362 | |
363 DebuggerScript.getBreakpointNumbers = function(eventData) | |
364 { | |
365 var breakpoints = eventData.breakPointsHit(); | |
366 var numbers = []; | |
367 if (!breakpoints) | |
368 return numbers; | |
369 | |
370 for (var i = 0; i < breakpoints.length; i++) { | |
371 var breakpoint = breakpoints[i]; | |
372 var scriptBreakPoint = breakpoint.script_break_point(); | |
373 numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.n
umber()); | |
374 } | |
375 return numbers; | |
376 } | |
377 | |
378 DebuggerScript.isEvalCompilation = function(eventData) | |
379 { | |
380 var script = eventData.script(); | |
381 return (script.compilationType() === Debug.ScriptCompilationType.Eval); | |
382 } | |
383 | |
384 // NOTE: This function is performance critical, as it can be run on every | |
385 // statement that generates an async event (like addEventListener) to support | |
386 // asynchronous call stacks. Thus, when possible, initialize the data lazily. | |
387 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, sc
opeDetailsLevel) | |
388 { | |
389 // Stuff that can not be initialized lazily (i.e. valid while paused with a
valid break_id). | |
390 // The frameMirror and scopeMirror can be accessed only while paused on the
debugger. | |
391 var frameDetails = frameMirror.details(); | |
392 | |
393 var funcObject = frameDetails.func(); | |
394 var sourcePosition = frameDetails.sourcePosition(); | |
395 var thisObject = frameDetails.receiver(); | |
396 | |
397 var isAtReturn = !!frameDetails.isAtReturn(); | |
398 var returnValue = isAtReturn ? frameDetails.returnValue() : undefined; | |
399 | |
400 var scopeMirrors = (scopeDetailsLevel === DebuggerScript.ScopeInfoDetails.No
Scopes ? [] : frameMirror.allScopes(scopeDetailsLevel === DebuggerScript.ScopeIn
foDetails.FastAsyncScopes)); | |
401 var scopeTypes = new Array(scopeMirrors.length); | |
402 var scopeObjects = new Array(scopeMirrors.length); | |
403 var scopeNames = new Array(scopeMirrors.length); | |
404 for (var i = 0; i < scopeMirrors.length; ++i) { | |
405 var scopeDetails = scopeMirrors[i].details(); | |
406 scopeTypes[i] = scopeDetails.type(); | |
407 scopeObjects[i] = scopeDetails.object(); | |
408 scopeNames[i] = scopeDetails.name(); | |
409 } | |
410 | |
411 // Calculated lazily. | |
412 var scopeChain; | |
413 var funcMirror; | |
414 var location; | |
415 | |
416 function lazyScopeChain() | |
417 { | |
418 if (!scopeChain) { | |
419 scopeChain = []; | |
420 for (var i = 0, j = 0; i < scopeObjects.length; ++i) { | |
421 var scopeObject = DebuggerScript._buildScopeObject(scopeTypes[i]
, scopeObjects[i], scopeNames[i]); | |
422 if (scopeObject) { | |
423 scopeTypes[j] = scopeTypes[i]; | |
424 scopeNames[j] = scopeNames[i]; | |
425 scopeChain[j] = scopeObject; | |
426 ++j; | |
427 } | |
428 } | |
429 scopeTypes.length = scopeChain.length; | |
430 scopeNames.length = scopeChain.length; | |
431 scopeObjects = null; // Free for GC. | |
432 } | |
433 return scopeChain; | |
434 } | |
435 | |
436 function lazyScopeTypes() | |
437 { | |
438 if (!scopeChain) | |
439 lazyScopeChain(); | |
440 return scopeTypes; | |
441 } | |
442 | |
443 function lazyScopeNames() | |
444 { | |
445 if (!scopeChain) | |
446 lazyScopeChain(); | |
447 return scopeNames; | |
448 } | |
449 | |
450 function ensureFuncMirror() | |
451 { | |
452 if (!funcMirror) { | |
453 funcMirror = MakeMirror(funcObject); | |
454 if (!funcMirror.isFunction()) | |
455 funcMirror = new UnresolvedFunctionMirror(funcObject); | |
456 } | |
457 return funcMirror; | |
458 } | |
459 | |
460 function ensureLocation() | |
461 { | |
462 if (!location) { | |
463 var script = ensureFuncMirror().script(); | |
464 if (script) | |
465 location = script.locationFromPosition(sourcePosition, true); | |
466 if (!location) | |
467 location = { line: 0, column: 0 }; | |
468 } | |
469 return location; | |
470 } | |
471 | |
472 function line() | |
473 { | |
474 return ensureLocation().line; | |
475 } | |
476 | |
477 function column() | |
478 { | |
479 return ensureLocation().column; | |
480 } | |
481 | |
482 function sourceID() | |
483 { | |
484 var script = ensureFuncMirror().script(); | |
485 return script && script.id(); | |
486 } | |
487 | |
488 function scriptName() | |
489 { | |
490 var script = ensureFuncMirror().script(); | |
491 return script && script.name(); | |
492 } | |
493 | |
494 function functionName() | |
495 { | |
496 return ensureFuncMirror().debugName(); | |
497 } | |
498 | |
499 function functionLine() | |
500 { | |
501 var location = ensureFuncMirror().sourceLocation(); | |
502 return location ? location.line : 0; | |
503 } | |
504 | |
505 function functionColumn() | |
506 { | |
507 var location = ensureFuncMirror().sourceLocation(); | |
508 return location ? location.column : 0; | |
509 } | |
510 | |
511 function evaluate(expression, scopeExtension) | |
512 { | |
513 return frameMirror.evaluate(expression, false, scopeExtension).value(); | |
514 } | |
515 | |
516 function restart() | |
517 { | |
518 return frameMirror.restart(); | |
519 } | |
520 | |
521 function setVariableValue(scopeNumber, variableName, newValue) | |
522 { | |
523 return DebuggerScript._setScopeVariableValue(frameMirror, scopeNumber, v
ariableName, newValue); | |
524 } | |
525 | |
526 function stepInPositions() | |
527 { | |
528 var stepInPositionsV8 = frameMirror.stepInPositions(); | |
529 var stepInPositionsProtocol; | |
530 if (stepInPositionsV8) { | |
531 stepInPositionsProtocol = []; | |
532 var script = ensureFuncMirror().script(); | |
533 if (script) { | |
534 var scriptId = String(script.id()); | |
535 for (var i = 0; i < stepInPositionsV8.length; i++) { | |
536 var item = { | |
537 scriptId: scriptId, | |
538 lineNumber: stepInPositionsV8[i].position.line, | |
539 columnNumber: stepInPositionsV8[i].position.column | |
540 }; | |
541 stepInPositionsProtocol.push(item); | |
542 } | |
543 } | |
544 } | |
545 return JSON.stringify(stepInPositionsProtocol); | |
546 } | |
547 | |
548 return { | |
549 "sourceID": sourceID, | |
550 "line": line, | |
551 "column": column, | |
552 "scriptName": scriptName, | |
553 "functionName": functionName, | |
554 "functionLine": functionLine, | |
555 "functionColumn": functionColumn, | |
556 "thisObject": thisObject, | |
557 "scopeChain": lazyScopeChain, | |
558 "scopeType": lazyScopeTypes, | |
559 "scopeName": lazyScopeNames, | |
560 "evaluate": evaluate, | |
561 "caller": callerFrame, | |
562 "restart": restart, | |
563 "setVariableValue": setVariableValue, | |
564 "stepInPositions": stepInPositions, | |
565 "isAtReturn": isAtReturn, | |
566 "returnValue": returnValue | |
567 }; | |
568 } | |
569 | |
570 DebuggerScript._buildScopeObject = function(scopeType, scopeObject) | |
571 { | |
572 var result; | |
573 switch (scopeType) { | |
574 case ScopeType.Local: | |
575 case ScopeType.Closure: | |
576 case ScopeType.Catch: | |
577 case ScopeType.Block: | |
578 case ScopeType.Script: | |
579 // For transient objects we create a "persistent" copy that contains | |
580 // the same properties. | |
581 // Reset scope object prototype to null so that the proto properties | |
582 // don't appear in the local scope section. | |
583 var properties = MakeMirror(scopeObject, true /* transient */).propertie
s(); | |
584 // Almost always Script scope will be empty, so just filter out that noi
se. | |
585 // Also drop empty Block scopes, should we get any. | |
586 if (!properties.length && (scopeType === ScopeType.Script || scopeType =
== ScopeType.Block)) | |
587 break; | |
588 result = { __proto__: null }; | |
589 for (var j = 0; j < properties.length; j++) { | |
590 var name = properties[j].name(); | |
591 if (name.charAt(0) === ".") | |
592 continue; // Skip internal variables like ".arguments" | |
593 result[name] = properties[j].value_; | |
594 } | |
595 break; | |
596 case ScopeType.Global: | |
597 case ScopeType.With: | |
598 result = scopeObject; | |
599 break; | |
600 } | |
601 return result; | |
602 } | |
603 | |
604 DebuggerScript.getPromiseDetails = function(eventData) | |
605 { | |
606 return { | |
607 "promise": eventData.promise().value(), | |
608 "parentPromise": eventData.parentPromise().value(), | |
609 "status": eventData.status() | |
610 }; | |
611 } | |
612 | |
613 // We never resolve Mirror by its handle so to avoid memory leaks caused by Mirr
ors in the cache we disable it. | |
614 ToggleMirrorCache(false); | |
615 | |
616 return DebuggerScript; | |
617 })(); | |
OLD | NEW |