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 /** @enum */ |
| 37 DebuggerScript.PauseOnExceptionsState = { |
| 38 DontPauseOnExceptions: 0, |
| 39 PauseOnAllExceptions: 1, |
| 40 PauseOnUncaughtExceptions: 2 |
| 41 }; |
| 42 |
| 43 DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.D
ontPauseOnExceptions; |
| 44 Debug.clearBreakOnException(); |
| 45 Debug.clearBreakOnUncaughtException(); |
| 46 |
| 47 /** |
| 48 * @param {?CompileEvent} eventData |
| 49 */ |
| 50 DebuggerScript.getAfterCompileScript = function(eventData) |
| 51 { |
| 52 var script = eventData.script().value(); |
| 53 if (!script.is_debugger_script) |
| 54 return DebuggerScript._formatScript(eventData.script().value()); |
| 55 return null; |
| 56 } |
| 57 |
| 58 /** @type {!Map<!ScopeType, string>} */ |
| 59 DebuggerScript._scopeTypeNames = new Map(); |
| 60 DebuggerScript._scopeTypeNames.set(ScopeType.Global, "global"); |
| 61 DebuggerScript._scopeTypeNames.set(ScopeType.Local, "local"); |
| 62 DebuggerScript._scopeTypeNames.set(ScopeType.With, "with"); |
| 63 DebuggerScript._scopeTypeNames.set(ScopeType.Closure, "closure"); |
| 64 DebuggerScript._scopeTypeNames.set(ScopeType.Catch, "catch"); |
| 65 DebuggerScript._scopeTypeNames.set(ScopeType.Block, "block"); |
| 66 DebuggerScript._scopeTypeNames.set(ScopeType.Script, "script"); |
| 67 |
| 68 /** |
| 69 * @param {function()} fun |
| 70 * @return {?Array<!Scope>} |
| 71 */ |
| 72 DebuggerScript.getFunctionScopes = function(fun) |
| 73 { |
| 74 var mirror = MakeMirror(fun); |
| 75 if (!mirror.isFunction()) |
| 76 return null; |
| 77 var functionMirror = /** @type {!FunctionMirror} */(mirror); |
| 78 var count = functionMirror.scopeCount(); |
| 79 if (count == 0) |
| 80 return null; |
| 81 var result = []; |
| 82 for (var i = 0; i < count; i++) { |
| 83 var scopeDetails = functionMirror.scope(i).details(); |
| 84 var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(),
scopeDetails.object()); |
| 85 if (!scopeObject) |
| 86 continue; |
| 87 result.push({ |
| 88 type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scope
Details.type())), |
| 89 object: scopeObject, |
| 90 name: scopeDetails.name() || "" |
| 91 }); |
| 92 } |
| 93 return result; |
| 94 } |
| 95 |
| 96 /** |
| 97 * @param {Object} object |
| 98 * @return {?RawLocation} |
| 99 */ |
| 100 DebuggerScript.getGeneratorObjectLocation = function(object) |
| 101 { |
| 102 var mirror = MakeMirror(object, true /* transient */); |
| 103 if (!mirror.isGenerator()) |
| 104 return null; |
| 105 var generatorMirror = /** @type {!GeneratorMirror} */(mirror); |
| 106 var funcMirror = generatorMirror.func(); |
| 107 if (!funcMirror.resolved()) |
| 108 return null; |
| 109 var location = generatorMirror.sourceLocation() || funcMirror.sourceLocation
(); |
| 110 var script = funcMirror.script(); |
| 111 if (script && location) { |
| 112 return { |
| 113 scriptId: "" + script.id(), |
| 114 lineNumber: location.line, |
| 115 columnNumber: location.column |
| 116 }; |
| 117 } |
| 118 return null; |
| 119 } |
| 120 |
| 121 /** |
| 122 * @param {Object} object |
| 123 * @return {!Array<!{value: *}>|undefined} |
| 124 */ |
| 125 DebuggerScript.getCollectionEntries = function(object) |
| 126 { |
| 127 var mirror = MakeMirror(object, true /* transient */); |
| 128 if (mirror.isMap()) |
| 129 return /** @type {!MapMirror} */(mirror).entries(); |
| 130 if (mirror.isSet() || mirror.isIterator()) { |
| 131 var result = []; |
| 132 var values = mirror.isSet() ? /** @type {!SetMirror} */(mirror).values()
: /** @type {!IteratorMirror} */(mirror).preview(); |
| 133 for (var i = 0; i < values.length; ++i) |
| 134 result.push({ value: values[i] }); |
| 135 return result; |
| 136 } |
| 137 } |
| 138 |
| 139 /** |
| 140 * @param {string|undefined} contextData |
| 141 * @return {number} |
| 142 */ |
| 143 DebuggerScript._executionContextId = function(contextData) |
| 144 { |
| 145 if (!contextData) |
| 146 return 0; |
| 147 var match = contextData.match(/^[^,]*,([^,]*),.*$/); |
| 148 if (!match) |
| 149 return 0; |
| 150 return parseInt(match[1], 10) || 0; |
| 151 } |
| 152 |
| 153 /** |
| 154 * @param {string|undefined} contextData |
| 155 * @return {string} |
| 156 */ |
| 157 DebuggerScript._executionContextAuxData = function(contextData) |
| 158 { |
| 159 if (!contextData) |
| 160 return ""; |
| 161 var match = contextData.match(/^[^,]*,[^,]*,(.*)$/); |
| 162 return match ? match[1] : ""; |
| 163 } |
| 164 |
| 165 /** |
| 166 * @param {string} contextGroupId |
| 167 * @return {!Array<!FormattedScript>} |
| 168 */ |
| 169 DebuggerScript.getScripts = function(contextGroupId) |
| 170 { |
| 171 var result = []; |
| 172 var scripts = Debug.scripts(); |
| 173 var contextDataPrefix = null; |
| 174 if (contextGroupId) |
| 175 contextDataPrefix = contextGroupId + ","; |
| 176 for (var i = 0; i < scripts.length; ++i) { |
| 177 var script = scripts[i]; |
| 178 if (contextDataPrefix) { |
| 179 if (!script.context_data) |
| 180 continue; |
| 181 // Context data is a string in the following format: |
| 182 // <contextGroupId>,<contextId>,<auxData> |
| 183 if (script.context_data.indexOf(contextDataPrefix) !== 0) |
| 184 continue; |
| 185 } |
| 186 if (script.is_debugger_script) |
| 187 continue; |
| 188 result.push(DebuggerScript._formatScript(script)); |
| 189 } |
| 190 return result; |
| 191 } |
| 192 |
| 193 /** |
| 194 * @param {!Script} script |
| 195 * @return {!FormattedScript} |
| 196 */ |
| 197 DebuggerScript._formatScript = function(script) |
| 198 { |
| 199 var lineEnds = script.line_ends; |
| 200 var lineCount = lineEnds.length; |
| 201 var endLine = script.line_offset + lineCount - 1; |
| 202 var endColumn; |
| 203 // V8 will not count last line if script source ends with \n. |
| 204 if (script.source[script.source.length - 1] === '\n') { |
| 205 endLine += 1; |
| 206 endColumn = 0; |
| 207 } else { |
| 208 if (lineCount === 1) |
| 209 endColumn = script.source.length + script.column_offset; |
| 210 else |
| 211 endColumn = script.source.length - (lineEnds[lineCount - 2] + 1); |
| 212 } |
| 213 return { |
| 214 id: script.id, |
| 215 name: script.nameOrSourceURL(), |
| 216 sourceURL: script.source_url, |
| 217 sourceMappingURL: script.source_mapping_url, |
| 218 source: script.source, |
| 219 startLine: script.line_offset, |
| 220 startColumn: script.column_offset, |
| 221 endLine: endLine, |
| 222 endColumn: endColumn, |
| 223 executionContextId: DebuggerScript._executionContextId(script.context_da
ta), |
| 224 // Note that we cannot derive aux data from context id because of compil
ation cache. |
| 225 executionContextAuxData: DebuggerScript._executionContextAuxData(script.
context_data) |
| 226 }; |
| 227 } |
| 228 |
| 229 /** |
| 230 * @param {!ExecutionState} execState |
| 231 * @param {!BreakpointInfo} info |
| 232 * @return {string|undefined} |
| 233 */ |
| 234 DebuggerScript.setBreakpoint = function(execState, info) |
| 235 { |
| 236 var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber,
info.columnNumber, info.condition, undefined, Debug.BreakPositionAlignment.State
ment); |
| 237 var locations = Debug.findBreakPointActualLocations(breakId); |
| 238 if (!locations.length) |
| 239 return undefined; |
| 240 info.lineNumber = locations[0].line; |
| 241 info.columnNumber = locations[0].column; |
| 242 return breakId.toString(); |
| 243 } |
| 244 |
| 245 /** |
| 246 * @param {!ExecutionState} execState |
| 247 * @param {!{breakpointId: number}} info |
| 248 */ |
| 249 DebuggerScript.removeBreakpoint = function(execState, info) |
| 250 { |
| 251 Debug.findBreakPoint(info.breakpointId, true); |
| 252 } |
| 253 |
| 254 /** |
| 255 * @return {number} |
| 256 */ |
| 257 DebuggerScript.pauseOnExceptionsState = function() |
| 258 { |
| 259 return DebuggerScript._pauseOnExceptionsState; |
| 260 } |
| 261 |
| 262 /** |
| 263 * @param {number} newState |
| 264 */ |
| 265 DebuggerScript.setPauseOnExceptionsState = function(newState) |
| 266 { |
| 267 DebuggerScript._pauseOnExceptionsState = newState; |
| 268 |
| 269 if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState) |
| 270 Debug.setBreakOnException(); |
| 271 else |
| 272 Debug.clearBreakOnException(); |
| 273 |
| 274 if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newS
tate) |
| 275 Debug.setBreakOnUncaughtException(); |
| 276 else |
| 277 Debug.clearBreakOnUncaughtException(); |
| 278 } |
| 279 |
| 280 /** |
| 281 * @param {!ExecutionState} execState |
| 282 * @param {number} limit |
| 283 * @return {!Array<!JavaScriptCallFrame>} |
| 284 */ |
| 285 DebuggerScript.currentCallFrames = function(execState, limit) |
| 286 { |
| 287 var frames = []; |
| 288 for (var i = 0; i < execState.frameCount() && (!limit || i < limit); ++i) |
| 289 frames.push(DebuggerScript._frameMirrorToJSCallFrame(execState.frame(i))
); |
| 290 return frames; |
| 291 } |
| 292 |
| 293 /** |
| 294 * @param {!ExecutionState} execState |
| 295 */ |
| 296 DebuggerScript.stepIntoStatement = function(execState) |
| 297 { |
| 298 execState.prepareStep(Debug.StepAction.StepIn); |
| 299 } |
| 300 |
| 301 /** |
| 302 * @param {!ExecutionState} execState |
| 303 */ |
| 304 DebuggerScript.stepFrameStatement = function(execState) |
| 305 { |
| 306 execState.prepareStep(Debug.StepAction.StepFrame); |
| 307 } |
| 308 |
| 309 /** |
| 310 * @param {!ExecutionState} execState |
| 311 */ |
| 312 DebuggerScript.stepOverStatement = function(execState) |
| 313 { |
| 314 execState.prepareStep(Debug.StepAction.StepNext); |
| 315 } |
| 316 |
| 317 /** |
| 318 * @param {!ExecutionState} execState |
| 319 */ |
| 320 DebuggerScript.stepOutOfFunction = function(execState) |
| 321 { |
| 322 execState.prepareStep(Debug.StepAction.StepOut); |
| 323 } |
| 324 |
| 325 DebuggerScript.clearStepping = function() |
| 326 { |
| 327 Debug.clearStepping(); |
| 328 } |
| 329 |
| 330 // Returns array in form: |
| 331 // [ 0, <v8_result_report> ] in case of success |
| 332 // or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column
_number> ] in case of compile error, numbers are 1-based. |
| 333 // or throws exception with message. |
| 334 /** |
| 335 * @param {number} scriptId |
| 336 * @param {string} newSource |
| 337 * @param {boolean} preview |
| 338 * @return {!Array<*>} |
| 339 */ |
| 340 DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview) |
| 341 { |
| 342 var scripts = Debug.scripts(); |
| 343 var scriptToEdit = null; |
| 344 for (var i = 0; i < scripts.length; i++) { |
| 345 if (scripts[i].id == scriptId) { |
| 346 scriptToEdit = scripts[i]; |
| 347 break; |
| 348 } |
| 349 } |
| 350 if (!scriptToEdit) |
| 351 throw("Script not found"); |
| 352 |
| 353 var changeLog = []; |
| 354 try { |
| 355 var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, pre
view, changeLog); |
| 356 return [0, result.stack_modified]; |
| 357 } catch (e) { |
| 358 if (e instanceof Debug.LiveEdit.Failure && "details" in e) { |
| 359 var details = /** @type {!LiveEditErrorDetails} */(e.details); |
| 360 if (details.type === "liveedit_compile_error") { |
| 361 var startPosition = details.position.start; |
| 362 return [1, String(e), String(details.syntaxErrorMessage), Number
(startPosition.line), Number(startPosition.column)]; |
| 363 } |
| 364 } |
| 365 throw e; |
| 366 } |
| 367 } |
| 368 |
| 369 /** |
| 370 * @param {!ExecutionState} execState |
| 371 */ |
| 372 DebuggerScript.clearBreakpoints = function(execState) |
| 373 { |
| 374 Debug.clearAllBreakPoints(); |
| 375 } |
| 376 |
| 377 /** |
| 378 * @param {!ExecutionState} execState |
| 379 * @param {!{enabled: boolean}} info |
| 380 */ |
| 381 DebuggerScript.setBreakpointsActivated = function(execState, info) |
| 382 { |
| 383 Debug.debuggerFlags().breakPointsActive.setValue(info.enabled); |
| 384 } |
| 385 |
| 386 /** |
| 387 * @param {!BreakEvent} eventData |
| 388 */ |
| 389 DebuggerScript.getBreakpointNumbers = function(eventData) |
| 390 { |
| 391 var breakpoints = eventData.breakPointsHit(); |
| 392 var numbers = []; |
| 393 if (!breakpoints) |
| 394 return numbers; |
| 395 |
| 396 for (var i = 0; i < breakpoints.length; i++) { |
| 397 var breakpoint = breakpoints[i]; |
| 398 var scriptBreakPoint = breakpoint.script_break_point(); |
| 399 numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.n
umber()); |
| 400 } |
| 401 return numbers; |
| 402 } |
| 403 |
| 404 // NOTE: This function is performance critical, as it can be run on every |
| 405 // statement that generates an async event (like addEventListener) to support |
| 406 // asynchronous call stacks. Thus, when possible, initialize the data lazily. |
| 407 /** |
| 408 * @param {!FrameMirror} frameMirror |
| 409 * @return {!JavaScriptCallFrame} |
| 410 */ |
| 411 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror) |
| 412 { |
| 413 // Stuff that can not be initialized lazily (i.e. valid while paused with a
valid break_id). |
| 414 // The frameMirror and scopeMirror can be accessed only while paused on the
debugger. |
| 415 var frameDetails = frameMirror.details(); |
| 416 |
| 417 var funcObject = frameDetails.func(); |
| 418 var sourcePosition = frameDetails.sourcePosition(); |
| 419 var thisObject = frameDetails.receiver(); |
| 420 |
| 421 var isAtReturn = !!frameDetails.isAtReturn(); |
| 422 var returnValue = isAtReturn ? frameDetails.returnValue() : undefined; |
| 423 |
| 424 var scopeMirrors = frameMirror.allScopes(false); |
| 425 /** @type {!Array<ScopeType>} */ |
| 426 var scopeTypes = new Array(scopeMirrors.length); |
| 427 /** @type {?Array<!Object>} */ |
| 428 var scopeObjects = new Array(scopeMirrors.length); |
| 429 /** @type {!Array<string|undefined>} */ |
| 430 var scopeNames = new Array(scopeMirrors.length); |
| 431 /** @type {?Array<number>} */ |
| 432 var scopeStartPositions = new Array(scopeMirrors.length); |
| 433 /** @type {?Array<number>} */ |
| 434 var scopeEndPositions = new Array(scopeMirrors.length); |
| 435 /** @type {?Array<function()|null>} */ |
| 436 var scopeFunctions = new Array(scopeMirrors.length); |
| 437 for (var i = 0; i < scopeMirrors.length; ++i) { |
| 438 var scopeDetails = scopeMirrors[i].details(); |
| 439 scopeTypes[i] = scopeDetails.type(); |
| 440 scopeObjects[i] = scopeDetails.object(); |
| 441 scopeNames[i] = scopeDetails.name(); |
| 442 scopeStartPositions[i] = scopeDetails.startPosition ? scopeDetails.start
Position() : 0; |
| 443 scopeEndPositions[i] = scopeDetails.endPosition ? scopeDetails.endPositi
on() : 0; |
| 444 scopeFunctions[i] = scopeDetails.func ? scopeDetails.func() : null; |
| 445 } |
| 446 |
| 447 // Calculated lazily. |
| 448 var scopeChain; |
| 449 var funcMirror; |
| 450 var location; |
| 451 /** @type {!Array<?RawLocation>} */ |
| 452 var scopeStartLocations; |
| 453 /** @type {!Array<?RawLocation>} */ |
| 454 var scopeEndLocations; |
| 455 var details; |
| 456 |
| 457 /** |
| 458 * @param {!ScriptMirror|undefined} script |
| 459 * @param {number} pos |
| 460 * @return {?RawLocation} |
| 461 */ |
| 462 function createLocation(script, pos) |
| 463 { |
| 464 if (!script) |
| 465 return null; |
| 466 |
| 467 var location = script.locationFromPosition(pos, true); |
| 468 return { |
| 469 "lineNumber": location.line, |
| 470 "columnNumber": location.column, |
| 471 "scriptId": String(script.id()) |
| 472 } |
| 473 } |
| 474 |
| 475 /** |
| 476 * @return {!Array<!Object>} |
| 477 */ |
| 478 function ensureScopeChain() |
| 479 { |
| 480 if (!scopeChain) { |
| 481 scopeChain = []; |
| 482 scopeStartLocations = []; |
| 483 scopeEndLocations = []; |
| 484 for (var i = 0, j = 0; i < scopeObjects.length; ++i) { |
| 485 var scopeObject = DebuggerScript._buildScopeObject(scopeTypes[i]
, scopeObjects[i]); |
| 486 if (scopeObject) { |
| 487 scopeTypes[j] = scopeTypes[i]; |
| 488 scopeNames[j] = scopeNames[i]; |
| 489 scopeChain[j] = scopeObject; |
| 490 |
| 491 var funcMirror = scopeFunctions ? MakeMirror(scopeFunctions[
i]) : null; |
| 492 if (!funcMirror || !funcMirror.isFunction()) |
| 493 funcMirror = new UnresolvedFunctionMirror(funcObject); |
| 494 |
| 495 var script = /** @type {!FunctionMirror} */(funcMirror).scri
pt(); |
| 496 scopeStartLocations[j] = createLocation(script, scopeStartPo
sitions[i]); |
| 497 scopeEndLocations[j] = createLocation(script, scopeEndPositi
ons[i]); |
| 498 ++j; |
| 499 } |
| 500 } |
| 501 scopeTypes.length = scopeChain.length; |
| 502 scopeNames.length = scopeChain.length; |
| 503 scopeObjects = null; // Free for GC. |
| 504 scopeFunctions = null; |
| 505 scopeStartPositions = null; |
| 506 scopeEndPositions = null; |
| 507 } |
| 508 return scopeChain; |
| 509 } |
| 510 |
| 511 /** |
| 512 * @return {!JavaScriptCallFrameDetails} |
| 513 */ |
| 514 function lazyDetails() |
| 515 { |
| 516 if (!details) { |
| 517 var scopeObjects = ensureScopeChain(); |
| 518 var script = ensureFuncMirror().script(); |
| 519 /** @type {!Array<Scope>} */ |
| 520 var scopes = []; |
| 521 for (var i = 0; i < scopeObjects.length; ++i) { |
| 522 var scope = { |
| 523 "type": /** @type {string} */(DebuggerScript._scopeTypeNames
.get(scopeTypes[i])), |
| 524 "object": scopeObjects[i], |
| 525 }; |
| 526 if (scopeNames[i]) |
| 527 scope.name = scopeNames[i]; |
| 528 if (scopeStartLocations[i]) |
| 529 scope.startLocation = /** @type {!RawLocation} */(scopeStart
Locations[i]); |
| 530 if (scopeEndLocations[i]) |
| 531 scope.endLocation = /** @type {!RawLocation} */(scopeEndLoca
tions[i]); |
| 532 scopes.push(scope); |
| 533 } |
| 534 details = { |
| 535 "functionName": ensureFuncMirror().debugName(), |
| 536 "location": { |
| 537 "lineNumber": line(), |
| 538 "columnNumber": column(), |
| 539 "scriptId": String(script.id()) |
| 540 }, |
| 541 "this": thisObject, |
| 542 "scopeChain": scopes |
| 543 }; |
| 544 var functionLocation = ensureFuncMirror().sourceLocation(); |
| 545 if (functionLocation) { |
| 546 details.functionLocation = { |
| 547 "lineNumber": functionLocation.line, |
| 548 "columnNumber": functionLocation.column, |
| 549 "scriptId": String(script.id()) |
| 550 }; |
| 551 } |
| 552 if (isAtReturn) |
| 553 details.returnValue = returnValue; |
| 554 } |
| 555 return details; |
| 556 } |
| 557 |
| 558 /** |
| 559 * @return {!FunctionMirror} |
| 560 */ |
| 561 function ensureFuncMirror() |
| 562 { |
| 563 if (!funcMirror) { |
| 564 funcMirror = MakeMirror(funcObject); |
| 565 if (!funcMirror.isFunction()) |
| 566 funcMirror = new UnresolvedFunctionMirror(funcObject); |
| 567 } |
| 568 return /** @type {!FunctionMirror} */(funcMirror); |
| 569 } |
| 570 |
| 571 /** |
| 572 * @return {!{line: number, column: number}} |
| 573 */ |
| 574 function ensureLocation() |
| 575 { |
| 576 if (!location) { |
| 577 var script = ensureFuncMirror().script(); |
| 578 if (script) |
| 579 location = script.locationFromPosition(sourcePosition, true); |
| 580 if (!location) |
| 581 location = { line: 0, column: 0 }; |
| 582 } |
| 583 return location; |
| 584 } |
| 585 |
| 586 /** |
| 587 * @return {number} |
| 588 */ |
| 589 function line() |
| 590 { |
| 591 return ensureLocation().line; |
| 592 } |
| 593 |
| 594 /** |
| 595 * @return {number} |
| 596 */ |
| 597 function column() |
| 598 { |
| 599 return ensureLocation().column; |
| 600 } |
| 601 |
| 602 /** |
| 603 * @return {number} |
| 604 */ |
| 605 function contextId() |
| 606 { |
| 607 var mirror = ensureFuncMirror(); |
| 608 // Old V8 do not have context() function on these objects |
| 609 if (!mirror.context) |
| 610 return DebuggerScript._executionContextId(mirror.script().value().co
ntext_data); |
| 611 var context = mirror.context(); |
| 612 if (context) |
| 613 return DebuggerScript._executionContextId(context.data()); |
| 614 return 0; |
| 615 } |
| 616 |
| 617 /** |
| 618 * @return {number|undefined} |
| 619 */ |
| 620 function sourceID() |
| 621 { |
| 622 var script = ensureFuncMirror().script(); |
| 623 return script && script.id(); |
| 624 } |
| 625 |
| 626 /** |
| 627 * @param {string} expression |
| 628 * @return {*} |
| 629 */ |
| 630 function evaluate(expression) |
| 631 { |
| 632 return frameMirror.evaluate(expression, false).value(); |
| 633 } |
| 634 |
| 635 /** @return {undefined} */ |
| 636 function restart() |
| 637 { |
| 638 return frameMirror.restart(); |
| 639 } |
| 640 |
| 641 /** |
| 642 * @param {number} scopeNumber |
| 643 * @param {string} variableName |
| 644 * @param {*} newValue |
| 645 */ |
| 646 function setVariableValue(scopeNumber, variableName, newValue) |
| 647 { |
| 648 var scopeMirror = frameMirror.scope(scopeNumber); |
| 649 if (!scopeMirror) |
| 650 throw new Error("Incorrect scope index"); |
| 651 scopeMirror.setVariableValue(variableName, newValue); |
| 652 } |
| 653 |
| 654 return { |
| 655 "sourceID": sourceID, |
| 656 "line": line, |
| 657 "column": column, |
| 658 "contextId": contextId, |
| 659 "thisObject": thisObject, |
| 660 "evaluate": evaluate, |
| 661 "restart": restart, |
| 662 "setVariableValue": setVariableValue, |
| 663 "isAtReturn": isAtReturn, |
| 664 "details": lazyDetails |
| 665 }; |
| 666 } |
| 667 |
| 668 /** |
| 669 * @param {number} scopeType |
| 670 * @param {!Object} scopeObject |
| 671 * @return {!Object|undefined} |
| 672 */ |
| 673 DebuggerScript._buildScopeObject = function(scopeType, scopeObject) |
| 674 { |
| 675 var result; |
| 676 switch (scopeType) { |
| 677 case ScopeType.Local: |
| 678 case ScopeType.Closure: |
| 679 case ScopeType.Catch: |
| 680 case ScopeType.Block: |
| 681 case ScopeType.Script: |
| 682 // For transient objects we create a "persistent" copy that contains |
| 683 // the same properties. |
| 684 // Reset scope object prototype to null so that the proto properties |
| 685 // don't appear in the local scope section. |
| 686 var properties = /** @type {!ObjectMirror} */(MakeMirror(scopeObject, tr
ue /* transient */)).properties(); |
| 687 // Almost always Script scope will be empty, so just filter out that noi
se. |
| 688 // Also drop empty Block scopes, should we get any. |
| 689 if (!properties.length && (scopeType === ScopeType.Script || scopeType =
== ScopeType.Block)) |
| 690 break; |
| 691 result = { __proto__: null }; |
| 692 for (var j = 0; j < properties.length; j++) { |
| 693 var name = properties[j].name(); |
| 694 if (name.length === 0 || name.charAt(0) === ".") |
| 695 continue; // Skip internal variables like ".arguments" and varia
bles with empty name |
| 696 result[name] = properties[j].value_; |
| 697 } |
| 698 break; |
| 699 case ScopeType.Global: |
| 700 case ScopeType.With: |
| 701 result = scopeObject; |
| 702 break; |
| 703 } |
| 704 return result; |
| 705 } |
| 706 |
| 707 // We never resolve Mirror by its handle so to avoid memory leaks caused by Mirr
ors in the cache we disable it. |
| 708 ToggleMirrorCache(false); |
| 709 |
| 710 return DebuggerScript; |
| 711 })(); |
OLD | NEW |