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 |