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