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