OLD | NEW |
| (Empty) |
1 // Copyright 2012 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 "use strict"; | |
5 | |
6 // Default number of frames to include in the response to backtrace request. | |
7 var kDefaultBacktraceLength = 10; | |
8 | |
9 var Debug = {}; | |
10 | |
11 // Regular expression to skip "crud" at the beginning of a source line which is | |
12 // not really code. Currently the regular expression matches whitespace and | |
13 // comments. | |
14 var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/; | |
15 | |
16 // Debug events which can occour in the V8 JavaScript engine. These originate | |
17 // from the API include file debug.h. | |
18 Debug.DebugEvent = { Break: 1, | |
19 Exception: 2, | |
20 NewFunction: 3, | |
21 BeforeCompile: 4, | |
22 AfterCompile: 5, | |
23 CompileError: 6, | |
24 PromiseEvent: 7, | |
25 AsyncTaskEvent: 8 }; | |
26 | |
27 // Types of exceptions that can be broken upon. | |
28 Debug.ExceptionBreak = { Caught : 0, | |
29 Uncaught: 1 }; | |
30 | |
31 // The different types of steps. | |
32 Debug.StepAction = { StepOut: 0, | |
33 StepNext: 1, | |
34 StepIn: 2, | |
35 StepMin: 3, | |
36 StepInMin: 4, | |
37 StepFrame: 5 }; | |
38 | |
39 // The different types of scripts matching enum ScriptType in objects.h. | |
40 Debug.ScriptType = { Native: 0, | |
41 Extension: 1, | |
42 Normal: 2 }; | |
43 | |
44 // The different types of script compilations matching enum | |
45 // Script::CompilationType in objects.h. | |
46 Debug.ScriptCompilationType = { Host: 0, | |
47 Eval: 1, | |
48 JSON: 2 }; | |
49 | |
50 // The different script break point types. | |
51 Debug.ScriptBreakPointType = { ScriptId: 0, | |
52 ScriptName: 1, | |
53 ScriptRegExp: 2 }; | |
54 | |
55 // The different types of breakpoint position alignments. | |
56 // Must match BreakPositionAlignment in debug.h. | |
57 Debug.BreakPositionAlignment = { | |
58 Statement: 0, | |
59 BreakPosition: 1 | |
60 }; | |
61 | |
62 function ScriptTypeFlag(type) { | |
63 return (1 << type); | |
64 } | |
65 | |
66 // Globals. | |
67 var next_response_seq = 0; | |
68 var next_break_point_number = 1; | |
69 var break_points = []; | |
70 var script_break_points = []; | |
71 var debugger_flags = { | |
72 breakPointsActive: { | |
73 value: true, | |
74 getValue: function() { return this.value; }, | |
75 setValue: function(value) { | |
76 this.value = !!value; | |
77 %SetDisableBreak(!this.value); | |
78 } | |
79 }, | |
80 breakOnCaughtException: { | |
81 getValue: function() { return Debug.isBreakOnException(); }, | |
82 setValue: function(value) { | |
83 if (value) { | |
84 Debug.setBreakOnException(); | |
85 } else { | |
86 Debug.clearBreakOnException(); | |
87 } | |
88 } | |
89 }, | |
90 breakOnUncaughtException: { | |
91 getValue: function() { return Debug.isBreakOnUncaughtException(); }, | |
92 setValue: function(value) { | |
93 if (value) { | |
94 Debug.setBreakOnUncaughtException(); | |
95 } else { | |
96 Debug.clearBreakOnUncaughtException(); | |
97 } | |
98 } | |
99 }, | |
100 }; | |
101 | |
102 | |
103 // Create a new break point object and add it to the list of break points. | |
104 function MakeBreakPoint(source_position, opt_script_break_point) { | |
105 var break_point = new BreakPoint(source_position, opt_script_break_point); | |
106 break_points.push(break_point); | |
107 return break_point; | |
108 } | |
109 | |
110 | |
111 // Object representing a break point. | |
112 // NOTE: This object does not have a reference to the function having break | |
113 // point as this would cause function not to be garbage collected when it is | |
114 // not used any more. We do not want break points to keep functions alive. | |
115 function BreakPoint(source_position, opt_script_break_point) { | |
116 this.source_position_ = source_position; | |
117 if (opt_script_break_point) { | |
118 this.script_break_point_ = opt_script_break_point; | |
119 } else { | |
120 this.number_ = next_break_point_number++; | |
121 } | |
122 this.hit_count_ = 0; | |
123 this.active_ = true; | |
124 this.condition_ = null; | |
125 this.ignoreCount_ = 0; | |
126 } | |
127 | |
128 | |
129 BreakPoint.prototype.number = function() { | |
130 return this.number_; | |
131 }; | |
132 | |
133 | |
134 BreakPoint.prototype.func = function() { | |
135 return this.func_; | |
136 }; | |
137 | |
138 | |
139 BreakPoint.prototype.source_position = function() { | |
140 return this.source_position_; | |
141 }; | |
142 | |
143 | |
144 BreakPoint.prototype.hit_count = function() { | |
145 return this.hit_count_; | |
146 }; | |
147 | |
148 | |
149 BreakPoint.prototype.active = function() { | |
150 if (this.script_break_point()) { | |
151 return this.script_break_point().active(); | |
152 } | |
153 return this.active_; | |
154 }; | |
155 | |
156 | |
157 BreakPoint.prototype.condition = function() { | |
158 if (this.script_break_point() && this.script_break_point().condition()) { | |
159 return this.script_break_point().condition(); | |
160 } | |
161 return this.condition_; | |
162 }; | |
163 | |
164 | |
165 BreakPoint.prototype.ignoreCount = function() { | |
166 return this.ignoreCount_; | |
167 }; | |
168 | |
169 | |
170 BreakPoint.prototype.script_break_point = function() { | |
171 return this.script_break_point_; | |
172 }; | |
173 | |
174 | |
175 BreakPoint.prototype.enable = function() { | |
176 this.active_ = true; | |
177 }; | |
178 | |
179 | |
180 BreakPoint.prototype.disable = function() { | |
181 this.active_ = false; | |
182 }; | |
183 | |
184 | |
185 BreakPoint.prototype.setCondition = function(condition) { | |
186 this.condition_ = condition; | |
187 }; | |
188 | |
189 | |
190 BreakPoint.prototype.setIgnoreCount = function(ignoreCount) { | |
191 this.ignoreCount_ = ignoreCount; | |
192 }; | |
193 | |
194 | |
195 BreakPoint.prototype.isTriggered = function(exec_state) { | |
196 // Break point not active - not triggered. | |
197 if (!this.active()) return false; | |
198 | |
199 // Check for conditional break point. | |
200 if (this.condition()) { | |
201 // If break point has condition try to evaluate it in the top frame. | |
202 try { | |
203 var mirror = exec_state.frame(0).evaluate(this.condition()); | |
204 // If no sensible mirror or non true value break point not triggered. | |
205 if (!(mirror instanceof ValueMirror) || | |
206 !builtins.$toBoolean(mirror.value_)) { | |
207 return false; | |
208 } | |
209 } catch (e) { | |
210 // Exception evaluating condition counts as not triggered. | |
211 return false; | |
212 } | |
213 } | |
214 | |
215 // Update the hit count. | |
216 this.hit_count_++; | |
217 if (this.script_break_point_) { | |
218 this.script_break_point_.hit_count_++; | |
219 } | |
220 | |
221 // If the break point has an ignore count it is not triggered. | |
222 if (this.ignoreCount_ > 0) { | |
223 this.ignoreCount_--; | |
224 return false; | |
225 } | |
226 | |
227 // Break point triggered. | |
228 return true; | |
229 }; | |
230 | |
231 | |
232 // Function called from the runtime when a break point is hit. Returns true if | |
233 // the break point is triggered and supposed to break execution. | |
234 function IsBreakPointTriggered(break_id, break_point) { | |
235 return break_point.isTriggered(MakeExecutionState(break_id)); | |
236 } | |
237 | |
238 | |
239 // Object representing a script break point. The script is referenced by its | |
240 // script name or script id and the break point is represented as line and | |
241 // column. | |
242 function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, | |
243 opt_groupId, opt_position_alignment) { | |
244 this.type_ = type; | |
245 if (type == Debug.ScriptBreakPointType.ScriptId) { | |
246 this.script_id_ = script_id_or_name; | |
247 } else if (type == Debug.ScriptBreakPointType.ScriptName) { | |
248 this.script_name_ = script_id_or_name; | |
249 } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) { | |
250 this.script_regexp_object_ = new RegExp(script_id_or_name); | |
251 } else { | |
252 throw new Error("Unexpected breakpoint type " + type); | |
253 } | |
254 this.line_ = opt_line || 0; | |
255 this.column_ = opt_column; | |
256 this.groupId_ = opt_groupId; | |
257 this.position_alignment_ = IS_UNDEFINED(opt_position_alignment) | |
258 ? Debug.BreakPositionAlignment.Statement : opt_position_alignment; | |
259 this.hit_count_ = 0; | |
260 this.active_ = true; | |
261 this.condition_ = null; | |
262 this.ignoreCount_ = 0; | |
263 this.break_points_ = []; | |
264 } | |
265 | |
266 | |
267 // Creates a clone of script breakpoint that is linked to another script. | |
268 ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) { | |
269 var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, | |
270 other_script.id, this.line_, this.column_, this.groupId_, | |
271 this.position_alignment_); | |
272 copy.number_ = next_break_point_number++; | |
273 script_break_points.push(copy); | |
274 | |
275 copy.hit_count_ = this.hit_count_; | |
276 copy.active_ = this.active_; | |
277 copy.condition_ = this.condition_; | |
278 copy.ignoreCount_ = this.ignoreCount_; | |
279 return copy; | |
280 }; | |
281 | |
282 | |
283 ScriptBreakPoint.prototype.number = function() { | |
284 return this.number_; | |
285 }; | |
286 | |
287 | |
288 ScriptBreakPoint.prototype.groupId = function() { | |
289 return this.groupId_; | |
290 }; | |
291 | |
292 | |
293 ScriptBreakPoint.prototype.type = function() { | |
294 return this.type_; | |
295 }; | |
296 | |
297 | |
298 ScriptBreakPoint.prototype.script_id = function() { | |
299 return this.script_id_; | |
300 }; | |
301 | |
302 | |
303 ScriptBreakPoint.prototype.script_name = function() { | |
304 return this.script_name_; | |
305 }; | |
306 | |
307 | |
308 ScriptBreakPoint.prototype.script_regexp_object = function() { | |
309 return this.script_regexp_object_; | |
310 }; | |
311 | |
312 | |
313 ScriptBreakPoint.prototype.line = function() { | |
314 return this.line_; | |
315 }; | |
316 | |
317 | |
318 ScriptBreakPoint.prototype.column = function() { | |
319 return this.column_; | |
320 }; | |
321 | |
322 | |
323 ScriptBreakPoint.prototype.actual_locations = function() { | |
324 var locations = []; | |
325 for (var i = 0; i < this.break_points_.length; i++) { | |
326 locations.push(this.break_points_[i].actual_location); | |
327 } | |
328 return locations; | |
329 }; | |
330 | |
331 | |
332 ScriptBreakPoint.prototype.update_positions = function(line, column) { | |
333 this.line_ = line; | |
334 this.column_ = column; | |
335 }; | |
336 | |
337 | |
338 ScriptBreakPoint.prototype.hit_count = function() { | |
339 return this.hit_count_; | |
340 }; | |
341 | |
342 | |
343 ScriptBreakPoint.prototype.active = function() { | |
344 return this.active_; | |
345 }; | |
346 | |
347 | |
348 ScriptBreakPoint.prototype.condition = function() { | |
349 return this.condition_; | |
350 }; | |
351 | |
352 | |
353 ScriptBreakPoint.prototype.ignoreCount = function() { | |
354 return this.ignoreCount_; | |
355 }; | |
356 | |
357 | |
358 ScriptBreakPoint.prototype.enable = function() { | |
359 this.active_ = true; | |
360 }; | |
361 | |
362 | |
363 ScriptBreakPoint.prototype.disable = function() { | |
364 this.active_ = false; | |
365 }; | |
366 | |
367 | |
368 ScriptBreakPoint.prototype.setCondition = function(condition) { | |
369 this.condition_ = condition; | |
370 }; | |
371 | |
372 | |
373 ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) { | |
374 this.ignoreCount_ = ignoreCount; | |
375 | |
376 // Set ignore count on all break points created from this script break point. | |
377 for (var i = 0; i < this.break_points_.length; i++) { | |
378 this.break_points_[i].setIgnoreCount(ignoreCount); | |
379 } | |
380 }; | |
381 | |
382 | |
383 // Check whether a script matches this script break point. Currently this is | |
384 // only based on script name. | |
385 ScriptBreakPoint.prototype.matchesScript = function(script) { | |
386 if (this.type_ == Debug.ScriptBreakPointType.ScriptId) { | |
387 return this.script_id_ == script.id; | |
388 } else { | |
389 // We might want to account columns here as well. | |
390 if (!(script.line_offset <= this.line_ && | |
391 this.line_ < script.line_offset + script.lineCount())) { | |
392 return false; | |
393 } | |
394 if (this.type_ == Debug.ScriptBreakPointType.ScriptName) { | |
395 return this.script_name_ == script.nameOrSourceURL(); | |
396 } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) { | |
397 return this.script_regexp_object_.test(script.nameOrSourceURL()); | |
398 } else { | |
399 throw new Error("Unexpected breakpoint type " + this.type_); | |
400 } | |
401 } | |
402 }; | |
403 | |
404 | |
405 // Set the script break point in a script. | |
406 ScriptBreakPoint.prototype.set = function (script) { | |
407 var column = this.column(); | |
408 var line = this.line(); | |
409 // If the column is undefined the break is on the line. To help locate the | |
410 // first piece of breakable code on the line try to find the column on the | |
411 // line which contains some source. | |
412 if (IS_UNDEFINED(column)) { | |
413 var source_line = script.sourceLine(this.line()); | |
414 | |
415 // Allocate array for caching the columns where the actual source starts. | |
416 if (!script.sourceColumnStart_) { | |
417 script.sourceColumnStart_ = new Array(script.lineCount()); | |
418 } | |
419 | |
420 // Fill cache if needed and get column where the actual source starts. | |
421 if (IS_UNDEFINED(script.sourceColumnStart_[line])) { | |
422 script.sourceColumnStart_[line] = | |
423 source_line.match(sourceLineBeginningSkip)[0].length; | |
424 } | |
425 column = script.sourceColumnStart_[line]; | |
426 } | |
427 | |
428 // Convert the line and column into an absolute position within the script. | |
429 var position = Debug.findScriptSourcePosition(script, this.line(), column); | |
430 | |
431 // If the position is not found in the script (the script might be shorter | |
432 // than it used to be) just ignore it. | |
433 if (IS_NULL(position)) return; | |
434 | |
435 // Create a break point object and set the break point. | |
436 var break_point = MakeBreakPoint(position, this); | |
437 break_point.setIgnoreCount(this.ignoreCount()); | |
438 var actual_position = %SetScriptBreakPoint(script, position, | |
439 this.position_alignment_, | |
440 break_point); | |
441 if (IS_UNDEFINED(actual_position)) { | |
442 actual_position = position; | |
443 } | |
444 var actual_location = script.locationFromPosition(actual_position, true); | |
445 break_point.actual_location = { line: actual_location.line, | |
446 column: actual_location.column, | |
447 script_id: script.id }; | |
448 this.break_points_.push(break_point); | |
449 return break_point; | |
450 }; | |
451 | |
452 | |
453 // Clear all the break points created from this script break point | |
454 ScriptBreakPoint.prototype.clear = function () { | |
455 var remaining_break_points = []; | |
456 for (var i = 0; i < break_points.length; i++) { | |
457 if (break_points[i].script_break_point() && | |
458 break_points[i].script_break_point() === this) { | |
459 %ClearBreakPoint(break_points[i]); | |
460 } else { | |
461 remaining_break_points.push(break_points[i]); | |
462 } | |
463 } | |
464 break_points = remaining_break_points; | |
465 this.break_points_ = []; | |
466 }; | |
467 | |
468 | |
469 // Function called from runtime when a new script is compiled to set any script | |
470 // break points set in this script. | |
471 function UpdateScriptBreakPoints(script) { | |
472 for (var i = 0; i < script_break_points.length; i++) { | |
473 var break_point = script_break_points[i]; | |
474 if ((break_point.type() == Debug.ScriptBreakPointType.ScriptName || | |
475 break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) && | |
476 break_point.matchesScript(script)) { | |
477 break_point.set(script); | |
478 } | |
479 } | |
480 } | |
481 | |
482 | |
483 function GetScriptBreakPoints(script) { | |
484 var result = []; | |
485 for (var i = 0; i < script_break_points.length; i++) { | |
486 if (script_break_points[i].matchesScript(script)) { | |
487 result.push(script_break_points[i]); | |
488 } | |
489 } | |
490 return result; | |
491 } | |
492 | |
493 | |
494 Debug.setListener = function(listener, opt_data) { | |
495 if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) { | |
496 throw new Error('Parameters have wrong types.'); | |
497 } | |
498 %SetDebugEventListener(listener, opt_data); | |
499 }; | |
500 | |
501 | |
502 Debug.breakLocations = function(f, opt_position_aligment) { | |
503 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); | |
504 var position_aligment = IS_UNDEFINED(opt_position_aligment) | |
505 ? Debug.BreakPositionAlignment.Statement : opt_position_aligment; | |
506 return %GetBreakLocations(f, position_aligment); | |
507 }; | |
508 | |
509 // Returns a Script object. If the parameter is a function the return value | |
510 // is the script in which the function is defined. If the parameter is a string | |
511 // the return value is the script for which the script name has that string | |
512 // value. If it is a regexp and there is a unique script whose name matches | |
513 // we return that, otherwise undefined. | |
514 Debug.findScript = function(func_or_script_name) { | |
515 if (IS_FUNCTION(func_or_script_name)) { | |
516 return %FunctionGetScript(func_or_script_name); | |
517 } else if (IS_REGEXP(func_or_script_name)) { | |
518 var scripts = Debug.scripts(); | |
519 var last_result = null; | |
520 var result_count = 0; | |
521 for (var i in scripts) { | |
522 var script = scripts[i]; | |
523 if (func_or_script_name.test(script.name)) { | |
524 last_result = script; | |
525 result_count++; | |
526 } | |
527 } | |
528 // Return the unique script matching the regexp. If there are more | |
529 // than one we don't return a value since there is no good way to | |
530 // decide which one to return. Returning a "random" one, say the | |
531 // first, would introduce nondeterminism (or something close to it) | |
532 // because the order is the heap iteration order. | |
533 if (result_count == 1) { | |
534 return last_result; | |
535 } else { | |
536 return undefined; | |
537 } | |
538 } else { | |
539 return %GetScript(func_or_script_name); | |
540 } | |
541 }; | |
542 | |
543 // Returns the script source. If the parameter is a function the return value | |
544 // is the script source for the script in which the function is defined. If the | |
545 // parameter is a string the return value is the script for which the script | |
546 // name has that string value. | |
547 Debug.scriptSource = function(func_or_script_name) { | |
548 return this.findScript(func_or_script_name).source; | |
549 }; | |
550 | |
551 | |
552 Debug.source = function(f) { | |
553 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); | |
554 return %FunctionGetSourceCode(f); | |
555 }; | |
556 | |
557 | |
558 Debug.sourcePosition = function(f) { | |
559 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); | |
560 return %FunctionGetScriptSourcePosition(f); | |
561 }; | |
562 | |
563 | |
564 Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { | |
565 var script = %FunctionGetScript(func); | |
566 var script_offset = %FunctionGetScriptSourcePosition(func); | |
567 return script.locationFromLine(opt_line, opt_column, script_offset); | |
568 }; | |
569 | |
570 | |
571 // Returns the character position in a script based on a line number and an | |
572 // optional position within that line. | |
573 Debug.findScriptSourcePosition = function(script, opt_line, opt_column) { | |
574 var location = script.locationFromLine(opt_line, opt_column); | |
575 return location ? location.position : null; | |
576 }; | |
577 | |
578 | |
579 Debug.findBreakPoint = function(break_point_number, remove) { | |
580 var break_point; | |
581 for (var i = 0; i < break_points.length; i++) { | |
582 if (break_points[i].number() == break_point_number) { | |
583 break_point = break_points[i]; | |
584 // Remove the break point from the list if requested. | |
585 if (remove) { | |
586 break_points.splice(i, 1); | |
587 } | |
588 break; | |
589 } | |
590 } | |
591 if (break_point) { | |
592 return break_point; | |
593 } else { | |
594 return this.findScriptBreakPoint(break_point_number, remove); | |
595 } | |
596 }; | |
597 | |
598 Debug.findBreakPointActualLocations = function(break_point_number) { | |
599 for (var i = 0; i < script_break_points.length; i++) { | |
600 if (script_break_points[i].number() == break_point_number) { | |
601 return script_break_points[i].actual_locations(); | |
602 } | |
603 } | |
604 for (var i = 0; i < break_points.length; i++) { | |
605 if (break_points[i].number() == break_point_number) { | |
606 return [break_points[i].actual_location]; | |
607 } | |
608 } | |
609 return []; | |
610 }; | |
611 | |
612 Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) { | |
613 if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.'); | |
614 // Break points in API functions are not supported. | |
615 if (%FunctionIsAPIFunction(func)) { | |
616 throw new Error('Cannot set break point in native code.'); | |
617 } | |
618 // Find source position relative to start of the function | |
619 var break_position = | |
620 this.findFunctionSourceLocation(func, opt_line, opt_column).position; | |
621 var source_position = break_position - this.sourcePosition(func); | |
622 // Find the script for the function. | |
623 var script = %FunctionGetScript(func); | |
624 // Break in builtin JavaScript code is not supported. | |
625 if (script.type == Debug.ScriptType.Native) { | |
626 throw new Error('Cannot set break point in native code.'); | |
627 } | |
628 // If the script for the function has a name convert this to a script break | |
629 // point. | |
630 if (script && script.id) { | |
631 // Adjust the source position to be script relative. | |
632 source_position += %FunctionGetScriptSourcePosition(func); | |
633 // Find line and column for the position in the script and set a script | |
634 // break point from that. | |
635 var location = script.locationFromPosition(source_position, false); | |
636 return this.setScriptBreakPointById(script.id, | |
637 location.line, location.column, | |
638 opt_condition); | |
639 } else { | |
640 // Set a break point directly on the function. | |
641 var break_point = MakeBreakPoint(source_position); | |
642 var actual_position = | |
643 %SetFunctionBreakPoint(func, source_position, break_point); | |
644 actual_position += this.sourcePosition(func); | |
645 var actual_location = script.locationFromPosition(actual_position, true); | |
646 break_point.actual_location = { line: actual_location.line, | |
647 column: actual_location.column, | |
648 script_id: script.id }; | |
649 break_point.setCondition(opt_condition); | |
650 return break_point.number(); | |
651 } | |
652 }; | |
653 | |
654 | |
655 Debug.setBreakPointByScriptIdAndPosition = function(script_id, position, | |
656 condition, enabled, | |
657 opt_position_alignment) | |
658 { | |
659 var break_point = MakeBreakPoint(position); | |
660 break_point.setCondition(condition); | |
661 if (!enabled) { | |
662 break_point.disable(); | |
663 } | |
664 var scripts = this.scripts(); | |
665 var position_alignment = IS_UNDEFINED(opt_position_alignment) | |
666 ? Debug.BreakPositionAlignment.Statement : opt_position_alignment; | |
667 for (var i = 0; i < scripts.length; i++) { | |
668 if (script_id == scripts[i].id) { | |
669 break_point.actual_position = %SetScriptBreakPoint(scripts[i], position, | |
670 position_alignment, break_point); | |
671 break; | |
672 } | |
673 } | |
674 return break_point; | |
675 }; | |
676 | |
677 | |
678 Debug.enableBreakPoint = function(break_point_number) { | |
679 var break_point = this.findBreakPoint(break_point_number, false); | |
680 // Only enable if the breakpoint hasn't been deleted: | |
681 if (break_point) { | |
682 break_point.enable(); | |
683 } | |
684 }; | |
685 | |
686 | |
687 Debug.disableBreakPoint = function(break_point_number) { | |
688 var break_point = this.findBreakPoint(break_point_number, false); | |
689 // Only enable if the breakpoint hasn't been deleted: | |
690 if (break_point) { | |
691 break_point.disable(); | |
692 } | |
693 }; | |
694 | |
695 | |
696 Debug.changeBreakPointCondition = function(break_point_number, condition) { | |
697 var break_point = this.findBreakPoint(break_point_number, false); | |
698 break_point.setCondition(condition); | |
699 }; | |
700 | |
701 | |
702 Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) { | |
703 if (ignoreCount < 0) { | |
704 throw new Error('Invalid argument'); | |
705 } | |
706 var break_point = this.findBreakPoint(break_point_number, false); | |
707 break_point.setIgnoreCount(ignoreCount); | |
708 }; | |
709 | |
710 | |
711 Debug.clearBreakPoint = function(break_point_number) { | |
712 var break_point = this.findBreakPoint(break_point_number, true); | |
713 if (break_point) { | |
714 return %ClearBreakPoint(break_point); | |
715 } else { | |
716 break_point = this.findScriptBreakPoint(break_point_number, true); | |
717 if (!break_point) { | |
718 throw new Error('Invalid breakpoint'); | |
719 } | |
720 } | |
721 }; | |
722 | |
723 | |
724 Debug.clearAllBreakPoints = function() { | |
725 for (var i = 0; i < break_points.length; i++) { | |
726 var break_point = break_points[i]; | |
727 %ClearBreakPoint(break_point); | |
728 } | |
729 break_points = []; | |
730 }; | |
731 | |
732 | |
733 Debug.disableAllBreakPoints = function() { | |
734 // Disable all user defined breakpoints: | |
735 for (var i = 1; i < next_break_point_number; i++) { | |
736 Debug.disableBreakPoint(i); | |
737 } | |
738 // Disable all exception breakpoints: | |
739 %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); | |
740 %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); | |
741 }; | |
742 | |
743 | |
744 Debug.findScriptBreakPoint = function(break_point_number, remove) { | |
745 var script_break_point; | |
746 for (var i = 0; i < script_break_points.length; i++) { | |
747 if (script_break_points[i].number() == break_point_number) { | |
748 script_break_point = script_break_points[i]; | |
749 // Remove the break point from the list if requested. | |
750 if (remove) { | |
751 script_break_point.clear(); | |
752 script_break_points.splice(i,1); | |
753 } | |
754 break; | |
755 } | |
756 } | |
757 return script_break_point; | |
758 }; | |
759 | |
760 | |
761 // Sets a breakpoint in a script identified through id or name at the | |
762 // specified source line and column within that line. | |
763 Debug.setScriptBreakPoint = function(type, script_id_or_name, | |
764 opt_line, opt_column, opt_condition, | |
765 opt_groupId, opt_position_alignment) { | |
766 // Create script break point object. | |
767 var script_break_point = | |
768 new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, | |
769 opt_groupId, opt_position_alignment); | |
770 | |
771 // Assign number to the new script break point and add it. | |
772 script_break_point.number_ = next_break_point_number++; | |
773 script_break_point.setCondition(opt_condition); | |
774 script_break_points.push(script_break_point); | |
775 | |
776 // Run through all scripts to see if this script break point matches any | |
777 // loaded scripts. | |
778 var scripts = this.scripts(); | |
779 for (var i = 0; i < scripts.length; i++) { | |
780 if (script_break_point.matchesScript(scripts[i])) { | |
781 script_break_point.set(scripts[i]); | |
782 } | |
783 } | |
784 | |
785 return script_break_point.number(); | |
786 }; | |
787 | |
788 | |
789 Debug.setScriptBreakPointById = function(script_id, | |
790 opt_line, opt_column, | |
791 opt_condition, opt_groupId, | |
792 opt_position_alignment) { | |
793 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, | |
794 script_id, opt_line, opt_column, | |
795 opt_condition, opt_groupId, | |
796 opt_position_alignment); | |
797 }; | |
798 | |
799 | |
800 Debug.setScriptBreakPointByName = function(script_name, | |
801 opt_line, opt_column, | |
802 opt_condition, opt_groupId) { | |
803 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName, | |
804 script_name, opt_line, opt_column, | |
805 opt_condition, opt_groupId); | |
806 }; | |
807 | |
808 | |
809 Debug.setScriptBreakPointByRegExp = function(script_regexp, | |
810 opt_line, opt_column, | |
811 opt_condition, opt_groupId) { | |
812 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp, | |
813 script_regexp, opt_line, opt_column, | |
814 opt_condition, opt_groupId); | |
815 }; | |
816 | |
817 | |
818 Debug.enableScriptBreakPoint = function(break_point_number) { | |
819 var script_break_point = this.findScriptBreakPoint(break_point_number, false); | |
820 script_break_point.enable(); | |
821 }; | |
822 | |
823 | |
824 Debug.disableScriptBreakPoint = function(break_point_number) { | |
825 var script_break_point = this.findScriptBreakPoint(break_point_number, false); | |
826 script_break_point.disable(); | |
827 }; | |
828 | |
829 | |
830 Debug.changeScriptBreakPointCondition = function( | |
831 break_point_number, condition) { | |
832 var script_break_point = this.findScriptBreakPoint(break_point_number, false); | |
833 script_break_point.setCondition(condition); | |
834 }; | |
835 | |
836 | |
837 Debug.changeScriptBreakPointIgnoreCount = function( | |
838 break_point_number, ignoreCount) { | |
839 if (ignoreCount < 0) { | |
840 throw new Error('Invalid argument'); | |
841 } | |
842 var script_break_point = this.findScriptBreakPoint(break_point_number, false); | |
843 script_break_point.setIgnoreCount(ignoreCount); | |
844 }; | |
845 | |
846 | |
847 Debug.scriptBreakPoints = function() { | |
848 return script_break_points; | |
849 }; | |
850 | |
851 | |
852 Debug.clearStepping = function() { | |
853 %ClearStepping(); | |
854 }; | |
855 | |
856 Debug.setBreakOnException = function() { | |
857 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true); | |
858 }; | |
859 | |
860 Debug.clearBreakOnException = function() { | |
861 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); | |
862 }; | |
863 | |
864 Debug.isBreakOnException = function() { | |
865 return !!%IsBreakOnException(Debug.ExceptionBreak.Caught); | |
866 }; | |
867 | |
868 Debug.setBreakOnUncaughtException = function() { | |
869 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true); | |
870 }; | |
871 | |
872 Debug.clearBreakOnUncaughtException = function() { | |
873 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); | |
874 }; | |
875 | |
876 Debug.isBreakOnUncaughtException = function() { | |
877 return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught); | |
878 }; | |
879 | |
880 Debug.showBreakPoints = function(f, full, opt_position_alignment) { | |
881 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); | |
882 var source = full ? this.scriptSource(f) : this.source(f); | |
883 var offset = full ? this.sourcePosition(f) : 0; | |
884 var locations = this.breakLocations(f, opt_position_alignment); | |
885 if (!locations) return source; | |
886 locations.sort(function(x, y) { return x - y; }); | |
887 var result = ""; | |
888 var prev_pos = 0; | |
889 var pos; | |
890 for (var i = 0; i < locations.length; i++) { | |
891 pos = locations[i] - offset; | |
892 result += source.slice(prev_pos, pos); | |
893 result += "[B" + i + "]"; | |
894 prev_pos = pos; | |
895 } | |
896 pos = source.length; | |
897 result += source.substring(prev_pos, pos); | |
898 return result; | |
899 }; | |
900 | |
901 | |
902 // Get all the scripts currently loaded. Locating all the scripts is based on | |
903 // scanning the heap. | |
904 Debug.scripts = function() { | |
905 // Collect all scripts in the heap. | |
906 return %DebugGetLoadedScripts(); | |
907 }; | |
908 | |
909 | |
910 Debug.debuggerFlags = function() { | |
911 return debugger_flags; | |
912 }; | |
913 | |
914 Debug.MakeMirror = MakeMirror; | |
915 | |
916 function MakeExecutionState(break_id) { | |
917 return new ExecutionState(break_id); | |
918 } | |
919 | |
920 function ExecutionState(break_id) { | |
921 this.break_id = break_id; | |
922 this.selected_frame = 0; | |
923 } | |
924 | |
925 ExecutionState.prototype.prepareStep = function(opt_action, opt_count, | |
926 opt_callframe) { | |
927 var action = Debug.StepAction.StepIn; | |
928 if (!IS_UNDEFINED(opt_action)) action = builtins.$toNumber(opt_action); | |
929 var count = opt_count ? builtins.$toNumber(opt_count) : 1; | |
930 var callFrameId = 0; | |
931 if (!IS_UNDEFINED(opt_callframe)) { | |
932 callFrameId = opt_callframe.details_.frameId(); | |
933 } | |
934 | |
935 return %PrepareStep(this.break_id, action, count, callFrameId); | |
936 }; | |
937 | |
938 ExecutionState.prototype.evaluateGlobal = function(source, disable_break, | |
939 opt_additional_context) { | |
940 return MakeMirror(%DebugEvaluateGlobal(this.break_id, source, | |
941 Boolean(disable_break), | |
942 opt_additional_context)); | |
943 }; | |
944 | |
945 ExecutionState.prototype.frameCount = function() { | |
946 return %GetFrameCount(this.break_id); | |
947 }; | |
948 | |
949 ExecutionState.prototype.threadCount = function() { | |
950 return %GetThreadCount(this.break_id); | |
951 }; | |
952 | |
953 ExecutionState.prototype.frame = function(opt_index) { | |
954 // If no index supplied return the selected frame. | |
955 if (opt_index == null) opt_index = this.selected_frame; | |
956 if (opt_index < 0 || opt_index >= this.frameCount()) { | |
957 throw new Error('Illegal frame index.'); | |
958 } | |
959 return new FrameMirror(this.break_id, opt_index); | |
960 }; | |
961 | |
962 ExecutionState.prototype.setSelectedFrame = function(index) { | |
963 var i = builtins.$toNumber(index); | |
964 if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.'); | |
965 this.selected_frame = i; | |
966 }; | |
967 | |
968 ExecutionState.prototype.selectedFrame = function() { | |
969 return this.selected_frame; | |
970 }; | |
971 | |
972 ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) { | |
973 return new DebugCommandProcessor(this, opt_is_running); | |
974 }; | |
975 | |
976 | |
977 function MakeBreakEvent(break_id, break_points_hit) { | |
978 return new BreakEvent(break_id, break_points_hit); | |
979 } | |
980 | |
981 | |
982 function BreakEvent(break_id, break_points_hit) { | |
983 this.frame_ = new FrameMirror(break_id, 0); | |
984 this.break_points_hit_ = break_points_hit; | |
985 } | |
986 | |
987 | |
988 BreakEvent.prototype.eventType = function() { | |
989 return Debug.DebugEvent.Break; | |
990 }; | |
991 | |
992 | |
993 BreakEvent.prototype.func = function() { | |
994 return this.frame_.func(); | |
995 }; | |
996 | |
997 | |
998 BreakEvent.prototype.sourceLine = function() { | |
999 return this.frame_.sourceLine(); | |
1000 }; | |
1001 | |
1002 | |
1003 BreakEvent.prototype.sourceColumn = function() { | |
1004 return this.frame_.sourceColumn(); | |
1005 }; | |
1006 | |
1007 | |
1008 BreakEvent.prototype.sourceLineText = function() { | |
1009 return this.frame_.sourceLineText(); | |
1010 }; | |
1011 | |
1012 | |
1013 BreakEvent.prototype.breakPointsHit = function() { | |
1014 return this.break_points_hit_; | |
1015 }; | |
1016 | |
1017 | |
1018 BreakEvent.prototype.toJSONProtocol = function() { | |
1019 var o = { seq: next_response_seq++, | |
1020 type: "event", | |
1021 event: "break", | |
1022 body: { invocationText: this.frame_.invocationText() } | |
1023 }; | |
1024 | |
1025 // Add script related information to the event if available. | |
1026 var script = this.func().script(); | |
1027 if (script) { | |
1028 o.body.sourceLine = this.sourceLine(), | |
1029 o.body.sourceColumn = this.sourceColumn(), | |
1030 o.body.sourceLineText = this.sourceLineText(), | |
1031 o.body.script = MakeScriptObject_(script, false); | |
1032 } | |
1033 | |
1034 // Add an Array of break points hit if any. | |
1035 if (this.breakPointsHit()) { | |
1036 o.body.breakpoints = []; | |
1037 for (var i = 0; i < this.breakPointsHit().length; i++) { | |
1038 // Find the break point number. For break points originating from a | |
1039 // script break point supply the script break point number. | |
1040 var breakpoint = this.breakPointsHit()[i]; | |
1041 var script_break_point = breakpoint.script_break_point(); | |
1042 var number; | |
1043 if (script_break_point) { | |
1044 number = script_break_point.number(); | |
1045 } else { | |
1046 number = breakpoint.number(); | |
1047 } | |
1048 o.body.breakpoints.push(number); | |
1049 } | |
1050 } | |
1051 return JSON.stringify(ObjectToProtocolObject_(o)); | |
1052 }; | |
1053 | |
1054 | |
1055 function MakeExceptionEvent(break_id, exception, uncaught, promise) { | |
1056 return new ExceptionEvent(break_id, exception, uncaught, promise); | |
1057 } | |
1058 | |
1059 | |
1060 function ExceptionEvent(break_id, exception, uncaught, promise) { | |
1061 this.exec_state_ = new ExecutionState(break_id); | |
1062 this.exception_ = exception; | |
1063 this.uncaught_ = uncaught; | |
1064 this.promise_ = promise; | |
1065 } | |
1066 | |
1067 | |
1068 ExceptionEvent.prototype.eventType = function() { | |
1069 return Debug.DebugEvent.Exception; | |
1070 }; | |
1071 | |
1072 | |
1073 ExceptionEvent.prototype.exception = function() { | |
1074 return this.exception_; | |
1075 }; | |
1076 | |
1077 | |
1078 ExceptionEvent.prototype.uncaught = function() { | |
1079 return this.uncaught_; | |
1080 }; | |
1081 | |
1082 | |
1083 ExceptionEvent.prototype.promise = function() { | |
1084 return this.promise_; | |
1085 }; | |
1086 | |
1087 | |
1088 ExceptionEvent.prototype.func = function() { | |
1089 return this.exec_state_.frame(0).func(); | |
1090 }; | |
1091 | |
1092 | |
1093 ExceptionEvent.prototype.sourceLine = function() { | |
1094 return this.exec_state_.frame(0).sourceLine(); | |
1095 }; | |
1096 | |
1097 | |
1098 ExceptionEvent.prototype.sourceColumn = function() { | |
1099 return this.exec_state_.frame(0).sourceColumn(); | |
1100 }; | |
1101 | |
1102 | |
1103 ExceptionEvent.prototype.sourceLineText = function() { | |
1104 return this.exec_state_.frame(0).sourceLineText(); | |
1105 }; | |
1106 | |
1107 | |
1108 ExceptionEvent.prototype.toJSONProtocol = function() { | |
1109 var o = new ProtocolMessage(); | |
1110 o.event = "exception"; | |
1111 o.body = { uncaught: this.uncaught_, | |
1112 exception: MakeMirror(this.exception_) | |
1113 }; | |
1114 | |
1115 // Exceptions might happen whithout any JavaScript frames. | |
1116 if (this.exec_state_.frameCount() > 0) { | |
1117 o.body.sourceLine = this.sourceLine(); | |
1118 o.body.sourceColumn = this.sourceColumn(); | |
1119 o.body.sourceLineText = this.sourceLineText(); | |
1120 | |
1121 // Add script information to the event if available. | |
1122 var script = this.func().script(); | |
1123 if (script) { | |
1124 o.body.script = MakeScriptObject_(script, false); | |
1125 } | |
1126 } else { | |
1127 o.body.sourceLine = -1; | |
1128 } | |
1129 | |
1130 return o.toJSONProtocol(); | |
1131 }; | |
1132 | |
1133 | |
1134 function MakeCompileEvent(script, type) { | |
1135 return new CompileEvent(script, type); | |
1136 } | |
1137 | |
1138 | |
1139 function CompileEvent(script, type) { | |
1140 this.script_ = MakeMirror(script); | |
1141 this.type_ = type; | |
1142 } | |
1143 | |
1144 | |
1145 CompileEvent.prototype.eventType = function() { | |
1146 return this.type_; | |
1147 }; | |
1148 | |
1149 | |
1150 CompileEvent.prototype.script = function() { | |
1151 return this.script_; | |
1152 }; | |
1153 | |
1154 | |
1155 CompileEvent.prototype.toJSONProtocol = function() { | |
1156 var o = new ProtocolMessage(); | |
1157 o.running = true; | |
1158 switch (this.type_) { | |
1159 case Debug.DebugEvent.BeforeCompile: | |
1160 o.event = "beforeCompile"; | |
1161 break; | |
1162 case Debug.DebugEvent.AfterCompile: | |
1163 o.event = "afterCompile"; | |
1164 break; | |
1165 case Debug.DebugEvent.CompileError: | |
1166 o.event = "compileError"; | |
1167 break; | |
1168 } | |
1169 o.body = {}; | |
1170 o.body.script = this.script_; | |
1171 | |
1172 return o.toJSONProtocol(); | |
1173 }; | |
1174 | |
1175 | |
1176 function MakeScriptObject_(script, include_source) { | |
1177 var o = { id: script.id(), | |
1178 name: script.name(), | |
1179 lineOffset: script.lineOffset(), | |
1180 columnOffset: script.columnOffset(), | |
1181 lineCount: script.lineCount(), | |
1182 }; | |
1183 if (!IS_UNDEFINED(script.data())) { | |
1184 o.data = script.data(); | |
1185 } | |
1186 if (include_source) { | |
1187 o.source = script.source(); | |
1188 } | |
1189 return o; | |
1190 } | |
1191 | |
1192 | |
1193 function MakePromiseEvent(event_data) { | |
1194 return new PromiseEvent(event_data); | |
1195 } | |
1196 | |
1197 | |
1198 function PromiseEvent(event_data) { | |
1199 this.promise_ = event_data.promise; | |
1200 this.parentPromise_ = event_data.parentPromise; | |
1201 this.status_ = event_data.status; | |
1202 this.value_ = event_data.value; | |
1203 } | |
1204 | |
1205 | |
1206 PromiseEvent.prototype.promise = function() { | |
1207 return MakeMirror(this.promise_); | |
1208 } | |
1209 | |
1210 | |
1211 PromiseEvent.prototype.parentPromise = function() { | |
1212 return MakeMirror(this.parentPromise_); | |
1213 } | |
1214 | |
1215 | |
1216 PromiseEvent.prototype.status = function() { | |
1217 return this.status_; | |
1218 } | |
1219 | |
1220 | |
1221 PromiseEvent.prototype.value = function() { | |
1222 return MakeMirror(this.value_); | |
1223 } | |
1224 | |
1225 | |
1226 function MakeAsyncTaskEvent(event_data) { | |
1227 return new AsyncTaskEvent(event_data); | |
1228 } | |
1229 | |
1230 | |
1231 function AsyncTaskEvent(event_data) { | |
1232 this.type_ = event_data.type; | |
1233 this.name_ = event_data.name; | |
1234 this.id_ = event_data.id; | |
1235 } | |
1236 | |
1237 | |
1238 AsyncTaskEvent.prototype.type = function() { | |
1239 return this.type_; | |
1240 } | |
1241 | |
1242 | |
1243 AsyncTaskEvent.prototype.name = function() { | |
1244 return this.name_; | |
1245 } | |
1246 | |
1247 | |
1248 AsyncTaskEvent.prototype.id = function() { | |
1249 return this.id_; | |
1250 } | |
1251 | |
1252 | |
1253 function DebugCommandProcessor(exec_state, opt_is_running) { | |
1254 this.exec_state_ = exec_state; | |
1255 this.running_ = opt_is_running || false; | |
1256 } | |
1257 | |
1258 | |
1259 DebugCommandProcessor.prototype.processDebugRequest = function (request) { | |
1260 return this.processDebugJSONRequest(request); | |
1261 }; | |
1262 | |
1263 | |
1264 function ProtocolMessage(request) { | |
1265 // Update sequence number. | |
1266 this.seq = next_response_seq++; | |
1267 | |
1268 if (request) { | |
1269 // If message is based on a request this is a response. Fill the initial | |
1270 // response from the request. | |
1271 this.type = 'response'; | |
1272 this.request_seq = request.seq; | |
1273 this.command = request.command; | |
1274 } else { | |
1275 // If message is not based on a request it is a dabugger generated event. | |
1276 this.type = 'event'; | |
1277 } | |
1278 this.success = true; | |
1279 // Handler may set this field to control debugger state. | |
1280 this.running = undefined; | |
1281 } | |
1282 | |
1283 | |
1284 ProtocolMessage.prototype.setOption = function(name, value) { | |
1285 if (!this.options_) { | |
1286 this.options_ = {}; | |
1287 } | |
1288 this.options_[name] = value; | |
1289 }; | |
1290 | |
1291 | |
1292 ProtocolMessage.prototype.failed = function(message, opt_details) { | |
1293 this.success = false; | |
1294 this.message = message; | |
1295 if (IS_OBJECT(opt_details)) { | |
1296 this.error_details = opt_details; | |
1297 } | |
1298 }; | |
1299 | |
1300 | |
1301 ProtocolMessage.prototype.toJSONProtocol = function() { | |
1302 // Encode the protocol header. | |
1303 var json = {}; | |
1304 json.seq= this.seq; | |
1305 if (this.request_seq) { | |
1306 json.request_seq = this.request_seq; | |
1307 } | |
1308 json.type = this.type; | |
1309 if (this.event) { | |
1310 json.event = this.event; | |
1311 } | |
1312 if (this.command) { | |
1313 json.command = this.command; | |
1314 } | |
1315 if (this.success) { | |
1316 json.success = this.success; | |
1317 } else { | |
1318 json.success = false; | |
1319 } | |
1320 if (this.body) { | |
1321 // Encode the body part. | |
1322 var bodyJson; | |
1323 var serializer = MakeMirrorSerializer(true, this.options_); | |
1324 if (this.body instanceof Mirror) { | |
1325 bodyJson = serializer.serializeValue(this.body); | |
1326 } else if (this.body instanceof Array) { | |
1327 bodyJson = []; | |
1328 for (var i = 0; i < this.body.length; i++) { | |
1329 if (this.body[i] instanceof Mirror) { | |
1330 bodyJson.push(serializer.serializeValue(this.body[i])); | |
1331 } else { | |
1332 bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer)); | |
1333 } | |
1334 } | |
1335 } else { | |
1336 bodyJson = ObjectToProtocolObject_(this.body, serializer); | |
1337 } | |
1338 json.body = bodyJson; | |
1339 json.refs = serializer.serializeReferencedObjects(); | |
1340 } | |
1341 if (this.message) { | |
1342 json.message = this.message; | |
1343 } | |
1344 if (this.error_details) { | |
1345 json.error_details = this.error_details; | |
1346 } | |
1347 json.running = this.running; | |
1348 return JSON.stringify(json); | |
1349 }; | |
1350 | |
1351 | |
1352 DebugCommandProcessor.prototype.createResponse = function(request) { | |
1353 return new ProtocolMessage(request); | |
1354 }; | |
1355 | |
1356 | |
1357 DebugCommandProcessor.prototype.processDebugJSONRequest = function( | |
1358 json_request) { | |
1359 var request; // Current request. | |
1360 var response; // Generated response. | |
1361 try { | |
1362 try { | |
1363 // Convert the JSON string to an object. | |
1364 request = JSON.parse(json_request); | |
1365 | |
1366 // Create an initial response. | |
1367 response = this.createResponse(request); | |
1368 | |
1369 if (!request.type) { | |
1370 throw new Error('Type not specified'); | |
1371 } | |
1372 | |
1373 if (request.type != 'request') { | |
1374 throw new Error("Illegal type '" + request.type + "' in request"); | |
1375 } | |
1376 | |
1377 if (!request.command) { | |
1378 throw new Error('Command not specified'); | |
1379 } | |
1380 | |
1381 if (request.arguments) { | |
1382 var args = request.arguments; | |
1383 // TODO(yurys): remove request.arguments.compactFormat check once | |
1384 // ChromeDevTools are switched to 'inlineRefs' | |
1385 if (args.inlineRefs || args.compactFormat) { | |
1386 response.setOption('inlineRefs', true); | |
1387 } | |
1388 if (!IS_UNDEFINED(args.maxStringLength)) { | |
1389 response.setOption('maxStringLength', args.maxStringLength); | |
1390 } | |
1391 } | |
1392 | |
1393 var key = request.command.toLowerCase(); | |
1394 var handler = DebugCommandProcessor.prototype.dispatch_[key]; | |
1395 if (IS_FUNCTION(handler)) { | |
1396 %_CallFunction(this, request, response, handler); | |
1397 } else { | |
1398 throw new Error('Unknown command "' + request.command + '" in request'); | |
1399 } | |
1400 } catch (e) { | |
1401 // If there is no response object created one (without command). | |
1402 if (!response) { | |
1403 response = this.createResponse(); | |
1404 } | |
1405 response.success = false; | |
1406 response.message = builtins.$toString(e); | |
1407 } | |
1408 | |
1409 // Return the response as a JSON encoded string. | |
1410 try { | |
1411 if (!IS_UNDEFINED(response.running)) { | |
1412 // Response controls running state. | |
1413 this.running_ = response.running; | |
1414 } | |
1415 response.running = this.running_; | |
1416 return response.toJSONProtocol(); | |
1417 } catch (e) { | |
1418 // Failed to generate response - return generic error. | |
1419 return '{"seq":' + response.seq + ',' + | |
1420 '"request_seq":' + request.seq + ',' + | |
1421 '"type":"response",' + | |
1422 '"success":false,' + | |
1423 '"message":"Internal error: ' + builtins.$toString(e) + '"}'; | |
1424 } | |
1425 } catch (e) { | |
1426 // Failed in one of the catch blocks above - most generic error. | |
1427 return '{"seq":0,"type":"response","success":false,"message":"Internal error
"}'; | |
1428 } | |
1429 }; | |
1430 | |
1431 | |
1432 DebugCommandProcessor.prototype.continueRequest_ = function(request, response) { | |
1433 // Check for arguments for continue. | |
1434 if (request.arguments) { | |
1435 var count = 1; | |
1436 var action = Debug.StepAction.StepIn; | |
1437 | |
1438 // Pull out arguments. | |
1439 var stepaction = request.arguments.stepaction; | |
1440 var stepcount = request.arguments.stepcount; | |
1441 | |
1442 // Get the stepcount argument if any. | |
1443 if (stepcount) { | |
1444 count = builtins.$toNumber(stepcount); | |
1445 if (count < 0) { | |
1446 throw new Error('Invalid stepcount argument "' + stepcount + '".'); | |
1447 } | |
1448 } | |
1449 | |
1450 // Get the stepaction argument. | |
1451 if (stepaction) { | |
1452 if (stepaction == 'in') { | |
1453 action = Debug.StepAction.StepIn; | |
1454 } else if (stepaction == 'min') { | |
1455 action = Debug.StepAction.StepMin; | |
1456 } else if (stepaction == 'next') { | |
1457 action = Debug.StepAction.StepNext; | |
1458 } else if (stepaction == 'out') { | |
1459 action = Debug.StepAction.StepOut; | |
1460 } else { | |
1461 throw new Error('Invalid stepaction argument "' + stepaction + '".'); | |
1462 } | |
1463 } | |
1464 | |
1465 // Set up the VM for stepping. | |
1466 this.exec_state_.prepareStep(action, count); | |
1467 } | |
1468 | |
1469 // VM should be running after executing this request. | |
1470 response.running = true; | |
1471 }; | |
1472 | |
1473 | |
1474 DebugCommandProcessor.prototype.breakRequest_ = function(request, response) { | |
1475 // Ignore as break command does not do anything when broken. | |
1476 }; | |
1477 | |
1478 | |
1479 DebugCommandProcessor.prototype.setBreakPointRequest_ = | |
1480 function(request, response) { | |
1481 // Check for legal request. | |
1482 if (!request.arguments) { | |
1483 response.failed('Missing arguments'); | |
1484 return; | |
1485 } | |
1486 | |
1487 // Pull out arguments. | |
1488 var type = request.arguments.type; | |
1489 var target = request.arguments.target; | |
1490 var line = request.arguments.line; | |
1491 var column = request.arguments.column; | |
1492 var enabled = IS_UNDEFINED(request.arguments.enabled) ? | |
1493 true : request.arguments.enabled; | |
1494 var condition = request.arguments.condition; | |
1495 var ignoreCount = request.arguments.ignoreCount; | |
1496 var groupId = request.arguments.groupId; | |
1497 | |
1498 // Check for legal arguments. | |
1499 if (!type || IS_UNDEFINED(target)) { | |
1500 response.failed('Missing argument "type" or "target"'); | |
1501 return; | |
1502 } | |
1503 | |
1504 // Either function or script break point. | |
1505 var break_point_number; | |
1506 if (type == 'function') { | |
1507 // Handle function break point. | |
1508 if (!IS_STRING(target)) { | |
1509 response.failed('Argument "target" is not a string value'); | |
1510 return; | |
1511 } | |
1512 var f; | |
1513 try { | |
1514 // Find the function through a global evaluate. | |
1515 f = this.exec_state_.evaluateGlobal(target).value(); | |
1516 } catch (e) { | |
1517 response.failed('Error: "' + builtins.$toString(e) + | |
1518 '" evaluating "' + target + '"'); | |
1519 return; | |
1520 } | |
1521 if (!IS_FUNCTION(f)) { | |
1522 response.failed('"' + target + '" does not evaluate to a function'); | |
1523 return; | |
1524 } | |
1525 | |
1526 // Set function break point. | |
1527 break_point_number = Debug.setBreakPoint(f, line, column, condition); | |
1528 } else if (type == 'handle') { | |
1529 // Find the object pointed by the specified handle. | |
1530 var handle = parseInt(target, 10); | |
1531 var mirror = LookupMirror(handle); | |
1532 if (!mirror) { | |
1533 return response.failed('Object #' + handle + '# not found'); | |
1534 } | |
1535 if (!mirror.isFunction()) { | |
1536 return response.failed('Object #' + handle + '# is not a function'); | |
1537 } | |
1538 | |
1539 // Set function break point. | |
1540 break_point_number = Debug.setBreakPoint(mirror.value(), | |
1541 line, column, condition); | |
1542 } else if (type == 'script') { | |
1543 // set script break point. | |
1544 break_point_number = | |
1545 Debug.setScriptBreakPointByName(target, line, column, condition, | |
1546 groupId); | |
1547 } else if (type == 'scriptId') { | |
1548 break_point_number = | |
1549 Debug.setScriptBreakPointById(target, line, column, condition, groupId); | |
1550 } else if (type == 'scriptRegExp') { | |
1551 break_point_number = | |
1552 Debug.setScriptBreakPointByRegExp(target, line, column, condition, | |
1553 groupId); | |
1554 } else { | |
1555 response.failed('Illegal type "' + type + '"'); | |
1556 return; | |
1557 } | |
1558 | |
1559 // Set additional break point properties. | |
1560 var break_point = Debug.findBreakPoint(break_point_number); | |
1561 if (ignoreCount) { | |
1562 Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount); | |
1563 } | |
1564 if (!enabled) { | |
1565 Debug.disableBreakPoint(break_point_number); | |
1566 } | |
1567 | |
1568 // Add the break point number to the response. | |
1569 response.body = { type: type, | |
1570 breakpoint: break_point_number }; | |
1571 | |
1572 // Add break point information to the response. | |
1573 if (break_point instanceof ScriptBreakPoint) { | |
1574 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) { | |
1575 response.body.type = 'scriptId'; | |
1576 response.body.script_id = break_point.script_id(); | |
1577 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) { | |
1578 response.body.type = 'scriptName'; | |
1579 response.body.script_name = break_point.script_name(); | |
1580 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) { | |
1581 response.body.type = 'scriptRegExp'; | |
1582 response.body.script_regexp = break_point.script_regexp_object().source; | |
1583 } else { | |
1584 throw new Error("Internal error: Unexpected breakpoint type: " + | |
1585 break_point.type()); | |
1586 } | |
1587 response.body.line = break_point.line(); | |
1588 response.body.column = break_point.column(); | |
1589 response.body.actual_locations = break_point.actual_locations(); | |
1590 } else { | |
1591 response.body.type = 'function'; | |
1592 response.body.actual_locations = [break_point.actual_location]; | |
1593 } | |
1594 }; | |
1595 | |
1596 | |
1597 DebugCommandProcessor.prototype.changeBreakPointRequest_ = function( | |
1598 request, response) { | |
1599 // Check for legal request. | |
1600 if (!request.arguments) { | |
1601 response.failed('Missing arguments'); | |
1602 return; | |
1603 } | |
1604 | |
1605 // Pull out arguments. | |
1606 var break_point = builtins.$toNumber(request.arguments.breakpoint); | |
1607 var enabled = request.arguments.enabled; | |
1608 var condition = request.arguments.condition; | |
1609 var ignoreCount = request.arguments.ignoreCount; | |
1610 | |
1611 // Check for legal arguments. | |
1612 if (!break_point) { | |
1613 response.failed('Missing argument "breakpoint"'); | |
1614 return; | |
1615 } | |
1616 | |
1617 // Change enabled state if supplied. | |
1618 if (!IS_UNDEFINED(enabled)) { | |
1619 if (enabled) { | |
1620 Debug.enableBreakPoint(break_point); | |
1621 } else { | |
1622 Debug.disableBreakPoint(break_point); | |
1623 } | |
1624 } | |
1625 | |
1626 // Change condition if supplied | |
1627 if (!IS_UNDEFINED(condition)) { | |
1628 Debug.changeBreakPointCondition(break_point, condition); | |
1629 } | |
1630 | |
1631 // Change ignore count if supplied | |
1632 if (!IS_UNDEFINED(ignoreCount)) { | |
1633 Debug.changeBreakPointIgnoreCount(break_point, ignoreCount); | |
1634 } | |
1635 }; | |
1636 | |
1637 | |
1638 DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function( | |
1639 request, response) { | |
1640 // Check for legal request. | |
1641 if (!request.arguments) { | |
1642 response.failed('Missing arguments'); | |
1643 return; | |
1644 } | |
1645 | |
1646 // Pull out arguments. | |
1647 var group_id = request.arguments.groupId; | |
1648 | |
1649 // Check for legal arguments. | |
1650 if (!group_id) { | |
1651 response.failed('Missing argument "groupId"'); | |
1652 return; | |
1653 } | |
1654 | |
1655 var cleared_break_points = []; | |
1656 var new_script_break_points = []; | |
1657 for (var i = 0; i < script_break_points.length; i++) { | |
1658 var next_break_point = script_break_points[i]; | |
1659 if (next_break_point.groupId() == group_id) { | |
1660 cleared_break_points.push(next_break_point.number()); | |
1661 next_break_point.clear(); | |
1662 } else { | |
1663 new_script_break_points.push(next_break_point); | |
1664 } | |
1665 } | |
1666 script_break_points = new_script_break_points; | |
1667 | |
1668 // Add the cleared break point numbers to the response. | |
1669 response.body = { breakpoints: cleared_break_points }; | |
1670 }; | |
1671 | |
1672 | |
1673 DebugCommandProcessor.prototype.clearBreakPointRequest_ = function( | |
1674 request, response) { | |
1675 // Check for legal request. | |
1676 if (!request.arguments) { | |
1677 response.failed('Missing arguments'); | |
1678 return; | |
1679 } | |
1680 | |
1681 // Pull out arguments. | |
1682 var break_point = builtins.$toNumber(request.arguments.breakpoint); | |
1683 | |
1684 // Check for legal arguments. | |
1685 if (!break_point) { | |
1686 response.failed('Missing argument "breakpoint"'); | |
1687 return; | |
1688 } | |
1689 | |
1690 // Clear break point. | |
1691 Debug.clearBreakPoint(break_point); | |
1692 | |
1693 // Add the cleared break point number to the response. | |
1694 response.body = { breakpoint: break_point }; | |
1695 }; | |
1696 | |
1697 | |
1698 DebugCommandProcessor.prototype.listBreakpointsRequest_ = function( | |
1699 request, response) { | |
1700 var array = []; | |
1701 for (var i = 0; i < script_break_points.length; i++) { | |
1702 var break_point = script_break_points[i]; | |
1703 | |
1704 var description = { | |
1705 number: break_point.number(), | |
1706 line: break_point.line(), | |
1707 column: break_point.column(), | |
1708 groupId: break_point.groupId(), | |
1709 hit_count: break_point.hit_count(), | |
1710 active: break_point.active(), | |
1711 condition: break_point.condition(), | |
1712 ignoreCount: break_point.ignoreCount(), | |
1713 actual_locations: break_point.actual_locations() | |
1714 }; | |
1715 | |
1716 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) { | |
1717 description.type = 'scriptId'; | |
1718 description.script_id = break_point.script_id(); | |
1719 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) { | |
1720 description.type = 'scriptName'; | |
1721 description.script_name = break_point.script_name(); | |
1722 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) { | |
1723 description.type = 'scriptRegExp'; | |
1724 description.script_regexp = break_point.script_regexp_object().source; | |
1725 } else { | |
1726 throw new Error("Internal error: Unexpected breakpoint type: " + | |
1727 break_point.type()); | |
1728 } | |
1729 array.push(description); | |
1730 } | |
1731 | |
1732 response.body = { | |
1733 breakpoints: array, | |
1734 breakOnExceptions: Debug.isBreakOnException(), | |
1735 breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException() | |
1736 }; | |
1737 }; | |
1738 | |
1739 | |
1740 DebugCommandProcessor.prototype.disconnectRequest_ = | |
1741 function(request, response) { | |
1742 Debug.disableAllBreakPoints(); | |
1743 this.continueRequest_(request, response); | |
1744 }; | |
1745 | |
1746 | |
1747 DebugCommandProcessor.prototype.setExceptionBreakRequest_ = | |
1748 function(request, response) { | |
1749 // Check for legal request. | |
1750 if (!request.arguments) { | |
1751 response.failed('Missing arguments'); | |
1752 return; | |
1753 } | |
1754 | |
1755 // Pull out and check the 'type' argument: | |
1756 var type = request.arguments.type; | |
1757 if (!type) { | |
1758 response.failed('Missing argument "type"'); | |
1759 return; | |
1760 } | |
1761 | |
1762 // Initialize the default value of enable: | |
1763 var enabled; | |
1764 if (type == 'all') { | |
1765 enabled = !Debug.isBreakOnException(); | |
1766 } else if (type == 'uncaught') { | |
1767 enabled = !Debug.isBreakOnUncaughtException(); | |
1768 } | |
1769 | |
1770 // Pull out and check the 'enabled' argument if present: | |
1771 if (!IS_UNDEFINED(request.arguments.enabled)) { | |
1772 enabled = request.arguments.enabled; | |
1773 if ((enabled != true) && (enabled != false)) { | |
1774 response.failed('Illegal value for "enabled":"' + enabled + '"'); | |
1775 } | |
1776 } | |
1777 | |
1778 // Now set the exception break state: | |
1779 if (type == 'all') { | |
1780 %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled); | |
1781 } else if (type == 'uncaught') { | |
1782 %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled); | |
1783 } else { | |
1784 response.failed('Unknown "type":"' + type + '"'); | |
1785 } | |
1786 | |
1787 // Add the cleared break point number to the response. | |
1788 response.body = { 'type': type, 'enabled': enabled }; | |
1789 }; | |
1790 | |
1791 | |
1792 DebugCommandProcessor.prototype.backtraceRequest_ = function( | |
1793 request, response) { | |
1794 // Get the number of frames. | |
1795 var total_frames = this.exec_state_.frameCount(); | |
1796 | |
1797 // Create simple response if there are no frames. | |
1798 if (total_frames == 0) { | |
1799 response.body = { | |
1800 totalFrames: total_frames | |
1801 }; | |
1802 return; | |
1803 } | |
1804 | |
1805 // Default frame range to include in backtrace. | |
1806 var from_index = 0; | |
1807 var to_index = kDefaultBacktraceLength; | |
1808 | |
1809 // Get the range from the arguments. | |
1810 if (request.arguments) { | |
1811 if (request.arguments.fromFrame) { | |
1812 from_index = request.arguments.fromFrame; | |
1813 } | |
1814 if (request.arguments.toFrame) { | |
1815 to_index = request.arguments.toFrame; | |
1816 } | |
1817 if (request.arguments.bottom) { | |
1818 var tmp_index = total_frames - from_index; | |
1819 from_index = total_frames - to_index; | |
1820 to_index = tmp_index; | |
1821 } | |
1822 if (from_index < 0 || to_index < 0) { | |
1823 return response.failed('Invalid frame number'); | |
1824 } | |
1825 } | |
1826 | |
1827 // Adjust the index. | |
1828 to_index = Math.min(total_frames, to_index); | |
1829 | |
1830 if (to_index <= from_index) { | |
1831 var error = 'Invalid frame range'; | |
1832 return response.failed(error); | |
1833 } | |
1834 | |
1835 // Create the response body. | |
1836 var frames = []; | |
1837 for (var i = from_index; i < to_index; i++) { | |
1838 frames.push(this.exec_state_.frame(i)); | |
1839 } | |
1840 response.body = { | |
1841 fromFrame: from_index, | |
1842 toFrame: to_index, | |
1843 totalFrames: total_frames, | |
1844 frames: frames | |
1845 }; | |
1846 }; | |
1847 | |
1848 | |
1849 DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { | |
1850 // No frames no source. | |
1851 if (this.exec_state_.frameCount() == 0) { | |
1852 return response.failed('No frames'); | |
1853 } | |
1854 | |
1855 // With no arguments just keep the selected frame. | |
1856 if (request.arguments) { | |
1857 var index = request.arguments.number; | |
1858 if (index < 0 || this.exec_state_.frameCount() <= index) { | |
1859 return response.failed('Invalid frame number'); | |
1860 } | |
1861 | |
1862 this.exec_state_.setSelectedFrame(request.arguments.number); | |
1863 } | |
1864 response.body = this.exec_state_.frame(); | |
1865 }; | |
1866 | |
1867 | |
1868 DebugCommandProcessor.prototype.resolveFrameFromScopeDescription_ = | |
1869 function(scope_description) { | |
1870 // Get the frame for which the scope or scopes are requested. | |
1871 // With no frameNumber argument use the currently selected frame. | |
1872 if (scope_description && !IS_UNDEFINED(scope_description.frameNumber)) { | |
1873 var frame_index = scope_description.frameNumber; | |
1874 if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) { | |
1875 throw new Error('Invalid frame number'); | |
1876 } | |
1877 return this.exec_state_.frame(frame_index); | |
1878 } else { | |
1879 return this.exec_state_.frame(); | |
1880 } | |
1881 }; | |
1882 | |
1883 | |
1884 // Gets scope host object from request. It is either a function | |
1885 // ('functionHandle' argument must be specified) or a stack frame | |
1886 // ('frameNumber' may be specified and the current frame is taken by default). | |
1887 DebugCommandProcessor.prototype.resolveScopeHolder_ = | |
1888 function(scope_description) { | |
1889 if (scope_description && "functionHandle" in scope_description) { | |
1890 if (!IS_NUMBER(scope_description.functionHandle)) { | |
1891 throw new Error('Function handle must be a number'); | |
1892 } | |
1893 var function_mirror = LookupMirror(scope_description.functionHandle); | |
1894 if (!function_mirror) { | |
1895 throw new Error('Failed to find function object by handle'); | |
1896 } | |
1897 if (!function_mirror.isFunction()) { | |
1898 throw new Error('Value of non-function type is found by handle'); | |
1899 } | |
1900 return function_mirror; | |
1901 } else { | |
1902 // No frames no scopes. | |
1903 if (this.exec_state_.frameCount() == 0) { | |
1904 throw new Error('No scopes'); | |
1905 } | |
1906 | |
1907 // Get the frame for which the scopes are requested. | |
1908 var frame = this.resolveFrameFromScopeDescription_(scope_description); | |
1909 return frame; | |
1910 } | |
1911 } | |
1912 | |
1913 | |
1914 DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) { | |
1915 var scope_holder = this.resolveScopeHolder_(request.arguments); | |
1916 | |
1917 // Fill all scopes for this frame or function. | |
1918 var total_scopes = scope_holder.scopeCount(); | |
1919 var scopes = []; | |
1920 for (var i = 0; i < total_scopes; i++) { | |
1921 scopes.push(scope_holder.scope(i)); | |
1922 } | |
1923 response.body = { | |
1924 fromScope: 0, | |
1925 toScope: total_scopes, | |
1926 totalScopes: total_scopes, | |
1927 scopes: scopes | |
1928 }; | |
1929 }; | |
1930 | |
1931 | |
1932 DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) { | |
1933 // Get the frame or function for which the scope is requested. | |
1934 var scope_holder = this.resolveScopeHolder_(request.arguments); | |
1935 | |
1936 // With no scope argument just return top scope. | |
1937 var scope_index = 0; | |
1938 if (request.arguments && !IS_UNDEFINED(request.arguments.number)) { | |
1939 scope_index = builtins.$toNumber(request.arguments.number); | |
1940 if (scope_index < 0 || scope_holder.scopeCount() <= scope_index) { | |
1941 return response.failed('Invalid scope number'); | |
1942 } | |
1943 } | |
1944 | |
1945 response.body = scope_holder.scope(scope_index); | |
1946 }; | |
1947 | |
1948 | |
1949 // Reads value from protocol description. Description may be in form of type | |
1950 // (for singletons), raw value (primitive types supported in JSON), | |
1951 // string value description plus type (for primitive values) or handle id. | |
1952 // Returns raw value or throws exception. | |
1953 DebugCommandProcessor.resolveValue_ = function(value_description) { | |
1954 if ("handle" in value_description) { | |
1955 var value_mirror = LookupMirror(value_description.handle); | |
1956 if (!value_mirror) { | |
1957 throw new Error("Failed to resolve value by handle, ' #" + | |
1958 value_description.handle + "# not found"); | |
1959 } | |
1960 return value_mirror.value(); | |
1961 } else if ("stringDescription" in value_description) { | |
1962 if (value_description.type == BOOLEAN_TYPE) { | |
1963 return Boolean(value_description.stringDescription); | |
1964 } else if (value_description.type == NUMBER_TYPE) { | |
1965 return Number(value_description.stringDescription); | |
1966 } if (value_description.type == STRING_TYPE) { | |
1967 return String(value_description.stringDescription); | |
1968 } else { | |
1969 throw new Error("Unknown type"); | |
1970 } | |
1971 } else if ("value" in value_description) { | |
1972 return value_description.value; | |
1973 } else if (value_description.type == UNDEFINED_TYPE) { | |
1974 return UNDEFINED; | |
1975 } else if (value_description.type == NULL_TYPE) { | |
1976 return null; | |
1977 } else { | |
1978 throw new Error("Failed to parse value description"); | |
1979 } | |
1980 }; | |
1981 | |
1982 | |
1983 DebugCommandProcessor.prototype.setVariableValueRequest_ = | |
1984 function(request, response) { | |
1985 if (!request.arguments) { | |
1986 response.failed('Missing arguments'); | |
1987 return; | |
1988 } | |
1989 | |
1990 if (IS_UNDEFINED(request.arguments.name)) { | |
1991 response.failed('Missing variable name'); | |
1992 } | |
1993 var variable_name = request.arguments.name; | |
1994 | |
1995 var scope_description = request.arguments.scope; | |
1996 | |
1997 // Get the frame or function for which the scope is requested. | |
1998 var scope_holder = this.resolveScopeHolder_(scope_description); | |
1999 | |
2000 if (IS_UNDEFINED(scope_description.number)) { | |
2001 response.failed('Missing scope number'); | |
2002 } | |
2003 var scope_index = builtins.$toNumber(scope_description.number); | |
2004 | |
2005 var scope = scope_holder.scope(scope_index); | |
2006 | |
2007 var new_value = | |
2008 DebugCommandProcessor.resolveValue_(request.arguments.newValue); | |
2009 | |
2010 scope.setVariableValue(variable_name, new_value); | |
2011 | |
2012 var new_value_mirror = MakeMirror(new_value); | |
2013 | |
2014 response.body = { | |
2015 newValue: new_value_mirror | |
2016 }; | |
2017 }; | |
2018 | |
2019 | |
2020 DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { | |
2021 if (!request.arguments) { | |
2022 return response.failed('Missing arguments'); | |
2023 } | |
2024 | |
2025 // Pull out arguments. | |
2026 var expression = request.arguments.expression; | |
2027 var frame = request.arguments.frame; | |
2028 var global = request.arguments.global; | |
2029 var disable_break = request.arguments.disable_break; | |
2030 var additional_context = request.arguments.additional_context; | |
2031 | |
2032 // The expression argument could be an integer so we convert it to a | |
2033 // string. | |
2034 try { | |
2035 expression = String(expression); | |
2036 } catch(e) { | |
2037 return response.failed('Failed to convert expression argument to string'); | |
2038 } | |
2039 | |
2040 // Check for legal arguments. | |
2041 if (!IS_UNDEFINED(frame) && global) { | |
2042 return response.failed('Arguments "frame" and "global" are exclusive'); | |
2043 } | |
2044 | |
2045 var additional_context_object; | |
2046 if (additional_context) { | |
2047 additional_context_object = {}; | |
2048 for (var i = 0; i < additional_context.length; i++) { | |
2049 var mapping = additional_context[i]; | |
2050 | |
2051 if (!IS_STRING(mapping.name)) { | |
2052 return response.failed("Context element #" + i + | |
2053 " doesn't contain name:string property"); | |
2054 } | |
2055 | |
2056 var raw_value = DebugCommandProcessor.resolveValue_(mapping); | |
2057 additional_context_object[mapping.name] = raw_value; | |
2058 } | |
2059 } | |
2060 | |
2061 // Global evaluate. | |
2062 if (global) { | |
2063 // Evaluate in the native context. | |
2064 response.body = this.exec_state_.evaluateGlobal( | |
2065 expression, Boolean(disable_break), additional_context_object); | |
2066 return; | |
2067 } | |
2068 | |
2069 // Default value for disable_break is true. | |
2070 if (IS_UNDEFINED(disable_break)) { | |
2071 disable_break = true; | |
2072 } | |
2073 | |
2074 // No frames no evaluate in frame. | |
2075 if (this.exec_state_.frameCount() == 0) { | |
2076 return response.failed('No frames'); | |
2077 } | |
2078 | |
2079 // Check whether a frame was specified. | |
2080 if (!IS_UNDEFINED(frame)) { | |
2081 var frame_number = builtins.$toNumber(frame); | |
2082 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { | |
2083 return response.failed('Invalid frame "' + frame + '"'); | |
2084 } | |
2085 // Evaluate in the specified frame. | |
2086 response.body = this.exec_state_.frame(frame_number).evaluate( | |
2087 expression, Boolean(disable_break), additional_context_object); | |
2088 return; | |
2089 } else { | |
2090 // Evaluate in the selected frame. | |
2091 response.body = this.exec_state_.frame().evaluate( | |
2092 expression, Boolean(disable_break), additional_context_object); | |
2093 return; | |
2094 } | |
2095 }; | |
2096 | |
2097 | |
2098 DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { | |
2099 if (!request.arguments) { | |
2100 return response.failed('Missing arguments'); | |
2101 } | |
2102 | |
2103 // Pull out arguments. | |
2104 var handles = request.arguments.handles; | |
2105 | |
2106 // Check for legal arguments. | |
2107 if (IS_UNDEFINED(handles)) { | |
2108 return response.failed('Argument "handles" missing'); | |
2109 } | |
2110 | |
2111 // Set 'includeSource' option for script lookup. | |
2112 if (!IS_UNDEFINED(request.arguments.includeSource)) { | |
2113 var includeSource = builtins.$toBoolean(request.arguments.includeSource); | |
2114 response.setOption('includeSource', includeSource); | |
2115 } | |
2116 | |
2117 // Lookup handles. | |
2118 var mirrors = {}; | |
2119 for (var i = 0; i < handles.length; i++) { | |
2120 var handle = handles[i]; | |
2121 var mirror = LookupMirror(handle); | |
2122 if (!mirror) { | |
2123 return response.failed('Object #' + handle + '# not found'); | |
2124 } | |
2125 mirrors[handle] = mirror; | |
2126 } | |
2127 response.body = mirrors; | |
2128 }; | |
2129 | |
2130 | |
2131 DebugCommandProcessor.prototype.referencesRequest_ = | |
2132 function(request, response) { | |
2133 if (!request.arguments) { | |
2134 return response.failed('Missing arguments'); | |
2135 } | |
2136 | |
2137 // Pull out arguments. | |
2138 var type = request.arguments.type; | |
2139 var handle = request.arguments.handle; | |
2140 | |
2141 // Check for legal arguments. | |
2142 if (IS_UNDEFINED(type)) { | |
2143 return response.failed('Argument "type" missing'); | |
2144 } | |
2145 if (IS_UNDEFINED(handle)) { | |
2146 return response.failed('Argument "handle" missing'); | |
2147 } | |
2148 if (type != 'referencedBy' && type != 'constructedBy') { | |
2149 return response.failed('Invalid type "' + type + '"'); | |
2150 } | |
2151 | |
2152 // Lookup handle and return objects with references the object. | |
2153 var mirror = LookupMirror(handle); | |
2154 if (mirror) { | |
2155 if (type == 'referencedBy') { | |
2156 response.body = mirror.referencedBy(); | |
2157 } else { | |
2158 response.body = mirror.constructedBy(); | |
2159 } | |
2160 } else { | |
2161 return response.failed('Object #' + handle + '# not found'); | |
2162 } | |
2163 }; | |
2164 | |
2165 | |
2166 DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) { | |
2167 // No frames no source. | |
2168 if (this.exec_state_.frameCount() == 0) { | |
2169 return response.failed('No source'); | |
2170 } | |
2171 | |
2172 var from_line; | |
2173 var to_line; | |
2174 var frame = this.exec_state_.frame(); | |
2175 if (request.arguments) { | |
2176 // Pull out arguments. | |
2177 from_line = request.arguments.fromLine; | |
2178 to_line = request.arguments.toLine; | |
2179 | |
2180 if (!IS_UNDEFINED(request.arguments.frame)) { | |
2181 var frame_number = builtins.$toNumber(request.arguments.frame); | |
2182 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { | |
2183 return response.failed('Invalid frame "' + frame + '"'); | |
2184 } | |
2185 frame = this.exec_state_.frame(frame_number); | |
2186 } | |
2187 } | |
2188 | |
2189 // Get the script selected. | |
2190 var script = frame.func().script(); | |
2191 if (!script) { | |
2192 return response.failed('No source'); | |
2193 } | |
2194 | |
2195 // Get the source slice and fill it into the response. | |
2196 var slice = script.sourceSlice(from_line, to_line); | |
2197 if (!slice) { | |
2198 return response.failed('Invalid line interval'); | |
2199 } | |
2200 response.body = {}; | |
2201 response.body.source = slice.sourceText(); | |
2202 response.body.fromLine = slice.from_line; | |
2203 response.body.toLine = slice.to_line; | |
2204 response.body.fromPosition = slice.from_position; | |
2205 response.body.toPosition = slice.to_position; | |
2206 response.body.totalLines = script.lineCount(); | |
2207 }; | |
2208 | |
2209 | |
2210 DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { | |
2211 var types = ScriptTypeFlag(Debug.ScriptType.Normal); | |
2212 var includeSource = false; | |
2213 var idsToInclude = null; | |
2214 if (request.arguments) { | |
2215 // Pull out arguments. | |
2216 if (!IS_UNDEFINED(request.arguments.types)) { | |
2217 types = builtins.$toNumber(request.arguments.types); | |
2218 if (isNaN(types) || types < 0) { | |
2219 return response.failed('Invalid types "' + | |
2220 request.arguments.types + '"'); | |
2221 } | |
2222 } | |
2223 | |
2224 if (!IS_UNDEFINED(request.arguments.includeSource)) { | |
2225 includeSource = builtins.$toBoolean(request.arguments.includeSource); | |
2226 response.setOption('includeSource', includeSource); | |
2227 } | |
2228 | |
2229 if (IS_ARRAY(request.arguments.ids)) { | |
2230 idsToInclude = {}; | |
2231 var ids = request.arguments.ids; | |
2232 for (var i = 0; i < ids.length; i++) { | |
2233 idsToInclude[ids[i]] = true; | |
2234 } | |
2235 } | |
2236 | |
2237 var filterStr = null; | |
2238 var filterNum = null; | |
2239 if (!IS_UNDEFINED(request.arguments.filter)) { | |
2240 var num = builtins.$toNumber(request.arguments.filter); | |
2241 if (!isNaN(num)) { | |
2242 filterNum = num; | |
2243 } | |
2244 filterStr = request.arguments.filter; | |
2245 } | |
2246 } | |
2247 | |
2248 // Collect all scripts in the heap. | |
2249 var scripts = %DebugGetLoadedScripts(); | |
2250 | |
2251 response.body = []; | |
2252 | |
2253 for (var i = 0; i < scripts.length; i++) { | |
2254 if (idsToInclude && !idsToInclude[scripts[i].id]) { | |
2255 continue; | |
2256 } | |
2257 if (filterStr || filterNum) { | |
2258 var script = scripts[i]; | |
2259 var found = false; | |
2260 if (filterNum && !found) { | |
2261 if (script.id && script.id === filterNum) { | |
2262 found = true; | |
2263 } | |
2264 } | |
2265 if (filterStr && !found) { | |
2266 if (script.name && script.name.indexOf(filterStr) >= 0) { | |
2267 found = true; | |
2268 } | |
2269 } | |
2270 if (!found) continue; | |
2271 } | |
2272 if (types & ScriptTypeFlag(scripts[i].type)) { | |
2273 response.body.push(MakeMirror(scripts[i])); | |
2274 } | |
2275 } | |
2276 }; | |
2277 | |
2278 | |
2279 DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) { | |
2280 // Get the number of threads. | |
2281 var total_threads = this.exec_state_.threadCount(); | |
2282 | |
2283 // Get information for all threads. | |
2284 var threads = []; | |
2285 for (var i = 0; i < total_threads; i++) { | |
2286 var details = %GetThreadDetails(this.exec_state_.break_id, i); | |
2287 var thread_info = { current: details[0], | |
2288 id: details[1] | |
2289 }; | |
2290 threads.push(thread_info); | |
2291 } | |
2292 | |
2293 // Create the response body. | |
2294 response.body = { | |
2295 totalThreads: total_threads, | |
2296 threads: threads | |
2297 }; | |
2298 }; | |
2299 | |
2300 | |
2301 DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) { | |
2302 response.running = false; | |
2303 }; | |
2304 | |
2305 | |
2306 DebugCommandProcessor.prototype.versionRequest_ = function(request, response) { | |
2307 response.body = { | |
2308 V8Version: %GetV8Version() | |
2309 }; | |
2310 }; | |
2311 | |
2312 | |
2313 DebugCommandProcessor.prototype.changeLiveRequest_ = function( | |
2314 request, response) { | |
2315 if (!request.arguments) { | |
2316 return response.failed('Missing arguments'); | |
2317 } | |
2318 var script_id = request.arguments.script_id; | |
2319 var preview_only = !!request.arguments.preview_only; | |
2320 | |
2321 var scripts = %DebugGetLoadedScripts(); | |
2322 | |
2323 var the_script = null; | |
2324 for (var i = 0; i < scripts.length; i++) { | |
2325 if (scripts[i].id == script_id) { | |
2326 the_script = scripts[i]; | |
2327 } | |
2328 } | |
2329 if (!the_script) { | |
2330 response.failed('Script not found'); | |
2331 return; | |
2332 } | |
2333 | |
2334 var change_log = new Array(); | |
2335 | |
2336 if (!IS_STRING(request.arguments.new_source)) { | |
2337 throw "new_source argument expected"; | |
2338 } | |
2339 | |
2340 var new_source = request.arguments.new_source; | |
2341 | |
2342 var result_description; | |
2343 try { | |
2344 result_description = Debug.LiveEdit.SetScriptSource(the_script, | |
2345 new_source, preview_only, change_log); | |
2346 } catch (e) { | |
2347 if (e instanceof Debug.LiveEdit.Failure && "details" in e) { | |
2348 response.failed(e.message, e.details); | |
2349 return; | |
2350 } | |
2351 throw e; | |
2352 } | |
2353 response.body = {change_log: change_log, result: result_description}; | |
2354 | |
2355 if (!preview_only && !this.running_ && result_description.stack_modified) { | |
2356 response.body.stepin_recommended = true; | |
2357 } | |
2358 }; | |
2359 | |
2360 | |
2361 DebugCommandProcessor.prototype.restartFrameRequest_ = function( | |
2362 request, response) { | |
2363 if (!request.arguments) { | |
2364 return response.failed('Missing arguments'); | |
2365 } | |
2366 var frame = request.arguments.frame; | |
2367 | |
2368 // No frames to evaluate in frame. | |
2369 if (this.exec_state_.frameCount() == 0) { | |
2370 return response.failed('No frames'); | |
2371 } | |
2372 | |
2373 var frame_mirror; | |
2374 // Check whether a frame was specified. | |
2375 if (!IS_UNDEFINED(frame)) { | |
2376 var frame_number = builtins.$toNumber(frame); | |
2377 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { | |
2378 return response.failed('Invalid frame "' + frame + '"'); | |
2379 } | |
2380 // Restart specified frame. | |
2381 frame_mirror = this.exec_state_.frame(frame_number); | |
2382 } else { | |
2383 // Restart selected frame. | |
2384 frame_mirror = this.exec_state_.frame(); | |
2385 } | |
2386 | |
2387 var result_description = Debug.LiveEdit.RestartFrame(frame_mirror); | |
2388 response.body = {result: result_description}; | |
2389 }; | |
2390 | |
2391 | |
2392 DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request, | |
2393 response) { | |
2394 // Check for legal request. | |
2395 if (!request.arguments) { | |
2396 response.failed('Missing arguments'); | |
2397 return; | |
2398 } | |
2399 | |
2400 // Pull out arguments. | |
2401 var flags = request.arguments.flags; | |
2402 | |
2403 response.body = { flags: [] }; | |
2404 if (!IS_UNDEFINED(flags)) { | |
2405 for (var i = 0; i < flags.length; i++) { | |
2406 var name = flags[i].name; | |
2407 var debugger_flag = debugger_flags[name]; | |
2408 if (!debugger_flag) { | |
2409 continue; | |
2410 } | |
2411 if ('value' in flags[i]) { | |
2412 debugger_flag.setValue(flags[i].value); | |
2413 } | |
2414 response.body.flags.push({ name: name, value: debugger_flag.getValue() }); | |
2415 } | |
2416 } else { | |
2417 for (var name in debugger_flags) { | |
2418 var value = debugger_flags[name].getValue(); | |
2419 response.body.flags.push({ name: name, value: value }); | |
2420 } | |
2421 } | |
2422 }; | |
2423 | |
2424 | |
2425 DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) { | |
2426 var flags = request.arguments.flags; | |
2427 if (!flags) flags = ''; | |
2428 %SetFlags(flags); | |
2429 }; | |
2430 | |
2431 | |
2432 DebugCommandProcessor.prototype.gcRequest_ = function(request, response) { | |
2433 var type = request.arguments.type; | |
2434 if (!type) type = 'all'; | |
2435 | |
2436 var before = %GetHeapUsage(); | |
2437 %CollectGarbage(type); | |
2438 var after = %GetHeapUsage(); | |
2439 | |
2440 response.body = { "before": before, "after": after }; | |
2441 }; | |
2442 | |
2443 | |
2444 DebugCommandProcessor.prototype.dispatch_ = (function() { | |
2445 var proto = DebugCommandProcessor.prototype; | |
2446 return { | |
2447 "continue": proto.continueRequest_, | |
2448 "break" : proto.breakRequest_, | |
2449 "setbreakpoint" : proto.setBreakPointRequest_, | |
2450 "changebreakpoint": proto.changeBreakPointRequest_, | |
2451 "clearbreakpoint": proto.clearBreakPointRequest_, | |
2452 "clearbreakpointgroup": proto.clearBreakPointGroupRequest_, | |
2453 "disconnect": proto.disconnectRequest_, | |
2454 "setexceptionbreak": proto.setExceptionBreakRequest_, | |
2455 "listbreakpoints": proto.listBreakpointsRequest_, | |
2456 "backtrace": proto.backtraceRequest_, | |
2457 "frame": proto.frameRequest_, | |
2458 "scopes": proto.scopesRequest_, | |
2459 "scope": proto.scopeRequest_, | |
2460 "setvariablevalue": proto.setVariableValueRequest_, | |
2461 "evaluate": proto.evaluateRequest_, | |
2462 "lookup": proto.lookupRequest_, | |
2463 "references": proto.referencesRequest_, | |
2464 "source": proto.sourceRequest_, | |
2465 "scripts": proto.scriptsRequest_, | |
2466 "threads": proto.threadsRequest_, | |
2467 "suspend": proto.suspendRequest_, | |
2468 "version": proto.versionRequest_, | |
2469 "changelive": proto.changeLiveRequest_, | |
2470 "restartframe": proto.restartFrameRequest_, | |
2471 "flags": proto.debuggerFlagsRequest_, | |
2472 "v8flag": proto.v8FlagsRequest_, | |
2473 "gc": proto.gcRequest_, | |
2474 }; | |
2475 })(); | |
2476 | |
2477 | |
2478 // Check whether the previously processed command caused the VM to become | |
2479 // running. | |
2480 DebugCommandProcessor.prototype.isRunning = function() { | |
2481 return this.running_; | |
2482 }; | |
2483 | |
2484 | |
2485 DebugCommandProcessor.prototype.systemBreak = function(cmd, args) { | |
2486 return %SystemBreak(); | |
2487 }; | |
2488 | |
2489 | |
2490 /** | |
2491 * Convert an Object to its debugger protocol representation. The representation | |
2492 * may be serilized to a JSON object using JSON.stringify(). | |
2493 * This implementation simply runs through all string property names, converts | |
2494 * each property value to a protocol value and adds the property to the result | |
2495 * object. For type "object" the function will be called recursively. Note that | |
2496 * circular structures will cause infinite recursion. | |
2497 * @param {Object} object The object to format as protocol object. | |
2498 * @param {MirrorSerializer} mirror_serializer The serializer to use if any | |
2499 * mirror objects are encountered. | |
2500 * @return {Object} Protocol object value. | |
2501 */ | |
2502 function ObjectToProtocolObject_(object, mirror_serializer) { | |
2503 var content = {}; | |
2504 for (var key in object) { | |
2505 // Only consider string keys. | |
2506 if (typeof key == 'string') { | |
2507 // Format the value based on its type. | |
2508 var property_value_json = ValueToProtocolValue_(object[key], | |
2509 mirror_serializer); | |
2510 // Add the property if relevant. | |
2511 if (!IS_UNDEFINED(property_value_json)) { | |
2512 content[key] = property_value_json; | |
2513 } | |
2514 } | |
2515 } | |
2516 | |
2517 return content; | |
2518 } | |
2519 | |
2520 | |
2521 /** | |
2522 * Convert an array to its debugger protocol representation. It will convert | |
2523 * each array element to a protocol value. | |
2524 * @param {Array} array The array to format as protocol array. | |
2525 * @param {MirrorSerializer} mirror_serializer The serializer to use if any | |
2526 * mirror objects are encountered. | |
2527 * @return {Array} Protocol array value. | |
2528 */ | |
2529 function ArrayToProtocolArray_(array, mirror_serializer) { | |
2530 var json = []; | |
2531 for (var i = 0; i < array.length; i++) { | |
2532 json.push(ValueToProtocolValue_(array[i], mirror_serializer)); | |
2533 } | |
2534 return json; | |
2535 } | |
2536 | |
2537 | |
2538 /** | |
2539 * Convert a value to its debugger protocol representation. | |
2540 * @param {*} value The value to format as protocol value. | |
2541 * @param {MirrorSerializer} mirror_serializer The serializer to use if any | |
2542 * mirror objects are encountered. | |
2543 * @return {*} Protocol value. | |
2544 */ | |
2545 function ValueToProtocolValue_(value, mirror_serializer) { | |
2546 // Format the value based on its type. | |
2547 var json; | |
2548 switch (typeof value) { | |
2549 case 'object': | |
2550 if (value instanceof Mirror) { | |
2551 json = mirror_serializer.serializeValue(value); | |
2552 } else if (IS_ARRAY(value)){ | |
2553 json = ArrayToProtocolArray_(value, mirror_serializer); | |
2554 } else { | |
2555 json = ObjectToProtocolObject_(value, mirror_serializer); | |
2556 } | |
2557 break; | |
2558 | |
2559 case 'boolean': | |
2560 case 'string': | |
2561 case 'number': | |
2562 json = value; | |
2563 break; | |
2564 | |
2565 default: | |
2566 json = null; | |
2567 } | |
2568 return json; | |
2569 } | |
OLD | NEW |