Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Side by Side Diff: test/debugger/test-api.js

Issue 2497973002: [debug-wrapper] Further extend the debug wrapper (Closed)
Patch Set: Address comments Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « test/debugger/debugger.status ('k') | test/mjsunit/bugs/bug-2337.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 Block: 5, 56 Block: 5,
57 Script: 6, 57 Script: 6,
58 Eval: 7, 58 Eval: 7,
59 Module: 8 59 Module: 8
60 }; 60 };
61 61
62 // Types of exceptions that can be broken upon. 62 // Types of exceptions that can be broken upon.
63 this.ExceptionBreak = { Caught : 0, 63 this.ExceptionBreak = { Caught : 0,
64 Uncaught: 1 }; 64 Uncaught: 1 };
65 65
66 // The different types of breakpoint position alignments.
67 // Must match BreakPositionAlignment in debug.h.
68 this.BreakPositionAlignment = {
69 Statement: 0,
70 BreakPosition: 1
71 };
72
66 // Store the current script id so we can skip corresponding break events. 73 // Store the current script id so we can skip corresponding break events.
67 this.thisScriptId = %FunctionGetScriptId(receive); 74 this.thisScriptId = %FunctionGetScriptId(receive);
68 75
69 // Register as the active wrapper. 76 // Register as the active wrapper.
70 assertTrue(activeWrapper === undefined); 77 assertTrue(activeWrapper === undefined);
71 activeWrapper = this; 78 activeWrapper = this;
72 } 79 }
73 80
74 enable() { this.sendMessageForMethodChecked("Debugger.enable"); } 81 enable() { this.sendMessageForMethodChecked("Debugger.enable"); }
75 disable() { this.sendMessageForMethodChecked("Debugger.disable"); } 82 disable() { this.sendMessageForMethodChecked("Debugger.disable"); }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 return !!%IsBreakOnException(this.ExceptionBreak.Uncaught); 118 return !!%IsBreakOnException(this.ExceptionBreak.Uncaught);
112 }; 119 };
113 120
114 clearStepping() { %ClearStepping(); }; 121 clearStepping() { %ClearStepping(); };
115 122
116 // Returns the resulting breakpoint id. 123 // Returns the resulting breakpoint id.
117 setBreakPoint(func, opt_line, opt_column, opt_condition) { 124 setBreakPoint(func, opt_line, opt_column, opt_condition) {
118 assertTrue(%IsFunction(func)); 125 assertTrue(%IsFunction(func));
119 assertFalse(%FunctionIsAPIFunction(func)); 126 assertFalse(%FunctionIsAPIFunction(func));
120 127
121 // TODO(jgruber): We handle only script breakpoints for now.
122
123 const scriptid = %FunctionGetScriptId(func); 128 const scriptid = %FunctionGetScriptId(func);
124 assertTrue(scriptid != -1); 129 assertTrue(scriptid != -1);
125 130
126 const offset = %FunctionGetScriptSourcePosition(func); 131 const offset = %FunctionGetScriptSourcePosition(func);
127 const loc = 132 const loc =
128 %ScriptLocationFromLine2(scriptid, opt_line, opt_column, offset); 133 %ScriptLocationFromLine2(scriptid, opt_line, opt_column, offset);
129 134
130 const params = { location : 135 const params = { location :
131 { scriptId : scriptid.toString(), 136 { scriptId : scriptid.toString(),
132 lineNumber : loc.line, 137 lineNumber : loc.line,
(...skipping 15 matching lines...) Expand all
148 return breakid; 153 return breakid;
149 } 154 }
150 155
151 clearBreakPoint(breakid) { 156 clearBreakPoint(breakid) {
152 const {msgid, msg} = this.createMessage( 157 const {msgid, msg} = this.createMessage(
153 "Debugger.removeBreakpoint", { breakpointId : breakid }); 158 "Debugger.removeBreakpoint", { breakpointId : breakid });
154 this.sendMessage(msg); 159 this.sendMessage(msg);
155 this.takeReplyChecked(msgid); 160 this.takeReplyChecked(msgid);
156 } 161 }
157 162
158 // Returns the serialized result of the given expression. For example: 163 showBreakPoints(f, opt_position_alignment) {
159 // {"type":"number", "value":33, "description":"33"}. 164 if (!%IsFunction(f)) throw new Error("Not passed a Function");
160 evaluate(frameid, expression) { 165
166 const source = %FunctionGetSourceCode(f);
167 const offset = %FunctionGetScriptSourcePosition(f);
168 const position_alignment = opt_position_alignment === undefined
169 ? this.BreakPositionAlignment.Statement : opt_position_alignment;
170 const locations = %GetBreakLocations(f, position_alignment);
171
172 if (!locations) return source;
173
174 locations.sort(function(x, y) { return x - y; });
175
176 let result = "";
177 let prev_pos = 0;
178 let pos;
179
180 for (var i = 0; i < locations.length; i++) {
181 pos = locations[i] - offset;
182 result += source.slice(prev_pos, pos);
183 result += "[B" + i + "]";
184 prev_pos = pos;
185 }
186
187 pos = source.length;
188 result += source.substring(prev_pos, pos);
189
190 return result;
191 }
192
193 debuggerFlags() {
194 return { breakPointsActive :
195 { setValue : (enabled) => this.setBreakPointsActive(enabled) }
196 };
197 }
198
199 scripts() {
200 // Collect all scripts in the heap.
201 return %DebugGetLoadedScripts();
202 }
203
204 // Returns a Script object. If the parameter is a function the return value
205 // is the script in which the function is defined. If the parameter is a strin g
206 // the return value is the script for which the script name has that string
207 // value. If it is a regexp and there is a unique script whose name matches
208 // we return that, otherwise undefined.
209 findScript(func_or_script_name) {
210 if (%IsFunction(func_or_script_name)) {
211 return %FunctionGetScript(func_or_script_name);
212 } else if (%IsRegExp(func_or_script_name)) {
213 var scripts = this.scripts();
214 var last_result = null;
215 var result_count = 0;
216 for (var i in scripts) {
217 var script = scripts[i];
218 if (func_or_script_name.test(script.name)) {
219 last_result = script;
220 result_count++;
221 }
222 }
223 // Return the unique script matching the regexp. If there are more
224 // than one we don't return a value since there is no good way to
225 // decide which one to return. Returning a "random" one, say the
226 // first, would introduce nondeterminism (or something close to it)
227 // because the order is the heap iteration order.
228 if (result_count == 1) {
229 return last_result;
230 } else {
231 return undefined;
232 }
233 } else {
234 return %GetScript(func_or_script_name);
235 }
236 }
237
238 sourcePosition(f) {
239 if (!%IsFunction(f)) throw new Error("Not passed a Function");
240 return %FunctionGetScriptSourcePosition(f);
241 };
242
243 setBreakPointsActive(enabled) {
161 const {msgid, msg} = this.createMessage( 244 const {msgid, msg} = this.createMessage(
162 "Debugger.evaluateOnCallFrame", 245 "Debugger.setBreakpointsActive", { active : enabled });
163 { callFrameId : frameid,
164 expression : expression
165 });
166 this.sendMessage(msg); 246 this.sendMessage(msg);
167 247 this.takeReplyChecked(msgid);
168 const reply = this.takeReplyChecked(msgid);
169 return reply.result.result;
170 } 248 }
171 249
172 // --- Internal methods. ----------------------------------------------------- 250 // --- Internal methods. -----------------------------------------------------
173 251
174 getNextMessageId() { 252 getNextMessageId() {
175 return this.nextMessageId++; 253 return this.nextMessageId++;
176 } 254 }
177 255
178 createMessage(method, params) { 256 createMessage(method, params) {
179 const id = this.getNextMessageId(); 257 const id = this.getNextMessageId();
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 case "local": return this.ScopeType.Local; 307 case "local": return this.ScopeType.Local;
230 case "with": return this.ScopeType.With; 308 case "with": return this.ScopeType.With;
231 case "closure": return this.ScopeType.Closure; 309 case "closure": return this.ScopeType.Closure;
232 case "catch": return this.ScopeType.Catch; 310 case "catch": return this.ScopeType.Catch;
233 case "block": return this.ScopeType.Block; 311 case "block": return this.ScopeType.Block;
234 case "script": return this.ScopeType.Script; 312 case "script": return this.ScopeType.Script;
235 default: %AbortJS("Unexpected scope type"); 313 default: %AbortJS("Unexpected scope type");
236 } 314 }
237 } 315 }
238 316
317 execStateScopeObjectProperty(serialized_scope, prop) {
318 let found = null;
319 for (let i = 0; i < serialized_scope.length; i++) {
320 const elem = serialized_scope[i];
321 if (elem.name == prop) {
322 found = elem;
323 break;
324 }
325 }
326
327 if (found == null) return { isUndefined : true }
328
329 const val = { value : () => found.value.value };
330 return { value : () => val,
331 isUndefined : () => found.value.type == "undefined"
332 };
333 }
334
239 // Returns an array of property descriptors of the scope object. 335 // Returns an array of property descriptors of the scope object.
240 // This is in contrast to the original API, which simply passed object 336 // This is in contrast to the original API, which simply passed object
241 // mirrors. 337 // mirrors.
242 execStateScopeObject(obj) { 338 execStateScopeObject(obj) {
243 const serialized_scope = this.getProperties(obj.objectId); 339 const serialized_scope = this.getProperties(obj.objectId);
244 const scope = {} 340 const scope = this.propertiesToObject(serialized_scope);
245 const scope_tuples = serialized_scope.forEach((elem) => { 341 return { value : () => scope,
342 property : (prop) =>
343 this.execStateScopeObjectProperty(serialized_scope, prop)
344 };
345 }
346
347 setVariableValue(frame, scope_index, name, value) {
348 const frameid = frame.callFrameId;
349 const {msgid, msg} = this.createMessage(
350 "Debugger.setVariableValue",
351 { callFrameId : frameid,
352 scopeNumber : scope_index,
353 variableName : name,
354 newValue : { value : value }
355 });
356 this.sendMessage(msg);
357 this.takeReplyChecked(msgid);
358 }
359
360 execStateScope(frame, scope_index) {
361 const scope = frame.scopeChain[scope_index];
362 return { scopeType : () => this.execStateScopeType(scope.type),
363 scopeObject : () => this.execStateScopeObject(scope.object),
364 setVariableValue :
365 (name, value) => this.setVariableValue(frame, scope_index,
366 name, value)
367 };
368 }
369
370 // Takes a list of properties as produced by getProperties and turns them
371 // into an object.
372 propertiesToObject(props) {
373 const obj = {}
374 props.forEach((elem) => {
246 const key = elem.name; 375 const key = elem.name;
247 376
248 let value; 377 let value;
249 if (elem.value) { 378 if (elem.value) {
250 // Some properties (e.g. with getters/setters) don't have a value. 379 // Some properties (e.g. with getters/setters) don't have a value.
251 switch (elem.value.type) { 380 switch (elem.value.type) {
252 case "undefined": value = undefined; break; 381 case "undefined": value = undefined; break;
253 default: value = elem.value.value; break; 382 default: value = elem.value.value; break;
254 } 383 }
255 } 384 }
256 385
257 scope[key] = value; 386 obj[key] = value;
258 }) 387 })
259 388
260 return { value : () => scope }; 389 return obj;
261 }
262
263 execStateScope(scope) {
264 return { scopeType : () => this.execStateScopeType(scope.type),
265 scopeObject : () => this.execStateScopeObject(scope.object)
266 };
267 } 390 }
268 391
269 getProperties(objectId) { 392 getProperties(objectId) {
270 const {msgid, msg} = this.createMessage( 393 const {msgid, msg} = this.createMessage(
271 "Runtime.getProperties", { objectId : objectId }); 394 "Runtime.getProperties", { objectId : objectId });
272 this.sendMessage(msg); 395 this.sendMessage(msg);
273 const reply = this.takeReplyChecked(msgid); 396 const reply = this.takeReplyChecked(msgid);
274 return reply.result.result; 397 return reply.result.result;
275 } 398 }
276 399
(...skipping 28 matching lines...) Expand all
305 428
306 let localValue; 429 let localValue;
307 switch (local.value.type) { 430 switch (local.value.type) {
308 case "undefined": localValue = undefined; break; 431 case "undefined": localValue = undefined; break;
309 default: localValue = local.value.value; break; 432 default: localValue = local.value.value; break;
310 } 433 }
311 434
312 return { value : () => localValue }; 435 return { value : () => localValue };
313 } 436 }
314 437
315 execStateFrameEvaluate(frame, expr) { 438 reconstructRemoteObject(obj) {
439 let value = obj.value;
440 if (obj.type == "object") {
441 if (obj.subtype == "error") {
442 const desc = obj.description;
443 switch (obj.className) {
444 case "EvalError": throw new EvalError(desc);
445 case "RangeError": throw new RangeError(desc);
446 case "ReferenceError": throw new ReferenceError(desc);
447 case "SyntaxError": throw new SyntaxError(desc);
448 case "TypeError": throw new TypeError(desc);
449 case "URIError": throw new URIError(desc);
450 default: throw new Error(desc);
451 }
452 } else if (obj.subtype == "array") {
453 const array = [];
454 const props = this.propertiesToObject(
455 this.getProperties(obj.objectId));
456 for (let i = 0; i < props.length; i++) {
457 array[i] = props[i];
458 }
459 value = array;
460 }
461 }
462
463 return { value : () => value,
464 isUndefined : () => obj.type == "undefined"
465 };
466
467 }
468
469 evaluateOnCallFrame(frame, expr) {
316 const frameid = frame.callFrameId; 470 const frameid = frame.callFrameId;
317 const {msgid, msg} = this.createMessage( 471 const {msgid, msg} = this.createMessage(
318 "Debugger.evaluateOnCallFrame", 472 "Debugger.evaluateOnCallFrame",
319 { callFrameId : frameid, 473 { callFrameId : frameid,
320 expression : expr 474 expression : expr
321 }); 475 });
322 this.sendMessage(msg); 476 this.sendMessage(msg);
323 const reply = this.takeReplyChecked(msgid); 477 const reply = this.takeReplyChecked(msgid);
324 478
325 const result = reply.result.result; 479 const result = reply.result.result;
326 if (result.subtype == "error") { 480 return this.reconstructRemoteObject(result);
327 throw new Error(result.description);
328 }
329
330 return { value : () => result.value };
331 } 481 }
332 482
333 execStateFrame(frame) { 483 execStateFrame(frame) {
334 const scriptid = parseInt(frame.location.scriptId); 484 const scriptid = parseInt(frame.location.scriptId);
335 const line = frame.location.lineNumber; 485 const line = frame.location.lineNumber;
336 const column = frame.location.columnNumber; 486 const column = frame.location.columnNumber;
337 const loc = %ScriptLocationFromLine2(scriptid, line, column, 0); 487 const loc = %ScriptLocationFromLine2(scriptid, line, column, 0);
338 const func = { name : () => frame.functionName }; 488 const func = { name : () => frame.functionName };
339 return { sourceLineText : () => loc.sourceText, 489
340 evaluate : (expr) => this.execStateFrameEvaluate(frame, expr), 490 function allScopes() {
491 const scopes = [];
492 for (let i = 0; i < frame.scopeChain.length; i++) {
493 scopes.push(this.execStateScope(frame, i));
494 }
495 return scopes;
496 };
497
498 return { sourceColumn : () => loc.column,
499 sourceLine : () => loc.line + 1,
500 sourceLineText : () => loc.sourceText,
501 evaluate : (expr) => this.evaluateOnCallFrame(frame, expr),
341 functionName : () => frame.functionName, 502 functionName : () => frame.functionName,
342 func : () => func, 503 func : () => func,
343 localCount : () => this.execStateFrameLocalCount(frame), 504 localCount : () => this.execStateFrameLocalCount(frame),
344 localName : (ix) => this.execStateFrameLocalName(frame, ix), 505 localName : (ix) => this.execStateFrameLocalName(frame, ix),
345 localValue: (ix) => this.execStateFrameLocalValue(frame, ix), 506 localValue: (ix) => this.execStateFrameLocalValue(frame, ix),
346 scopeCount : () => frame.scopeChain.length, 507 scopeCount : () => frame.scopeChain.length,
347 scope : (index) => this.execStateScope(frame.scopeChain[index]), 508 scope : (index) => this.execStateScope(frame, index),
348 allScopes : () => frame.scopeChain.map( 509 allScopes : allScopes.bind(this)
349 this.execStateScope.bind(this))
350 }; 510 };
351 } 511 }
352 512
513 eventDataException(params) {
514 switch (params.data.type) {
515 case "string": {
516 return params.data.value;
517 }
518 case "object": {
519 const props = this.getProperties(params.data.objectId);
520 return this.propertiesToObject(props);
521 }
522 default: {
523 return undefined;
524 }
525 }
526 }
527
528 eventDataScriptSource(id) {
529 const {msgid, msg} = this.createMessage(
530 "Debugger.getScriptSource", { scriptId : id });
531 this.sendMessage(msg);
532 const reply = this.takeReplyChecked(msgid);
533 return reply.result.scriptSource;
534 }
535
536 eventDataScriptSetSource(id, src) {
537 const {msgid, msg} = this.createMessage(
538 "Debugger.setScriptSource", { scriptId : id, scriptSource : src });
539 this.sendMessage(msg);
540 this.takeReplyChecked(msgid);
541 }
542
543 eventDataScript(params) {
544 const id = params.scriptId;
545 const name = params.url ? params.url : undefined;
546
547 return { id : () => id,
548 name : () => name,
549 source : () => this.eventDataScriptSource(id),
550 setSource : (src) => this.eventDataScriptSetSource(id, src)
551 };
552 }
553
353 // --- Message handlers. ----------------------------------------------------- 554 // --- Message handlers. -----------------------------------------------------
354 555
355 dispatchMessage(message) { 556 dispatchMessage(message) {
356 const method = message.method; 557 const method = message.method;
357 if (method == "Debugger.paused") { 558 if (method == "Debugger.paused") {
358 this.handleDebuggerPaused(message); 559 this.handleDebuggerPaused(message);
359 } else if (method == "Debugger.scriptParsed") { 560 } else if (method == "Debugger.scriptParsed") {
360 this.handleDebuggerScriptParsed(message); 561 this.handleDebuggerScriptParsed(message);
562 } else if (method == "Debugger.scriptFailedToParse") {
563 this.handleDebuggerScriptFailedToParse(message);
361 } 564 }
362 } 565 }
363 566
364 handleDebuggerPaused(message) { 567 handleDebuggerPaused(message) {
365 const params = message.params; 568 const params = message.params;
366 569
367 var debugEvent; 570 var debugEvent;
368 switch (params.reason) { 571 switch (params.reason) {
369 case "exception": 572 case "exception":
370 case "promiseRejection": 573 case "promiseRejection":
(...skipping 13 matching lines...) Expand all
384 prepareStep : this.execStatePrepareStep.bind(this), 587 prepareStep : this.execStatePrepareStep.bind(this),
385 frame : (index) => this.execStateFrame( 588 frame : (index) => this.execStateFrame(
386 index ? params.callFrames[index] 589 index ? params.callFrames[index]
387 : params.callFrames[0]), 590 : params.callFrames[0]),
388 frameCount : () => params.callFrames.length 591 frameCount : () => params.callFrames.length
389 }; 592 };
390 593
391 let eventData = this.execStateFrame(params.callFrames[0]); 594 let eventData = this.execStateFrame(params.callFrames[0]);
392 if (debugEvent == this.DebugEvent.Exception) { 595 if (debugEvent == this.DebugEvent.Exception) {
393 eventData.uncaught = () => params.data.uncaught; 596 eventData.uncaught = () => params.data.uncaught;
597 eventData.exception = () => this.eventDataException(params);
394 } 598 }
395 599
396 this.invokeListener(debugEvent, execState, eventData); 600 this.invokeListener(debugEvent, execState, eventData);
397 } 601 }
398 602
399 handleDebuggerScriptParsed(message) { 603 handleDebuggerScriptParsed(message) {
400 const params = message.params; 604 const params = message.params;
401 let eventData = { scriptId : params.scriptId, 605 let eventData = { scriptId : params.scriptId,
606 script : () => this.eventDataScript(params),
402 eventType : this.DebugEvent.AfterCompile 607 eventType : this.DebugEvent.AfterCompile
403 } 608 }
404 609
405 // TODO(jgruber): Arguments as needed. Still completely missing exec_state, 610 // TODO(jgruber): Arguments as needed. Still completely missing exec_state,
406 // and eventData used to contain the script mirror instead of its id. 611 // and eventData used to contain the script mirror instead of its id.
407 this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData, 612 this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData,
408 undefined); 613 undefined);
409 } 614 }
410 615
616 handleDebuggerScriptFailedToParse(message) {
617 const params = message.params;
618 let eventData = { scriptId : params.scriptId,
619 script : () => this.eventDataScript(params),
620 eventType : this.DebugEvent.CompileError
621 }
622
623 // TODO(jgruber): Arguments as needed. Still completely missing exec_state,
624 // and eventData used to contain the script mirror instead of its id.
625 this.invokeListener(this.DebugEvent.CompileError, undefined, eventData,
626 undefined);
627 }
628
411 invokeListener(event, exec_state, event_data, data) { 629 invokeListener(event, exec_state, event_data, data) {
412 if (this.listener) { 630 if (this.listener) {
413 this.listener(event, exec_state, event_data, data); 631 this.listener(event, exec_state, event_data, data);
414 } 632 }
415 } 633 }
416 } 634 }
417 635
418 // Simulate the debug object generated by --expose-debug-as debug. 636 // Simulate the debug object generated by --expose-debug-as debug.
419 var debug = { instance : undefined }; 637 var debug = { instance : undefined };
420 638
421 Object.defineProperty(debug, 'Debug', { get: function() { 639 Object.defineProperty(debug, 'Debug', { get: function() {
422 if (!debug.instance) { 640 if (!debug.instance) {
423 debug.instance = new DebugWrapper(); 641 debug.instance = new DebugWrapper();
424 debug.instance.enable(); 642 debug.instance.enable();
425 } 643 }
426 return debug.instance; 644 return debug.instance;
427 }}); 645 }});
428 646
429 Object.defineProperty(debug, 'ScopeType', { get: function() { 647 Object.defineProperty(debug, 'ScopeType', { get: function() {
430 const instance = debug.Debug; 648 const instance = debug.Debug;
431 return instance.ScopeType; 649 return instance.ScopeType;
432 }}); 650 }});
OLDNEW
« no previous file with comments | « test/debugger/debugger.status ('k') | test/mjsunit/bugs/bug-2337.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698