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