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