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 | |
31 (function () { | |
32 | |
33 var DebuggerScript = {}; | |
34 | |
35 DebuggerScript.PauseOnExceptionsState = { | |
36 DontPauseOnExceptions: 0, | |
37 PauseOnAllExceptions: 1, | |
38 PauseOnUncaughtExceptions: 2 | |
39 }; | |
40 | |
41 DebuggerScript.ScopeInfoDetails = { | |
42 AllScopes: 0, | |
43 FastAsyncScopes: 1, | |
44 NoScopes: 2 | |
45 }; | |
46 | |
47 DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.D
ontPauseOnExceptions; | |
48 Debug.clearBreakOnException(); | |
49 Debug.clearBreakOnUncaughtException(); | |
50 | |
51 DebuggerScript.getAfterCompileScript = function(eventData) | |
52 { | |
53 return DebuggerScript._formatScript(eventData.script_.script_); | |
54 } | |
55 | |
56 DebuggerScript.getWorkerScripts = function() | |
57 { | |
58 var result = []; | |
59 var scripts = Debug.scripts(); | |
60 for (var i = 0; i < scripts.length; ++i) { | |
61 var script = scripts[i]; | |
62 // Workers don't share same V8 heap now so there is no need to complicat
e stuff with | |
63 // the context id like we do to discriminate between scripts from differ
ent pages. | |
64 // However we need to filter out v8 native scripts. | |
65 if (script.context_data && script.context_data === "worker") | |
66 result.push(DebuggerScript._formatScript(script)); | |
67 } | |
68 return result; | |
69 } | |
70 | |
71 DebuggerScript.getFunctionScopes = function(fun) | |
72 { | |
73 var mirror = MakeMirror(fun); | |
74 var count = mirror.scopeCount(); | |
75 if (count == 0) | |
76 return null; | |
77 var result = []; | |
78 for (var i = 0; i < count; i++) { | |
79 var scopeDetails = mirror.scope(i).details(); | |
80 result[i] = { | |
81 type: scopeDetails.type(), | |
82 object: DebuggerScript._buildScopeObject(scopeDetails.type(), scopeD
etails.object()) | |
83 }; | |
84 } | |
85 return result; | |
86 } | |
87 | |
88 DebuggerScript.getCollectionEntries = function(object) | |
89 { | |
90 var mirror = MakeMirror(object, true /* transient */); | |
91 if (mirror.isMap()) | |
92 return mirror.entries(); | |
93 if (mirror.isSet()) { | |
94 var result = []; | |
95 var values = mirror.values(); | |
96 for (var i = 0; i < values.length; ++i) | |
97 result.push({ value: values[i] }); | |
98 return result; | |
99 } | |
100 } | |
101 | |
102 DebuggerScript.getInternalProperties = function(value) | |
103 { | |
104 var properties = ObjectMirror.GetInternalProperties(value); | |
105 var result = []; | |
106 for (var i = 0; i < properties.length; i++) { | |
107 var mirror = properties[i]; | |
108 result.push({ | |
109 name: mirror.name(), | |
110 value: mirror.value().value() | |
111 }); | |
112 } | |
113 return result; | |
114 } | |
115 | |
116 DebuggerScript.setFunctionVariableValue = function(functionValue, scopeIndex, va
riableName, newValue) | |
117 { | |
118 var mirror = MakeMirror(functionValue); | |
119 if (!mirror.isFunction()) | |
120 throw new Error("Function value has incorrect type"); | |
121 return DebuggerScript._setScopeVariableValue(mirror, scopeIndex, variableNam
e, newValue); | |
122 } | |
123 | |
124 DebuggerScript._setScopeVariableValue = function(scopeHolder, scopeIndex, variab
leName, newValue) | |
125 { | |
126 var scopeMirror = scopeHolder.scope(scopeIndex); | |
127 if (!scopeMirror) | |
128 throw new Error("Incorrect scope index"); | |
129 scopeMirror.setVariableValue(variableName, newValue); | |
130 return undefined; | |
131 } | |
132 | |
133 DebuggerScript.getScripts = function(contextData) | |
134 { | |
135 var result = []; | |
136 | |
137 if (!contextData) | |
138 return result; | |
139 var comma = contextData.indexOf(","); | |
140 if (comma === -1) | |
141 return result; | |
142 // Context data is a string in the following format: | |
143 // ("page"|"injected")","<page id> | |
144 var idSuffix = contextData.substring(comma); // including the comma | |
145 | |
146 var scripts = Debug.scripts(); | |
147 for (var i = 0; i < scripts.length; ++i) { | |
148 var script = scripts[i]; | |
149 if (script.context_data && script.context_data.lastIndexOf(idSuffix) !=
-1) | |
150 result.push(DebuggerScript._formatScript(script)); | |
151 } | |
152 return result; | |
153 } | |
154 | |
155 DebuggerScript._formatScript = function(script) | |
156 { | |
157 var lineEnds = script.line_ends; | |
158 var lineCount = lineEnds.length; | |
159 var endLine = script.line_offset + lineCount - 1; | |
160 var endColumn; | |
161 // V8 will not count last line if script source ends with \n. | |
162 if (script.source[script.source.length - 1] === '\n') { | |
163 endLine += 1; | |
164 endColumn = 0; | |
165 } else { | |
166 if (lineCount === 1) | |
167 endColumn = script.source.length + script.column_offset; | |
168 else | |
169 endColumn = script.source.length - (lineEnds[lineCount - 2] + 1); | |
170 } | |
171 | |
172 return { | |
173 id: script.id, | |
174 name: script.nameOrSourceURL(), | |
175 sourceURL: script.source_url, | |
176 sourceMappingURL: script.source_mapping_url, | |
177 source: script.source, | |
178 startLine: script.line_offset, | |
179 startColumn: script.column_offset, | |
180 endLine: endLine, | |
181 endColumn: endColumn, | |
182 isContentScript: !!script.context_data && script.context_data.indexOf("i
njected") == 0 | |
183 }; | |
184 } | |
185 | |
186 DebuggerScript.setBreakpoint = function(execState, info) | |
187 { | |
188 var positionAlignment = info.interstatementLocation ? Debug.BreakPositionAli
gnment.BreakPosition : Debug.BreakPositionAlignment.Statement; | |
189 var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber,
info.columnNumber, info.condition, undefined, positionAlignment); | |
190 | |
191 var locations = Debug.findBreakPointActualLocations(breakId); | |
192 if (!locations.length) | |
193 return undefined; | |
194 info.lineNumber = locations[0].line; | |
195 info.columnNumber = locations[0].column; | |
196 return breakId.toString(); | |
197 } | |
198 | |
199 DebuggerScript.removeBreakpoint = function(execState, info) | |
200 { | |
201 Debug.findBreakPoint(info.breakpointId, true); | |
202 } | |
203 | |
204 DebuggerScript.pauseOnExceptionsState = function() | |
205 { | |
206 return DebuggerScript._pauseOnExceptionsState; | |
207 } | |
208 | |
209 DebuggerScript.setPauseOnExceptionsState = function(newState) | |
210 { | |
211 DebuggerScript._pauseOnExceptionsState = newState; | |
212 | |
213 if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState) | |
214 Debug.setBreakOnException(); | |
215 else | |
216 Debug.clearBreakOnException(); | |
217 | |
218 if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newS
tate) | |
219 Debug.setBreakOnUncaughtException(); | |
220 else | |
221 Debug.clearBreakOnUncaughtException(); | |
222 } | |
223 | |
224 DebuggerScript.frameCount = function(execState) | |
225 { | |
226 return execState.frameCount(); | |
227 } | |
228 | |
229 DebuggerScript.currentCallFrame = function(execState, data) | |
230 { | |
231 var maximumLimit = data >> 2; | |
232 var scopeDetailsLevel = data & 3; | |
233 | |
234 var frameCount = execState.frameCount(); | |
235 if (maximumLimit && maximumLimit < frameCount) | |
236 frameCount = maximumLimit; | |
237 var topFrame = undefined; | |
238 for (var i = frameCount - 1; i >= 0; i--) { | |
239 var frameMirror = execState.frame(i); | |
240 topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFram
e, scopeDetailsLevel); | |
241 } | |
242 return topFrame; | |
243 } | |
244 | |
245 DebuggerScript.currentCallFrameByIndex = function(execState, index) | |
246 { | |
247 if (index < 0) | |
248 return undefined; | |
249 var frameCount = execState.frameCount(); | |
250 if (index >= frameCount) | |
251 return undefined; | |
252 return DebuggerScript._frameMirrorToJSCallFrame(execState.frame(index), unde
fined, DebuggerScript.ScopeInfoDetails.NoScopes); | |
253 } | |
254 | |
255 DebuggerScript.stepIntoStatement = function(execState) | |
256 { | |
257 execState.prepareStep(Debug.StepAction.StepIn, 1); | |
258 } | |
259 | |
260 DebuggerScript.stepOverStatement = function(execState, callFrame) | |
261 { | |
262 execState.prepareStep(Debug.StepAction.StepNext, 1); | |
263 } | |
264 | |
265 DebuggerScript.stepOutOfFunction = function(execState, callFrame) | |
266 { | |
267 execState.prepareStep(Debug.StepAction.StepOut, 1); | |
268 } | |
269 | |
270 // Returns array in form: | |
271 // [ 0, <v8_result_report> ] in case of success | |
272 // or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column
_number> ] in case of compile error, numbers are 1-based. | |
273 // or throws exception with message. | |
274 DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview) | |
275 { | |
276 var scripts = Debug.scripts(); | |
277 var scriptToEdit = null; | |
278 for (var i = 0; i < scripts.length; i++) { | |
279 if (scripts[i].id == scriptId) { | |
280 scriptToEdit = scripts[i]; | |
281 break; | |
282 } | |
283 } | |
284 if (!scriptToEdit) | |
285 throw("Script not found"); | |
286 | |
287 var changeLog = []; | |
288 try { | |
289 var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, pre
view, changeLog); | |
290 return [0, result]; | |
291 } catch (e) { | |
292 if (e instanceof Debug.LiveEdit.Failure && "details" in e) { | |
293 var details = e.details; | |
294 if (details.type === "liveedit_compile_error") { | |
295 var startPosition = details.position.start; | |
296 return [1, String(e), String(details.syntaxErrorMessage), Number
(startPosition.line), Number(startPosition.column)]; | |
297 } | |
298 } | |
299 throw e; | |
300 } | |
301 } | |
302 | |
303 DebuggerScript.clearBreakpoints = function(execState, info) | |
304 { | |
305 Debug.clearAllBreakPoints(); | |
306 } | |
307 | |
308 DebuggerScript.setBreakpointsActivated = function(execState, info) | |
309 { | |
310 Debug.debuggerFlags().breakPointsActive.setValue(info.enabled); | |
311 } | |
312 | |
313 DebuggerScript.getScriptSource = function(eventData) | |
314 { | |
315 return eventData.script().source(); | |
316 } | |
317 | |
318 DebuggerScript.setScriptSource = function(eventData, source) | |
319 { | |
320 if (eventData.script().data() === "injected-script") | |
321 return; | |
322 eventData.script().setSource(source); | |
323 } | |
324 | |
325 DebuggerScript.getScriptName = function(eventData) | |
326 { | |
327 return eventData.script().script_.nameOrSourceURL(); | |
328 } | |
329 | |
330 DebuggerScript.getBreakpointNumbers = function(eventData) | |
331 { | |
332 var breakpoints = eventData.breakPointsHit(); | |
333 var numbers = []; | |
334 if (!breakpoints) | |
335 return numbers; | |
336 | |
337 for (var i = 0; i < breakpoints.length; i++) { | |
338 var breakpoint = breakpoints[i]; | |
339 var scriptBreakPoint = breakpoint.script_break_point(); | |
340 numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.n
umber()); | |
341 } | |
342 return numbers; | |
343 } | |
344 | |
345 DebuggerScript.isEvalCompilation = function(eventData) | |
346 { | |
347 var script = eventData.script(); | |
348 return (script.compilationType() === Debug.ScriptCompilationType.Eval); | |
349 } | |
350 | |
351 // NOTE: This function is performance critical, as it can be run on every | |
352 // statement that generates an async event (like addEventListener) to support | |
353 // asynchronous call stacks. Thus, when possible, initialize the data lazily. | |
354 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, sc
opeDetailsLevel) | |
355 { | |
356 // Stuff that can not be initialized lazily (i.e. valid while paused with a
valid break_id). | |
357 // The frameMirror and scopeMirror can be accessed only while paused on the
debugger. | |
358 var frameDetails = frameMirror.details(); | |
359 | |
360 var funcObject = frameDetails.func(); | |
361 var sourcePosition = frameDetails.sourcePosition(); | |
362 var thisObject = frameDetails.receiver(); | |
363 | |
364 var isAtReturn = !!frameDetails.isAtReturn(); | |
365 var returnValue = isAtReturn ? frameDetails.returnValue() : undefined; | |
366 | |
367 var scopeMirrors = (scopeDetailsLevel === DebuggerScript.ScopeInfoDetails.No
Scopes ? [] : frameMirror.allScopes(scopeDetailsLevel === DebuggerScript.ScopeIn
foDetails.FastAsyncScopes)); | |
368 var scopeTypes = new Array(scopeMirrors.length); | |
369 var scopeObjects = new Array(scopeMirrors.length); | |
370 for (var i = 0; i < scopeMirrors.length; ++i) { | |
371 var scopeDetails = scopeMirrors[i].details(); | |
372 scopeTypes[i] = scopeDetails.type(); | |
373 scopeObjects[i] = scopeDetails.object(); | |
374 } | |
375 | |
376 // Calculated lazily. | |
377 var scopeChain; | |
378 var funcMirror; | |
379 var location; | |
380 | |
381 function lazyScopeChain() | |
382 { | |
383 if (!scopeChain) { | |
384 scopeChain = []; | |
385 for (var i = 0; i < scopeObjects.length; ++i) | |
386 scopeChain.push(DebuggerScript._buildScopeObject(scopeTypes[i],
scopeObjects[i])); | |
387 scopeObjects = null; // Free for GC. | |
388 } | |
389 return scopeChain; | |
390 } | |
391 | |
392 function ensureFuncMirror() | |
393 { | |
394 if (!funcMirror) { | |
395 funcMirror = MakeMirror(funcObject); | |
396 if (!funcMirror.isFunction()) | |
397 funcMirror = new UnresolvedFunctionMirror(funcObject); | |
398 } | |
399 return funcMirror; | |
400 } | |
401 | |
402 function ensureLocation() | |
403 { | |
404 if (!location) { | |
405 var script = ensureFuncMirror().script(); | |
406 if (script) | |
407 location = script.locationFromPosition(sourcePosition, true); | |
408 if (!location) | |
409 location = { line: 0, column: 0 }; | |
410 } | |
411 return location; | |
412 } | |
413 | |
414 function line() | |
415 { | |
416 return ensureLocation().line; | |
417 } | |
418 | |
419 function column() | |
420 { | |
421 return ensureLocation().column; | |
422 } | |
423 | |
424 function sourceID() | |
425 { | |
426 var script = ensureFuncMirror().script(); | |
427 return script && script.id(); | |
428 } | |
429 | |
430 function scriptName() | |
431 { | |
432 var script = ensureFuncMirror().script(); | |
433 return script && script.name(); | |
434 } | |
435 | |
436 function functionName() | |
437 { | |
438 var func = ensureFuncMirror(); | |
439 if (!func.resolved()) | |
440 return undefined; | |
441 var displayName; | |
442 var valueMirror = func.property("displayName").value(); | |
443 if (valueMirror && valueMirror.isString()) | |
444 displayName = valueMirror.value(); | |
445 return displayName || func.name() || func.inferredName(); | |
446 } | |
447 | |
448 function evaluate(expression) | |
449 { | |
450 return frameMirror.evaluate(expression, false).value(); | |
451 } | |
452 | |
453 function restart() | |
454 { | |
455 return Debug.LiveEdit.RestartFrame(frameMirror); | |
456 } | |
457 | |
458 function setVariableValue(scopeNumber, variableName, newValue) | |
459 { | |
460 return DebuggerScript._setScopeVariableValue(frameMirror, scopeNumber, v
ariableName, newValue); | |
461 } | |
462 | |
463 function stepInPositions() | |
464 { | |
465 var stepInPositionsV8 = frameMirror.stepInPositions(); | |
466 var stepInPositionsProtocol; | |
467 if (stepInPositionsV8) { | |
468 stepInPositionsProtocol = []; | |
469 var script = ensureFuncMirror().script(); | |
470 if (script) { | |
471 var scriptId = String(script.id()); | |
472 for (var i = 0; i < stepInPositionsV8.length; i++) { | |
473 var item = { | |
474 scriptId: scriptId, | |
475 lineNumber: stepInPositionsV8[i].position.line, | |
476 columnNumber: stepInPositionsV8[i].position.column | |
477 }; | |
478 stepInPositionsProtocol.push(item); | |
479 } | |
480 } | |
481 } | |
482 return JSON.stringify(stepInPositionsProtocol); | |
483 } | |
484 | |
485 return { | |
486 "sourceID": sourceID, | |
487 "line": line, | |
488 "column": column, | |
489 "scriptName": scriptName, | |
490 "functionName": functionName, | |
491 "thisObject": thisObject, | |
492 "scopeChain": lazyScopeChain, | |
493 "scopeType": scopeTypes, | |
494 "evaluate": evaluate, | |
495 "caller": callerFrame, | |
496 "restart": restart, | |
497 "setVariableValue": setVariableValue, | |
498 "stepInPositions": stepInPositions, | |
499 "isAtReturn": isAtReturn, | |
500 "returnValue": returnValue | |
501 }; | |
502 } | |
503 | |
504 DebuggerScript._buildScopeObject = function(scopeType, scopeObject) | |
505 { | |
506 var result; | |
507 switch (scopeType) { | |
508 case ScopeType.Local: | |
509 case ScopeType.Closure: | |
510 case ScopeType.Catch: | |
511 // For transient objects we create a "persistent" copy that contains | |
512 // the same properties. | |
513 // Reset scope object prototype to null so that the proto properties | |
514 // don't appear in the local scope section. | |
515 result = { __proto__: null }; | |
516 var properties = MakeMirror(scopeObject, true /* transient */).propertie
s(); | |
517 for (var j = 0; j < properties.length; j++) { | |
518 var name = properties[j].name(); | |
519 if (name.charAt(0) === ".") | |
520 continue; // Skip internal variables like ".arguments" | |
521 result[name] = properties[j].value_; | |
522 } | |
523 break; | |
524 case ScopeType.Global: | |
525 case ScopeType.With: | |
526 result = scopeObject; | |
527 break; | |
528 case ScopeType.Block: | |
529 // Unsupported yet. Mustn't be reachable. | |
530 break; | |
531 } | |
532 return result; | |
533 } | |
534 | |
535 DebuggerScript.getPromiseDetails = function(eventData) | |
536 { | |
537 return { | |
538 "promise": eventData.promise().value(), | |
539 "parentPromise": eventData.parentPromise().value(), | |
540 "status": eventData.status() | |
541 }; | |
542 } | |
543 | |
544 // We never resolve Mirror by its handle so to avoid memory leaks caused by Mirr
ors in the cache we disable it. | |
545 ToggleMirrorCache(false); | |
546 | |
547 return DebuggerScript; | |
548 })(); | |
OLD | NEW |