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