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 |