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