| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 "use strict"; | 5 "use strict"; |
| 6 | 6 |
| 7 // If true, prints all messages sent and received by inspector. | 7 // If true, prints all messages sent and received by inspector. |
| 8 const printProtocolMessages = false; | 8 const printProtocolMessages = false; |
| 9 | 9 |
| 10 // The active wrapper instance. | 10 // The active wrapper instance. |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 return breakid; | 148 return breakid; |
| 149 } | 149 } |
| 150 | 150 |
| 151 clearBreakPoint(breakid) { | 151 clearBreakPoint(breakid) { |
| 152 const {msgid, msg} = this.createMessage( | 152 const {msgid, msg} = this.createMessage( |
| 153 "Debugger.removeBreakpoint", { breakpointId : breakid }); | 153 "Debugger.removeBreakpoint", { breakpointId : breakid }); |
| 154 this.sendMessage(msg); | 154 this.sendMessage(msg); |
| 155 this.takeReplyChecked(msgid); | 155 this.takeReplyChecked(msgid); |
| 156 } | 156 } |
| 157 | 157 |
| 158 // Returns the serialized result of the given expression. For example: | 158 debuggerFlags() { |
| 159 // {"type":"number", "value":33, "description":"33"}. | 159 return { breakPointsActive : |
| 160 evaluate(frameid, expression) { | 160 { setValue : (enabled) => this.setBreakPointsActive(enabled) } |
| 161 }; |
| 162 } |
| 163 |
| 164 scripts() { |
| 165 // Collect all scripts in the heap. |
| 166 return %DebugGetLoadedScripts(); |
| 167 } |
| 168 |
| 169 setBreakPointsActive(enabled) { |
| 161 const {msgid, msg} = this.createMessage( | 170 const {msgid, msg} = this.createMessage( |
| 162 "Debugger.evaluateOnCallFrame", | 171 "Debugger.setBreakpointsActive", { active : enabled }); |
| 163 { callFrameId : frameid, | |
| 164 expression : expression | |
| 165 }); | |
| 166 this.sendMessage(msg); | 172 this.sendMessage(msg); |
| 167 | 173 this.takeReplyChecked(msgid); |
| 168 const reply = this.takeReplyChecked(msgid); | |
| 169 return reply.result.result; | |
| 170 } | 174 } |
| 171 | 175 |
| 172 // --- Internal methods. ----------------------------------------------------- | 176 // --- Internal methods. ----------------------------------------------------- |
| 173 | 177 |
| 174 getNextMessageId() { | 178 getNextMessageId() { |
| 175 return this.nextMessageId++; | 179 return this.nextMessageId++; |
| 176 } | 180 } |
| 177 | 181 |
| 178 createMessage(method, params) { | 182 createMessage(method, params) { |
| 179 const id = this.getNextMessageId(); | 183 const id = this.getNextMessageId(); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 229 case "local": return this.ScopeType.Local; | 233 case "local": return this.ScopeType.Local; |
| 230 case "with": return this.ScopeType.With; | 234 case "with": return this.ScopeType.With; |
| 231 case "closure": return this.ScopeType.Closure; | 235 case "closure": return this.ScopeType.Closure; |
| 232 case "catch": return this.ScopeType.Catch; | 236 case "catch": return this.ScopeType.Catch; |
| 233 case "block": return this.ScopeType.Block; | 237 case "block": return this.ScopeType.Block; |
| 234 case "script": return this.ScopeType.Script; | 238 case "script": return this.ScopeType.Script; |
| 235 default: %AbortJS("Unexpected scope type"); | 239 default: %AbortJS("Unexpected scope type"); |
| 236 } | 240 } |
| 237 } | 241 } |
| 238 | 242 |
| 243 execStateScopeObjectProperty(serialized_scope, prop) { |
| 244 let found = null; |
| 245 for (let i = 0; i < serialized_scope.length; i++) { |
| 246 const elem = serialized_scope[i]; |
| 247 if (elem.name == prop) { |
| 248 found = elem; |
| 249 break; |
| 250 } |
| 251 } |
| 252 |
| 253 if (found == null) return { isUndefined : true } |
| 254 |
| 255 const val = { value : () => found.value.value }; |
| 256 return { value : () => val, |
| 257 isUndefined : () => found.value.type == "undefined" |
| 258 }; |
| 259 } |
| 260 |
| 239 // Returns an array of property descriptors of the scope object. | 261 // Returns an array of property descriptors of the scope object. |
| 240 // This is in contrast to the original API, which simply passed object | 262 // This is in contrast to the original API, which simply passed object |
| 241 // mirrors. | 263 // mirrors. |
| 242 execStateScopeObject(obj) { | 264 execStateScopeObject(obj) { |
| 243 const serialized_scope = this.getProperties(obj.objectId); | 265 const serialized_scope = this.getProperties(obj.objectId); |
| 244 const scope = {} | 266 const scope = this.propertiesToObject(serialized_scope); |
| 245 const scope_tuples = serialized_scope.forEach((elem) => { | 267 return { value : () => scope, |
| 268 property : (prop) => |
| 269 this.execStateScopeObjectProperty(serialized_scope, prop) |
| 270 }; |
| 271 } |
| 272 |
| 273 setVariableValue(frame, scope_index, name, value) { |
| 274 const frameid = frame.callFrameId; |
| 275 const {msgid, msg} = this.createMessage( |
| 276 "Debugger.setVariableValue", |
| 277 { callFrameId : frameid, |
| 278 scopeNumber : scope_index, |
| 279 variableName : name, |
| 280 newValue : { value : value } |
| 281 }); |
| 282 this.sendMessage(msg); |
| 283 this.takeReplyChecked(msgid); |
| 284 } |
| 285 |
| 286 execStateScope(frame, scope_index) { |
| 287 const scope = frame.scopeChain[scope_index]; |
| 288 return { scopeType : () => this.execStateScopeType(scope.type), |
| 289 scopeObject : () => this.execStateScopeObject(scope.object), |
| 290 setVariableValue : |
| 291 (name, value) => this.setVariableValue(frame, scope_index, |
| 292 name, value) |
| 293 }; |
| 294 } |
| 295 |
| 296 // Takes a list of properties as produced by getProperties and turns them |
| 297 // into an object. |
| 298 propertiesToObject(props) { |
| 299 const obj = {} |
| 300 props.forEach((elem) => { |
| 246 const key = elem.name; | 301 const key = elem.name; |
| 247 | 302 |
| 248 let value; | 303 let value; |
| 249 if (elem.value) { | 304 if (elem.value) { |
| 250 // Some properties (e.g. with getters/setters) don't have a value. | 305 // Some properties (e.g. with getters/setters) don't have a value. |
| 251 switch (elem.value.type) { | 306 switch (elem.value.type) { |
| 252 case "undefined": value = undefined; break; | 307 case "undefined": value = undefined; break; |
| 253 default: value = elem.value.value; break; | 308 default: value = elem.value.value; break; |
| 254 } | 309 } |
| 255 } | 310 } |
| 256 | 311 |
| 257 scope[key] = value; | 312 obj[key] = value; |
| 258 }) | 313 }) |
| 259 | 314 |
| 260 return { value : () => scope }; | 315 return obj; |
| 261 } | |
| 262 | |
| 263 execStateScope(scope) { | |
| 264 return { scopeType : () => this.execStateScopeType(scope.type), | |
| 265 scopeObject : () => this.execStateScopeObject(scope.object) | |
| 266 }; | |
| 267 } | 316 } |
| 268 | 317 |
| 269 getProperties(objectId) { | 318 getProperties(objectId) { |
| 270 const {msgid, msg} = this.createMessage( | 319 const {msgid, msg} = this.createMessage( |
| 271 "Runtime.getProperties", { objectId : objectId }); | 320 "Runtime.getProperties", { objectId : objectId }); |
| 272 this.sendMessage(msg); | 321 this.sendMessage(msg); |
| 273 const reply = this.takeReplyChecked(msgid); | 322 const reply = this.takeReplyChecked(msgid); |
| 274 return reply.result.result; | 323 return reply.result.result; |
| 275 } | 324 } |
| 276 | 325 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 305 | 354 |
| 306 let localValue; | 355 let localValue; |
| 307 switch (local.value.type) { | 356 switch (local.value.type) { |
| 308 case "undefined": localValue = undefined; break; | 357 case "undefined": localValue = undefined; break; |
| 309 default: localValue = local.value.value; break; | 358 default: localValue = local.value.value; break; |
| 310 } | 359 } |
| 311 | 360 |
| 312 return { value : () => localValue }; | 361 return { value : () => localValue }; |
| 313 } | 362 } |
| 314 | 363 |
| 315 execStateFrameEvaluate(frame, expr) { | 364 reconstructRemoteObject(obj) { |
| 365 let value = obj.value; |
| 366 if (obj.type == "object") { |
| 367 if (obj.subtype == "error") { |
| 368 const desc = obj.description; |
| 369 switch (obj.className) { |
| 370 case "EvalError": throw new EvalError(desc); |
| 371 case "RangeError": throw new RangeError(desc); |
| 372 case "ReferenceError": throw new ReferenceError(desc); |
| 373 case "SyntaxError": throw new SyntaxError(desc); |
| 374 case "TypeError": throw new TypeError(desc); |
| 375 case "URIError": throw new URIError(desc); |
| 376 default: throw new Error(desc); |
| 377 } |
| 378 } else if (obj.subtype == "array") { |
| 379 const array = []; |
| 380 const props = this.propertiesToObject( |
| 381 this.getProperties(obj.objectId)); |
| 382 for (let i = 0; i < props.length; i++) { |
| 383 array[i] = props[i]; |
| 384 } |
| 385 value = array; |
| 386 } |
| 387 } |
| 388 |
| 389 return { value : () => value, |
| 390 isUndefined : () => obj.type == "undefined" |
| 391 }; |
| 392 |
| 393 } |
| 394 |
| 395 evaluateOnCallFrame(frame, expr) { |
| 316 const frameid = frame.callFrameId; | 396 const frameid = frame.callFrameId; |
| 317 const {msgid, msg} = this.createMessage( | 397 const {msgid, msg} = this.createMessage( |
| 318 "Debugger.evaluateOnCallFrame", | 398 "Debugger.evaluateOnCallFrame", |
| 319 { callFrameId : frameid, | 399 { callFrameId : frameid, |
| 320 expression : expr | 400 expression : expr |
| 321 }); | 401 }); |
| 322 this.sendMessage(msg); | 402 this.sendMessage(msg); |
| 323 const reply = this.takeReplyChecked(msgid); | 403 const reply = this.takeReplyChecked(msgid); |
| 324 | 404 |
| 325 const result = reply.result.result; | 405 const result = reply.result.result; |
| 326 if (result.subtype == "error") { | 406 return this.reconstructRemoteObject(result); |
| 327 throw new Error(result.description); | |
| 328 } | |
| 329 | |
| 330 return { value : () => result.value }; | |
| 331 } | 407 } |
| 332 | 408 |
| 333 execStateFrame(frame) { | 409 execStateFrame(frame) { |
| 334 const scriptid = parseInt(frame.location.scriptId); | 410 const scriptid = parseInt(frame.location.scriptId); |
| 335 const line = frame.location.lineNumber; | 411 const line = frame.location.lineNumber; |
| 336 const column = frame.location.columnNumber; | 412 const column = frame.location.columnNumber; |
| 337 const loc = %ScriptLocationFromLine2(scriptid, line, column, 0); | 413 const loc = %ScriptLocationFromLine2(scriptid, line, column, 0); |
| 338 const func = { name : () => frame.functionName }; | 414 const func = { name : () => frame.functionName }; |
| 339 return { sourceLineText : () => loc.sourceText, | 415 |
| 340 evaluate : (expr) => this.execStateFrameEvaluate(frame, expr), | 416 function allScopes() { |
| 417 const scopes = []; |
| 418 for (let i = 0; i < frame.scopeChain.length; i++) { |
| 419 scopes.push(this.execStateScope(frame, i)); |
| 420 } |
| 421 return scopes; |
| 422 }; |
| 423 |
| 424 return { sourceColumn : () => loc.column, |
| 425 sourceLine : () => loc.line + 1, |
| 426 sourceLineText : () => loc.sourceText, |
| 427 evaluate : (expr) => this.evaluateOnCallFrame(frame, expr), |
| 341 functionName : () => frame.functionName, | 428 functionName : () => frame.functionName, |
| 342 func : () => func, | 429 func : () => func, |
| 343 localCount : () => this.execStateFrameLocalCount(frame), | 430 localCount : () => this.execStateFrameLocalCount(frame), |
| 344 localName : (ix) => this.execStateFrameLocalName(frame, ix), | 431 localName : (ix) => this.execStateFrameLocalName(frame, ix), |
| 345 localValue: (ix) => this.execStateFrameLocalValue(frame, ix), | 432 localValue: (ix) => this.execStateFrameLocalValue(frame, ix), |
| 346 scopeCount : () => frame.scopeChain.length, | 433 scopeCount : () => frame.scopeChain.length, |
| 347 scope : (index) => this.execStateScope(frame.scopeChain[index]), | 434 scope : (index) => this.execStateScope(frame, index), |
| 348 allScopes : () => frame.scopeChain.map( | 435 allScopes : allScopes.bind(this) |
| 349 this.execStateScope.bind(this)) | |
| 350 }; | 436 }; |
| 351 } | 437 } |
| 352 | 438 |
| 439 eventDataException(params) { |
| 440 switch (params.data.type) { |
| 441 case "string": { |
| 442 return params.data.value; |
| 443 } |
| 444 case "object": { |
| 445 const props = this.getProperties(params.data.objectId); |
| 446 return this.propertiesToObject(props); |
| 447 } |
| 448 default: { |
| 449 return undefined; |
| 450 } |
| 451 } |
| 452 } |
| 453 |
| 454 eventDataScriptSource(id) { |
| 455 const {msgid, msg} = this.createMessage( |
| 456 "Debugger.getScriptSource", { scriptId : id }); |
| 457 this.sendMessage(msg); |
| 458 const reply = this.takeReplyChecked(msgid); |
| 459 return reply.result.scriptSource; |
| 460 } |
| 461 |
| 462 eventDataScriptSetSource(id, src) { |
| 463 const {msgid, msg} = this.createMessage( |
| 464 "Debugger.setScriptSource", { scriptId : id, scriptSource : src }); |
| 465 this.sendMessage(msg); |
| 466 this.takeReplyChecked(msgid); |
| 467 } |
| 468 |
| 469 eventDataScript(params) { |
| 470 const id = params.scriptId; |
| 471 const name = params.url ? params.url : undefined; |
| 472 |
| 473 return { id : () => id, |
| 474 name : () => name, |
| 475 source : () => this.eventDataScriptSource(id), |
| 476 setSource : (src) => this.eventDataScriptSetSource(id, src) |
| 477 }; |
| 478 } |
| 479 |
| 353 // --- Message handlers. ----------------------------------------------------- | 480 // --- Message handlers. ----------------------------------------------------- |
| 354 | 481 |
| 355 dispatchMessage(message) { | 482 dispatchMessage(message) { |
| 356 const method = message.method; | 483 const method = message.method; |
| 357 if (method == "Debugger.paused") { | 484 if (method == "Debugger.paused") { |
| 358 this.handleDebuggerPaused(message); | 485 this.handleDebuggerPaused(message); |
| 359 } else if (method == "Debugger.scriptParsed") { | 486 } else if (method == "Debugger.scriptParsed") { |
| 360 this.handleDebuggerScriptParsed(message); | 487 this.handleDebuggerScriptParsed(message); |
| 488 } else if (method == "Debugger.scriptFailedToParse") { |
| 489 this.handleDebuggerScriptFailedToParse(message); |
| 361 } | 490 } |
| 362 } | 491 } |
| 363 | 492 |
| 364 handleDebuggerPaused(message) { | 493 handleDebuggerPaused(message) { |
| 365 const params = message.params; | 494 const params = message.params; |
| 366 | 495 |
| 367 var debugEvent; | 496 var debugEvent; |
| 368 switch (params.reason) { | 497 switch (params.reason) { |
| 369 case "exception": | 498 case "exception": |
| 370 case "promiseRejection": | 499 case "promiseRejection": |
| (...skipping 13 matching lines...) Expand all Loading... |
| 384 prepareStep : this.execStatePrepareStep.bind(this), | 513 prepareStep : this.execStatePrepareStep.bind(this), |
| 385 frame : (index) => this.execStateFrame( | 514 frame : (index) => this.execStateFrame( |
| 386 index ? params.callFrames[index] | 515 index ? params.callFrames[index] |
| 387 : params.callFrames[0]), | 516 : params.callFrames[0]), |
| 388 frameCount : () => params.callFrames.length | 517 frameCount : () => params.callFrames.length |
| 389 }; | 518 }; |
| 390 | 519 |
| 391 let eventData = this.execStateFrame(params.callFrames[0]); | 520 let eventData = this.execStateFrame(params.callFrames[0]); |
| 392 if (debugEvent == this.DebugEvent.Exception) { | 521 if (debugEvent == this.DebugEvent.Exception) { |
| 393 eventData.uncaught = () => params.data.uncaught; | 522 eventData.uncaught = () => params.data.uncaught; |
| 523 eventData.exception = () => this.eventDataException(params); |
| 394 } | 524 } |
| 395 | 525 |
| 396 this.invokeListener(debugEvent, execState, eventData); | 526 this.invokeListener(debugEvent, execState, eventData); |
| 397 } | 527 } |
| 398 | 528 |
| 399 handleDebuggerScriptParsed(message) { | 529 handleDebuggerScriptParsed(message) { |
| 400 const params = message.params; | 530 const params = message.params; |
| 401 let eventData = { scriptId : params.scriptId, | 531 let eventData = { scriptId : params.scriptId, |
| 532 script : () => this.eventDataScript(params), |
| 402 eventType : this.DebugEvent.AfterCompile | 533 eventType : this.DebugEvent.AfterCompile |
| 403 } | 534 } |
| 404 | 535 |
| 405 // TODO(jgruber): Arguments as needed. Still completely missing exec_state, | 536 // TODO(jgruber): Arguments as needed. Still completely missing exec_state, |
| 406 // and eventData used to contain the script mirror instead of its id. | 537 // and eventData used to contain the script mirror instead of its id. |
| 407 this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData, | 538 this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData, |
| 408 undefined); | 539 undefined); |
| 409 } | 540 } |
| 410 | 541 |
| 542 handleDebuggerScriptFailedToParse(message) { |
| 543 const params = message.params; |
| 544 let eventData = { scriptId : params.scriptId, |
| 545 script : () => this.eventDataScript(params), |
| 546 eventType : this.DebugEvent.CompileError |
| 547 } |
| 548 |
| 549 // TODO(jgruber): Arguments as needed. Still completely missing exec_state, |
| 550 // and eventData used to contain the script mirror instead of its id. |
| 551 this.invokeListener(this.DebugEvent.CompileError, undefined, eventData, |
| 552 undefined); |
| 553 } |
| 554 |
| 411 invokeListener(event, exec_state, event_data, data) { | 555 invokeListener(event, exec_state, event_data, data) { |
| 412 if (this.listener) { | 556 if (this.listener) { |
| 413 this.listener(event, exec_state, event_data, data); | 557 this.listener(event, exec_state, event_data, data); |
| 414 } | 558 } |
| 415 } | 559 } |
| 416 } | 560 } |
| 417 | 561 |
| 418 // Simulate the debug object generated by --expose-debug-as debug. | 562 // Simulate the debug object generated by --expose-debug-as debug. |
| 419 var debug = { instance : undefined }; | 563 var debug = { instance : undefined }; |
| 420 | 564 |
| 421 Object.defineProperty(debug, 'Debug', { get: function() { | 565 Object.defineProperty(debug, 'Debug', { get: function() { |
| 422 if (!debug.instance) { | 566 if (!debug.instance) { |
| 423 debug.instance = new DebugWrapper(); | 567 debug.instance = new DebugWrapper(); |
| 424 debug.instance.enable(); | 568 debug.instance.enable(); |
| 425 } | 569 } |
| 426 return debug.instance; | 570 return debug.instance; |
| 427 }}); | 571 }}); |
| 428 | 572 |
| 429 Object.defineProperty(debug, 'ScopeType', { get: function() { | 573 Object.defineProperty(debug, 'ScopeType', { get: function() { |
| 430 const instance = debug.Debug; | 574 const instance = debug.Debug; |
| 431 return instance.ScopeType; | 575 return instance.ScopeType; |
| 432 }}); | 576 }}); |
| OLD | NEW |