• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Default number of frames to include in the response to backtrace request.
29const kDefaultBacktraceLength = 10;
30
31const Debug = {};
32
33// Regular expression to skip "crud" at the beginning of a source line which is
34// not really code. Currently the regular expression matches whitespace and
35// comments.
36const sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
37
38// Debug events which can occour in the V8 JavaScript engine. These originate
39// from the API include file debug.h.
40Debug.DebugEvent = { Break: 1,
41                     Exception: 2,
42                     NewFunction: 3,
43                     BeforeCompile: 4,
44                     AfterCompile: 5,
45                     ScriptCollected: 6 };
46
47// Types of exceptions that can be broken upon.
48Debug.ExceptionBreak = { All : 0,
49                         Uncaught: 1 };
50
51// The different types of steps.
52Debug.StepAction = { StepOut: 0,
53                     StepNext: 1,
54                     StepIn: 2,
55                     StepMin: 3,
56                     StepInMin: 4 };
57
58// The different types of scripts matching enum ScriptType in objects.h.
59Debug.ScriptType = { Native: 0,
60                     Extension: 1,
61                     Normal: 2 };
62
63// The different types of script compilations matching enum
64// Script::CompilationType in objects.h.
65Debug.ScriptCompilationType = { Host: 0,
66                                Eval: 1,
67                                JSON: 2 };
68
69// The different script break point types.
70Debug.ScriptBreakPointType = { ScriptId: 0,
71                               ScriptName: 1 };
72
73function ScriptTypeFlag(type) {
74  return (1 << type);
75}
76
77// Globals.
78var next_response_seq = 0;
79var next_break_point_number = 1;
80var break_points = [];
81var script_break_points = [];
82
83
84// Create a new break point object and add it to the list of break points.
85function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
86  var break_point = new BreakPoint(source_position, opt_line, opt_column, opt_script_break_point);
87  break_points.push(break_point);
88  return break_point;
89}
90
91
92// Object representing a break point.
93// NOTE: This object does not have a reference to the function having break
94// point as this would cause function not to be garbage collected when it is
95// not used any more. We do not want break points to keep functions alive.
96function BreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
97  this.source_position_ = source_position;
98  this.source_line_ = opt_line;
99  this.source_column_ = opt_column;
100  if (opt_script_break_point) {
101    this.script_break_point_ = opt_script_break_point;
102  } else {
103    this.number_ = next_break_point_number++;
104  }
105  this.hit_count_ = 0;
106  this.active_ = true;
107  this.condition_ = null;
108  this.ignoreCount_ = 0;
109}
110
111
112BreakPoint.prototype.number = function() {
113  return this.number_;
114};
115
116
117BreakPoint.prototype.func = function() {
118  return this.func_;
119};
120
121
122BreakPoint.prototype.source_position = function() {
123  return this.source_position_;
124};
125
126
127BreakPoint.prototype.hit_count = function() {
128  return this.hit_count_;
129};
130
131
132BreakPoint.prototype.active = function() {
133  if (this.script_break_point()) {
134    return this.script_break_point().active();
135  }
136  return this.active_;
137};
138
139
140BreakPoint.prototype.condition = function() {
141  if (this.script_break_point() && this.script_break_point().condition()) {
142    return this.script_break_point().condition();
143  }
144  return this.condition_;
145};
146
147
148BreakPoint.prototype.ignoreCount = function() {
149  return this.ignoreCount_;
150};
151
152
153BreakPoint.prototype.script_break_point = function() {
154  return this.script_break_point_;
155};
156
157
158BreakPoint.prototype.enable = function() {
159  this.active_ = true;
160};
161
162
163BreakPoint.prototype.disable = function() {
164  this.active_ = false;
165};
166
167
168BreakPoint.prototype.setCondition = function(condition) {
169  this.condition_ = condition;
170};
171
172
173BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
174  this.ignoreCount_ = ignoreCount;
175};
176
177
178BreakPoint.prototype.isTriggered = function(exec_state) {
179  // Break point not active - not triggered.
180  if (!this.active()) return false;
181
182  // Check for conditional break point.
183  if (this.condition()) {
184    // If break point has condition try to evaluate it in the top frame.
185    try {
186      var mirror = exec_state.frame(0).evaluate(this.condition());
187      // If no sensible mirror or non true value break point not triggered.
188      if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) {
189        return false;
190      }
191    } catch (e) {
192      // Exception evaluating condition counts as not triggered.
193      return false;
194    }
195  }
196
197  // Update the hit count.
198  this.hit_count_++;
199  if (this.script_break_point_) {
200    this.script_break_point_.hit_count_++;
201  }
202
203  // If the break point has an ignore count it is not triggered.
204  if (this.ignoreCount_ > 0) {
205    this.ignoreCount_--;
206    return false;
207  }
208
209  // Break point triggered.
210  return true;
211};
212
213
214// Function called from the runtime when a break point is hit. Returns true if
215// the break point is triggered and supposed to break execution.
216function IsBreakPointTriggered(break_id, break_point) {
217  return break_point.isTriggered(MakeExecutionState(break_id));
218}
219
220
221// Object representing a script break point. The script is referenced by its
222// script name or script id and the break point is represented as line and
223// column.
224function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
225                          opt_groupId) {
226  this.type_ = type;
227  if (type == Debug.ScriptBreakPointType.ScriptId) {
228    this.script_id_ = script_id_or_name;
229  } else {  // type == Debug.ScriptBreakPointType.ScriptName
230    this.script_name_ = script_id_or_name;
231  }
232  this.line_ = opt_line || 0;
233  this.column_ = opt_column;
234  this.groupId_ = opt_groupId;
235  this.hit_count_ = 0;
236  this.active_ = true;
237  this.condition_ = null;
238  this.ignoreCount_ = 0;
239}
240
241
242ScriptBreakPoint.prototype.number = function() {
243  return this.number_;
244};
245
246
247ScriptBreakPoint.prototype.groupId = function() {
248  return this.groupId_;
249};
250
251
252ScriptBreakPoint.prototype.type = function() {
253  return this.type_;
254};
255
256
257ScriptBreakPoint.prototype.script_id = function() {
258  return this.script_id_;
259};
260
261
262ScriptBreakPoint.prototype.script_name = function() {
263  return this.script_name_;
264};
265
266
267ScriptBreakPoint.prototype.line = function() {
268  return this.line_;
269};
270
271
272ScriptBreakPoint.prototype.column = function() {
273  return this.column_;
274};
275
276
277ScriptBreakPoint.prototype.hit_count = function() {
278  return this.hit_count_;
279};
280
281
282ScriptBreakPoint.prototype.active = function() {
283  return this.active_;
284};
285
286
287ScriptBreakPoint.prototype.condition = function() {
288  return this.condition_;
289};
290
291
292ScriptBreakPoint.prototype.ignoreCount = function() {
293  return this.ignoreCount_;
294};
295
296
297ScriptBreakPoint.prototype.enable = function() {
298  this.active_ = true;
299};
300
301
302ScriptBreakPoint.prototype.disable = function() {
303  this.active_ = false;
304};
305
306
307ScriptBreakPoint.prototype.setCondition = function(condition) {
308  this.condition_ = condition;
309};
310
311
312ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
313  this.ignoreCount_ = ignoreCount;
314
315  // Set ignore count on all break points created from this script break point.
316  for (var i = 0; i < break_points.length; i++) {
317    if (break_points[i].script_break_point() === this) {
318      break_points[i].setIgnoreCount(ignoreCount);
319    }
320  }
321};
322
323
324// Check whether a script matches this script break point. Currently this is
325// only based on script name.
326ScriptBreakPoint.prototype.matchesScript = function(script) {
327  if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
328    return this.script_id_ == script.id;
329  } else {  // this.type_ == Debug.ScriptBreakPointType.ScriptName
330    return this.script_name_ == script.name &&
331           script.line_offset <= this.line_  &&
332           this.line_ < script.line_offset + script.lineCount();
333  }
334};
335
336
337// Set the script break point in a script.
338ScriptBreakPoint.prototype.set = function (script) {
339  var column = this.column();
340  var line = this.line();
341  // If the column is undefined the break is on the line. To help locate the
342  // first piece of breakable code on the line try to find the column on the
343  // line which contains some source.
344  if (IS_UNDEFINED(column)) {
345    var source_line = script.sourceLine(this.line());
346
347    // Allocate array for caching the columns where the actual source starts.
348    if (!script.sourceColumnStart_) {
349      script.sourceColumnStart_ = new Array(script.lineCount());
350    }
351
352    // Fill cache if needed and get column where the actual source starts.
353    if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
354      script.sourceColumnStart_[line] =
355          source_line.match(sourceLineBeginningSkip)[0].length;
356    }
357    column = script.sourceColumnStart_[line];
358  }
359
360  // Convert the line and column into an absolute position within the script.
361  var pos = Debug.findScriptSourcePosition(script, this.line(), column);
362
363  // If the position is not found in the script (the script might be shorter
364  // than it used to be) just ignore it.
365  if (pos === null) return;
366
367  // Create a break point object and set the break point.
368  break_point = MakeBreakPoint(pos, this.line(), this.column(), this);
369  break_point.setIgnoreCount(this.ignoreCount());
370  %SetScriptBreakPoint(script, pos, break_point);
371
372  return break_point;
373};
374
375
376// Clear all the break points created from this script break point
377ScriptBreakPoint.prototype.clear = function () {
378  var remaining_break_points = [];
379  for (var i = 0; i < break_points.length; i++) {
380    if (break_points[i].script_break_point() &&
381        break_points[i].script_break_point() === this) {
382      %ClearBreakPoint(break_points[i]);
383    } else {
384      remaining_break_points.push(break_points[i]);
385    }
386  }
387  break_points = remaining_break_points;
388};
389
390
391// Function called from runtime when a new script is compiled to set any script
392// break points set in this script.
393function UpdateScriptBreakPoints(script) {
394  for (var i = 0; i < script_break_points.length; i++) {
395    if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName &&
396        script_break_points[i].matchesScript(script)) {
397      script_break_points[i].set(script);
398    }
399  }
400}
401
402
403Debug.setListener = function(listener, opt_data) {
404  if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
405    throw new Error('Parameters have wrong types.');
406  }
407  %SetDebugEventListener(listener, opt_data);
408};
409
410
411Debug.breakExecution = function(f) {
412  %Break();
413};
414
415Debug.breakLocations = function(f) {
416  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
417  return %GetBreakLocations(f);
418};
419
420// Returns a Script object. If the parameter is a function the return value
421// is the script in which the function is defined. If the parameter is a string
422// the return value is the script for which the script name has that string
423// value.  If it is a regexp and there is a unique script whose name matches
424// we return that, otherwise undefined.
425Debug.findScript = function(func_or_script_name) {
426  if (IS_FUNCTION(func_or_script_name)) {
427    return %FunctionGetScript(func_or_script_name);
428  } else if (IS_REGEXP(func_or_script_name)) {
429    var scripts = Debug.scripts();
430    var last_result = null;
431    var result_count = 0;
432    for (var i in scripts) {
433      var script = scripts[i];
434      if (func_or_script_name.test(script.name)) {
435        last_result = script;
436        result_count++;
437      }
438    }
439    // Return the unique script matching the regexp.  If there are more
440    // than one we don't return a value since there is no good way to
441    // decide which one to return.  Returning a "random" one, say the
442    // first, would introduce nondeterminism (or something close to it)
443    // because the order is the heap iteration order.
444    if (result_count == 1) {
445      return last_result;
446    } else {
447      return undefined;
448    }
449  } else {
450    return %GetScript(func_or_script_name);
451  }
452};
453
454// Returns the script source. If the parameter is a function the return value
455// is the script source for the script in which the function is defined. If the
456// parameter is a string the return value is the script for which the script
457// name has that string value.
458Debug.scriptSource = function(func_or_script_name) {
459  return this.findScript(func_or_script_name).source;
460};
461
462Debug.source = function(f) {
463  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
464  return %FunctionGetSourceCode(f);
465};
466
467Debug.disassemble = function(f) {
468  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
469  return %DebugDisassembleFunction(f);
470};
471
472Debug.disassembleConstructor = function(f) {
473  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
474  return %DebugDisassembleConstructor(f);
475};
476
477Debug.sourcePosition = function(f) {
478  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
479  return %FunctionGetScriptSourcePosition(f);
480};
481
482
483Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
484  var script = %FunctionGetScript(func);
485  var script_offset = %FunctionGetScriptSourcePosition(func);
486  return script.locationFromLine(opt_line, opt_column, script_offset);
487}
488
489
490// Returns the character position in a script based on a line number and an
491// optional position within that line.
492Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
493  var location = script.locationFromLine(opt_line, opt_column);
494  return location ? location.position : null;
495}
496
497
498Debug.findBreakPoint = function(break_point_number, remove) {
499  var break_point;
500  for (var i = 0; i < break_points.length; i++) {
501    if (break_points[i].number() == break_point_number) {
502      break_point = break_points[i];
503      // Remove the break point from the list if requested.
504      if (remove) {
505        break_points.splice(i, 1);
506      }
507      break;
508    }
509  }
510  if (break_point) {
511    return break_point;
512  } else {
513    return this.findScriptBreakPoint(break_point_number, remove);
514  }
515};
516
517
518Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
519  if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
520  // Break points in API functions are not supported.
521  if (%FunctionIsAPIFunction(func)) {
522    throw new Error('Cannot set break point in native code.');
523  }
524  // Find source position relative to start of the function
525  var break_position =
526      this.findFunctionSourceLocation(func, opt_line, opt_column).position;
527  var source_position = break_position - this.sourcePosition(func);
528  // Find the script for the function.
529  var script = %FunctionGetScript(func);
530  // Break in builtin JavaScript code is not supported.
531  if (script.type == Debug.ScriptType.Native) {
532    throw new Error('Cannot set break point in native code.');
533  }
534  // If the script for the function has a name convert this to a script break
535  // point.
536  if (script && script.id) {
537    // Adjust the source position to be script relative.
538    source_position += %FunctionGetScriptSourcePosition(func);
539    // Find line and column for the position in the script and set a script
540    // break point from that.
541    var location = script.locationFromPosition(source_position, false);
542    return this.setScriptBreakPointById(script.id,
543                                        location.line, location.column,
544                                        opt_condition);
545  } else {
546    // Set a break point directly on the function.
547    var break_point = MakeBreakPoint(source_position, opt_line, opt_column);
548    %SetFunctionBreakPoint(func, source_position, break_point);
549    break_point.setCondition(opt_condition);
550    return break_point.number();
551  }
552};
553
554
555Debug.enableBreakPoint = function(break_point_number) {
556  var break_point = this.findBreakPoint(break_point_number, false);
557  break_point.enable();
558};
559
560
561Debug.disableBreakPoint = function(break_point_number) {
562  var break_point = this.findBreakPoint(break_point_number, false);
563  break_point.disable();
564};
565
566
567Debug.changeBreakPointCondition = function(break_point_number, condition) {
568  var break_point = this.findBreakPoint(break_point_number, false);
569  break_point.setCondition(condition);
570};
571
572
573Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
574  if (ignoreCount < 0) {
575    throw new Error('Invalid argument');
576  }
577  var break_point = this.findBreakPoint(break_point_number, false);
578  break_point.setIgnoreCount(ignoreCount);
579};
580
581
582Debug.clearBreakPoint = function(break_point_number) {
583  var break_point = this.findBreakPoint(break_point_number, true);
584  if (break_point) {
585    return %ClearBreakPoint(break_point);
586  } else {
587    break_point = this.findScriptBreakPoint(break_point_number, true);
588    if (!break_point) {
589      throw new Error('Invalid breakpoint');
590    }
591  }
592};
593
594
595Debug.clearAllBreakPoints = function() {
596  for (var i = 0; i < break_points.length; i++) {
597    break_point = break_points[i];
598    %ClearBreakPoint(break_point);
599  }
600  break_points = [];
601};
602
603
604Debug.findScriptBreakPoint = function(break_point_number, remove) {
605  var script_break_point;
606  for (var i = 0; i < script_break_points.length; i++) {
607    if (script_break_points[i].number() == break_point_number) {
608      script_break_point = script_break_points[i];
609      // Remove the break point from the list if requested.
610      if (remove) {
611        script_break_point.clear();
612        script_break_points.splice(i,1);
613      }
614      break;
615    }
616  }
617  return script_break_point;
618}
619
620
621// Sets a breakpoint in a script identified through id or name at the
622// specified source line and column within that line.
623Debug.setScriptBreakPoint = function(type, script_id_or_name,
624                                     opt_line, opt_column, opt_condition,
625                                     opt_groupId) {
626  // Create script break point object.
627  var script_break_point =
628      new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
629                           opt_groupId);
630
631  // Assign number to the new script break point and add it.
632  script_break_point.number_ = next_break_point_number++;
633  script_break_point.setCondition(opt_condition);
634  script_break_points.push(script_break_point);
635
636  // Run through all scripts to see if this script break point matches any
637  // loaded scripts.
638  var scripts = this.scripts();
639  for (var i = 0; i < scripts.length; i++) {
640    if (script_break_point.matchesScript(scripts[i])) {
641      script_break_point.set(scripts[i]);
642    }
643  }
644
645  return script_break_point.number();
646}
647
648
649Debug.setScriptBreakPointById = function(script_id,
650                                         opt_line, opt_column,
651                                         opt_condition, opt_groupId) {
652  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
653                                  script_id, opt_line, opt_column,
654                                  opt_condition, opt_groupId);
655}
656
657
658Debug.setScriptBreakPointByName = function(script_name,
659                                           opt_line, opt_column,
660                                           opt_condition, opt_groupId) {
661  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
662                                  script_name, opt_line, opt_column,
663                                  opt_condition, opt_groupId);
664}
665
666
667Debug.enableScriptBreakPoint = function(break_point_number) {
668  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
669  script_break_point.enable();
670};
671
672
673Debug.disableScriptBreakPoint = function(break_point_number) {
674  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
675  script_break_point.disable();
676};
677
678
679Debug.changeScriptBreakPointCondition = function(break_point_number, condition) {
680  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
681  script_break_point.setCondition(condition);
682};
683
684
685Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
686  if (ignoreCount < 0) {
687    throw new Error('Invalid argument');
688  }
689  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
690  script_break_point.setIgnoreCount(ignoreCount);
691};
692
693
694Debug.scriptBreakPoints = function() {
695  return script_break_points;
696}
697
698
699Debug.clearStepping = function() {
700  %ClearStepping();
701}
702
703Debug.setBreakOnException = function() {
704  return %ChangeBreakOnException(Debug.ExceptionBreak.All, true);
705};
706
707Debug.clearBreakOnException = function() {
708  return %ChangeBreakOnException(Debug.ExceptionBreak.All, false);
709};
710
711Debug.setBreakOnUncaughtException = function() {
712  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
713};
714
715Debug.clearBreakOnUncaughtException = function() {
716  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
717};
718
719Debug.showBreakPoints = function(f, full) {
720  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
721  var source = full ? this.scriptSource(f) : this.source(f);
722  var offset = full ? this.sourcePosition(f) : 0;
723  var locations = this.breakLocations(f);
724  if (!locations) return source;
725  locations.sort(function(x, y) { return x - y; });
726  var result = "";
727  var prev_pos = 0;
728  var pos;
729  for (var i = 0; i < locations.length; i++) {
730    pos = locations[i] - offset;
731    result += source.slice(prev_pos, pos);
732    result += "[B" + i + "]";
733    prev_pos = pos;
734  }
735  pos = source.length;
736  result += source.substring(prev_pos, pos);
737  return result;
738};
739
740
741// Get all the scripts currently loaded. Locating all the scripts is based on
742// scanning the heap.
743Debug.scripts = function() {
744  // Collect all scripts in the heap.
745  return %DebugGetLoadedScripts();
746}
747
748function MakeExecutionState(break_id) {
749  return new ExecutionState(break_id);
750}
751
752function ExecutionState(break_id) {
753  this.break_id = break_id;
754  this.selected_frame = 0;
755}
756
757ExecutionState.prototype.prepareStep = function(opt_action, opt_count) {
758  var action = Debug.StepAction.StepIn;
759  if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
760  var count = opt_count ? %ToNumber(opt_count) : 1;
761
762  return %PrepareStep(this.break_id, action, count);
763}
764
765ExecutionState.prototype.evaluateGlobal = function(source, disable_break) {
766  return MakeMirror(
767      %DebugEvaluateGlobal(this.break_id, source, Boolean(disable_break)));
768};
769
770ExecutionState.prototype.frameCount = function() {
771  return %GetFrameCount(this.break_id);
772};
773
774ExecutionState.prototype.threadCount = function() {
775  return %GetThreadCount(this.break_id);
776};
777
778ExecutionState.prototype.frame = function(opt_index) {
779  // If no index supplied return the selected frame.
780  if (opt_index == null) opt_index = this.selected_frame;
781  return new FrameMirror(this.break_id, opt_index);
782};
783
784ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) {
785  return %GetCFrames(this.break_id);
786};
787
788ExecutionState.prototype.setSelectedFrame = function(index) {
789  var i = %ToNumber(index);
790  if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
791  this.selected_frame = i;
792};
793
794ExecutionState.prototype.selectedFrame = function() {
795  return this.selected_frame;
796};
797
798ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
799  return new DebugCommandProcessor(this, opt_is_running);
800};
801
802
803function MakeBreakEvent(exec_state, break_points_hit) {
804  return new BreakEvent(exec_state, break_points_hit);
805}
806
807
808function BreakEvent(exec_state, break_points_hit) {
809  this.exec_state_ = exec_state;
810  this.break_points_hit_ = break_points_hit;
811}
812
813
814BreakEvent.prototype.executionState = function() {
815  return this.exec_state_;
816};
817
818
819BreakEvent.prototype.eventType = function() {
820  return Debug.DebugEvent.Break;
821};
822
823
824BreakEvent.prototype.func = function() {
825  return this.exec_state_.frame(0).func();
826};
827
828
829BreakEvent.prototype.sourceLine = function() {
830  return this.exec_state_.frame(0).sourceLine();
831};
832
833
834BreakEvent.prototype.sourceColumn = function() {
835  return this.exec_state_.frame(0).sourceColumn();
836};
837
838
839BreakEvent.prototype.sourceLineText = function() {
840  return this.exec_state_.frame(0).sourceLineText();
841};
842
843
844BreakEvent.prototype.breakPointsHit = function() {
845  return this.break_points_hit_;
846};
847
848
849BreakEvent.prototype.toJSONProtocol = function() {
850  var o = { seq: next_response_seq++,
851            type: "event",
852            event: "break",
853            body: { invocationText: this.exec_state_.frame(0).invocationText(),
854                  }
855          };
856
857  // Add script related information to the event if available.
858  var script = this.func().script();
859  if (script) {
860    o.body.sourceLine = this.sourceLine(),
861    o.body.sourceColumn = this.sourceColumn(),
862    o.body.sourceLineText = this.sourceLineText(),
863    o.body.script = MakeScriptObject_(script, false);
864  }
865
866  // Add an Array of break points hit if any.
867  if (this.breakPointsHit()) {
868    o.body.breakpoints = [];
869    for (var i = 0; i < this.breakPointsHit().length; i++) {
870      // Find the break point number. For break points originating from a
871      // script break point supply the script break point number.
872      var breakpoint = this.breakPointsHit()[i];
873      var script_break_point = breakpoint.script_break_point();
874      var number;
875      if (script_break_point) {
876        number = script_break_point.number();
877      } else {
878        number = breakpoint.number();
879      }
880      o.body.breakpoints.push(number);
881    }
882  }
883  return JSON.stringify(ObjectToProtocolObject_(o));
884};
885
886
887function MakeExceptionEvent(exec_state, exception, uncaught) {
888  return new ExceptionEvent(exec_state, exception, uncaught);
889}
890
891
892function ExceptionEvent(exec_state, exception, uncaught) {
893  this.exec_state_ = exec_state;
894  this.exception_ = exception;
895  this.uncaught_ = uncaught;
896}
897
898
899ExceptionEvent.prototype.executionState = function() {
900  return this.exec_state_;
901};
902
903
904ExceptionEvent.prototype.eventType = function() {
905  return Debug.DebugEvent.Exception;
906};
907
908
909ExceptionEvent.prototype.exception = function() {
910  return this.exception_;
911}
912
913
914ExceptionEvent.prototype.uncaught = function() {
915  return this.uncaught_;
916}
917
918
919ExceptionEvent.prototype.func = function() {
920  return this.exec_state_.frame(0).func();
921};
922
923
924ExceptionEvent.prototype.sourceLine = function() {
925  return this.exec_state_.frame(0).sourceLine();
926};
927
928
929ExceptionEvent.prototype.sourceColumn = function() {
930  return this.exec_state_.frame(0).sourceColumn();
931};
932
933
934ExceptionEvent.prototype.sourceLineText = function() {
935  return this.exec_state_.frame(0).sourceLineText();
936};
937
938
939ExceptionEvent.prototype.toJSONProtocol = function() {
940  var o = new ProtocolMessage();
941  o.event = "exception";
942  o.body = { uncaught: this.uncaught_,
943             exception: MakeMirror(this.exception_)
944           };
945
946  // Exceptions might happen whithout any JavaScript frames.
947  if (this.exec_state_.frameCount() > 0) {
948    o.body.sourceLine = this.sourceLine();
949    o.body.sourceColumn = this.sourceColumn();
950    o.body.sourceLineText = this.sourceLineText();
951
952    // Add script information to the event if available.
953    var script = this.func().script();
954    if (script) {
955      o.body.script = MakeScriptObject_(script, false);
956    }
957  } else {
958    o.body.sourceLine = -1;
959  }
960
961  return o.toJSONProtocol();
962};
963
964
965function MakeCompileEvent(exec_state, script, before) {
966  return new CompileEvent(exec_state, script, before);
967}
968
969
970function CompileEvent(exec_state, script, before) {
971  this.exec_state_ = exec_state;
972  this.script_ = MakeMirror(script);
973  this.before_ = before;
974}
975
976
977CompileEvent.prototype.executionState = function() {
978  return this.exec_state_;
979};
980
981
982CompileEvent.prototype.eventType = function() {
983  if (this.before_) {
984    return Debug.DebugEvent.BeforeCompile;
985  } else {
986    return Debug.DebugEvent.AfterCompile;
987  }
988};
989
990
991CompileEvent.prototype.script = function() {
992  return this.script_;
993};
994
995
996CompileEvent.prototype.toJSONProtocol = function() {
997  var o = new ProtocolMessage();
998  o.running = true;
999  if (this.before_) {
1000    o.event = "beforeCompile";
1001  } else {
1002    o.event = "afterCompile";
1003  }
1004  o.body = {};
1005  o.body.script = this.script_;
1006
1007  return o.toJSONProtocol();
1008}
1009
1010
1011function MakeNewFunctionEvent(func) {
1012  return new NewFunctionEvent(func);
1013}
1014
1015
1016function NewFunctionEvent(func) {
1017  this.func = func;
1018}
1019
1020
1021NewFunctionEvent.prototype.eventType = function() {
1022  return Debug.DebugEvent.NewFunction;
1023};
1024
1025
1026NewFunctionEvent.prototype.name = function() {
1027  return this.func.name;
1028};
1029
1030
1031NewFunctionEvent.prototype.setBreakPoint = function(p) {
1032  Debug.setBreakPoint(this.func, p || 0);
1033};
1034
1035
1036function MakeScriptCollectedEvent(exec_state, id) {
1037  return new ScriptCollectedEvent(exec_state, id);
1038}
1039
1040
1041function ScriptCollectedEvent(exec_state, id) {
1042  this.exec_state_ = exec_state;
1043  this.id_ = id;
1044}
1045
1046
1047ScriptCollectedEvent.prototype.id = function() {
1048  return this.id_;
1049};
1050
1051
1052ScriptCollectedEvent.prototype.executionState = function() {
1053  return this.exec_state_;
1054};
1055
1056
1057ScriptCollectedEvent.prototype.toJSONProtocol = function() {
1058  var o = new ProtocolMessage();
1059  o.running = true;
1060  o.event = "scriptCollected";
1061  o.body = {};
1062  o.body.script = { id: this.id() };
1063  return o.toJSONProtocol();
1064}
1065
1066
1067function MakeScriptObject_(script, include_source) {
1068  var o = { id: script.id(),
1069            name: script.name(),
1070            lineOffset: script.lineOffset(),
1071            columnOffset: script.columnOffset(),
1072            lineCount: script.lineCount(),
1073          };
1074  if (!IS_UNDEFINED(script.data())) {
1075    o.data = script.data();
1076  }
1077  if (include_source) {
1078    o.source = script.source();
1079  }
1080  return o;
1081};
1082
1083
1084function DebugCommandProcessor(exec_state, opt_is_running) {
1085  this.exec_state_ = exec_state;
1086  this.running_ = opt_is_running || false;
1087};
1088
1089
1090DebugCommandProcessor.prototype.processDebugRequest = function (request) {
1091  return this.processDebugJSONRequest(request);
1092}
1093
1094
1095function ProtocolMessage(request) {
1096  // Update sequence number.
1097  this.seq = next_response_seq++;
1098
1099  if (request) {
1100    // If message is based on a request this is a response. Fill the initial
1101    // response from the request.
1102    this.type = 'response';
1103    this.request_seq = request.seq;
1104    this.command = request.command;
1105  } else {
1106    // If message is not based on a request it is a dabugger generated event.
1107    this.type = 'event';
1108  }
1109  this.success = true;
1110  // Handler may set this field to control debugger state.
1111  this.running = undefined;
1112}
1113
1114
1115ProtocolMessage.prototype.setOption = function(name, value) {
1116  if (!this.options_) {
1117    this.options_ = {};
1118  }
1119  this.options_[name] = value;
1120}
1121
1122
1123ProtocolMessage.prototype.failed = function(message) {
1124  this.success = false;
1125  this.message = message;
1126}
1127
1128
1129ProtocolMessage.prototype.toJSONProtocol = function() {
1130  // Encode the protocol header.
1131  var json = {};
1132  json.seq= this.seq;
1133  if (this.request_seq) {
1134    json.request_seq = this.request_seq;
1135  }
1136  json.type = this.type;
1137  if (this.event) {
1138    json.event = this.event;
1139  }
1140  if (this.command) {
1141    json.command = this.command;
1142  }
1143  if (this.success) {
1144    json.success = this.success;
1145  } else {
1146    json.success = false;
1147  }
1148  if (this.body) {
1149    // Encode the body part.
1150    var bodyJson;
1151    var serializer = MakeMirrorSerializer(true, this.options_);
1152    if (this.body instanceof Mirror) {
1153      bodyJson = serializer.serializeValue(this.body);
1154    } else if (this.body instanceof Array) {
1155      bodyJson = [];
1156      for (var i = 0; i < this.body.length; i++) {
1157        if (this.body[i] instanceof Mirror) {
1158          bodyJson.push(serializer.serializeValue(this.body[i]));
1159        } else {
1160          bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
1161        }
1162      }
1163    } else {
1164      bodyJson = ObjectToProtocolObject_(this.body, serializer);
1165    }
1166    json.body = bodyJson;
1167    json.refs = serializer.serializeReferencedObjects();
1168  }
1169  if (this.message) {
1170    json.message = this.message;
1171  }
1172  json.running = this.running;
1173  return JSON.stringify(json);
1174}
1175
1176
1177DebugCommandProcessor.prototype.createResponse = function(request) {
1178  return new ProtocolMessage(request);
1179};
1180
1181
1182DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) {
1183  var request;  // Current request.
1184  var response;  // Generated response.
1185  try {
1186    try {
1187      // Convert the JSON string to an object.
1188      request = %CompileString('(' + json_request + ')', false)();
1189
1190      // Create an initial response.
1191      response = this.createResponse(request);
1192
1193      if (!request.type) {
1194        throw new Error('Type not specified');
1195      }
1196
1197      if (request.type != 'request') {
1198        throw new Error("Illegal type '" + request.type + "' in request");
1199      }
1200
1201      if (!request.command) {
1202        throw new Error('Command not specified');
1203      }
1204
1205      if (request.arguments) {
1206        var args = request.arguments;
1207        // TODO(yurys): remove request.arguments.compactFormat check once
1208        // ChromeDevTools are switched to 'inlineRefs'
1209        if (args.inlineRefs || args.compactFormat) {
1210          response.setOption('inlineRefs', true);
1211        }
1212        if (!IS_UNDEFINED(args.maxStringLength)) {
1213          response.setOption('maxStringLength', args.maxStringLength);
1214        }
1215      }
1216
1217      if (request.command == 'continue') {
1218        this.continueRequest_(request, response);
1219      } else if (request.command == 'break') {
1220        this.breakRequest_(request, response);
1221      } else if (request.command == 'setbreakpoint') {
1222        this.setBreakPointRequest_(request, response);
1223      } else if (request.command == 'changebreakpoint') {
1224        this.changeBreakPointRequest_(request, response);
1225      } else if (request.command == 'clearbreakpoint') {
1226        this.clearBreakPointRequest_(request, response);
1227      } else if (request.command == 'clearbreakpointgroup') {
1228        this.clearBreakPointGroupRequest_(request, response);
1229      } else if (request.command == 'backtrace') {
1230        this.backtraceRequest_(request, response);
1231      } else if (request.command == 'frame') {
1232        this.frameRequest_(request, response);
1233      } else if (request.command == 'scopes') {
1234        this.scopesRequest_(request, response);
1235      } else if (request.command == 'scope') {
1236        this.scopeRequest_(request, response);
1237      } else if (request.command == 'evaluate') {
1238        this.evaluateRequest_(request, response);
1239      } else if (request.command == 'lookup') {
1240        this.lookupRequest_(request, response);
1241      } else if (request.command == 'references') {
1242        this.referencesRequest_(request, response);
1243      } else if (request.command == 'source') {
1244        this.sourceRequest_(request, response);
1245      } else if (request.command == 'scripts') {
1246        this.scriptsRequest_(request, response);
1247      } else if (request.command == 'threads') {
1248        this.threadsRequest_(request, response);
1249      } else if (request.command == 'suspend') {
1250        this.suspendRequest_(request, response);
1251      } else if (request.command == 'version') {
1252        this.versionRequest_(request, response);
1253      } else if (request.command == 'profile') {
1254        this.profileRequest_(request, response);
1255      } else {
1256        throw new Error('Unknown command "' + request.command + '" in request');
1257      }
1258    } catch (e) {
1259      // If there is no response object created one (without command).
1260      if (!response) {
1261        response = this.createResponse();
1262      }
1263      response.success = false;
1264      response.message = %ToString(e);
1265    }
1266
1267    // Return the response as a JSON encoded string.
1268    try {
1269      if (!IS_UNDEFINED(response.running)) {
1270        // Response controls running state.
1271        this.running_ = response.running;
1272      }
1273      response.running = this.running_;
1274      return response.toJSONProtocol();
1275    } catch (e) {
1276      // Failed to generate response - return generic error.
1277      return '{"seq":' + response.seq + ',' +
1278              '"request_seq":' + request.seq + ',' +
1279              '"type":"response",' +
1280              '"success":false,' +
1281              '"message":"Internal error: ' + %ToString(e) + '"}';
1282    }
1283  } catch (e) {
1284    // Failed in one of the catch blocks above - most generic error.
1285    return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
1286  }
1287};
1288
1289
1290DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
1291  // Check for arguments for continue.
1292  if (request.arguments) {
1293    var count = 1;
1294    var action = Debug.StepAction.StepIn;
1295
1296    // Pull out arguments.
1297    var stepaction = request.arguments.stepaction;
1298    var stepcount = request.arguments.stepcount;
1299
1300    // Get the stepcount argument if any.
1301    if (stepcount) {
1302      count = %ToNumber(stepcount);
1303      if (count < 0) {
1304        throw new Error('Invalid stepcount argument "' + stepcount + '".');
1305      }
1306    }
1307
1308    // Get the stepaction argument.
1309    if (stepaction) {
1310      if (stepaction == 'in') {
1311        action = Debug.StepAction.StepIn;
1312      } else if (stepaction == 'min') {
1313        action = Debug.StepAction.StepMin;
1314      } else if (stepaction == 'next') {
1315        action = Debug.StepAction.StepNext;
1316      } else if (stepaction == 'out') {
1317        action = Debug.StepAction.StepOut;
1318      } else {
1319        throw new Error('Invalid stepaction argument "' + stepaction + '".');
1320      }
1321    }
1322
1323    // Setup the VM for stepping.
1324    this.exec_state_.prepareStep(action, count);
1325  }
1326
1327  // VM should be running after executing this request.
1328  response.running = true;
1329};
1330
1331
1332DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
1333  // Ignore as break command does not do anything when broken.
1334};
1335
1336
1337DebugCommandProcessor.prototype.setBreakPointRequest_ =
1338    function(request, response) {
1339  // Check for legal request.
1340  if (!request.arguments) {
1341    response.failed('Missing arguments');
1342    return;
1343  }
1344
1345  // Pull out arguments.
1346  var type = request.arguments.type;
1347  var target = request.arguments.target;
1348  var line = request.arguments.line;
1349  var column = request.arguments.column;
1350  var enabled = IS_UNDEFINED(request.arguments.enabled) ?
1351      true : request.arguments.enabled;
1352  var condition = request.arguments.condition;
1353  var ignoreCount = request.arguments.ignoreCount;
1354  var groupId = request.arguments.groupId;
1355
1356  // Check for legal arguments.
1357  if (!type || IS_UNDEFINED(target)) {
1358    response.failed('Missing argument "type" or "target"');
1359    return;
1360  }
1361  if (type != 'function' && type != 'handle' &&
1362      type != 'script' && type != 'scriptId') {
1363    response.failed('Illegal type "' + type + '"');
1364    return;
1365  }
1366
1367  // Either function or script break point.
1368  var break_point_number;
1369  if (type == 'function') {
1370    // Handle function break point.
1371    if (!IS_STRING(target)) {
1372      response.failed('Argument "target" is not a string value');
1373      return;
1374    }
1375    var f;
1376    try {
1377      // Find the function through a global evaluate.
1378      f = this.exec_state_.evaluateGlobal(target).value();
1379    } catch (e) {
1380      response.failed('Error: "' + %ToString(e) +
1381                      '" evaluating "' + target + '"');
1382      return;
1383    }
1384    if (!IS_FUNCTION(f)) {
1385      response.failed('"' + target + '" does not evaluate to a function');
1386      return;
1387    }
1388
1389    // Set function break point.
1390    break_point_number = Debug.setBreakPoint(f, line, column, condition);
1391  } else if (type == 'handle') {
1392    // Find the object pointed by the specified handle.
1393    var handle = parseInt(target, 10);
1394    var mirror = LookupMirror(handle);
1395    if (!mirror) {
1396      return response.failed('Object #' + handle + '# not found');
1397    }
1398    if (!mirror.isFunction()) {
1399      return response.failed('Object #' + handle + '# is not a function');
1400    }
1401
1402    // Set function break point.
1403    break_point_number = Debug.setBreakPoint(mirror.value(),
1404                                             line, column, condition);
1405  } else if (type == 'script') {
1406    // set script break point.
1407    break_point_number =
1408        Debug.setScriptBreakPointByName(target, line, column, condition,
1409                                        groupId);
1410  } else {  // type == 'scriptId.
1411    break_point_number =
1412        Debug.setScriptBreakPointById(target, line, column, condition, groupId);
1413  }
1414
1415  // Set additional break point properties.
1416  var break_point = Debug.findBreakPoint(break_point_number);
1417  if (ignoreCount) {
1418    Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
1419  }
1420  if (!enabled) {
1421    Debug.disableBreakPoint(break_point_number);
1422  }
1423
1424  // Add the break point number to the response.
1425  response.body = { type: type,
1426                    breakpoint: break_point_number }
1427
1428  // Add break point information to the response.
1429  if (break_point instanceof ScriptBreakPoint) {
1430    if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1431      response.body.type = 'scriptId';
1432      response.body.script_id = break_point.script_id();
1433    } else {
1434      response.body.type = 'scriptName';
1435      response.body.script_name = break_point.script_name();
1436    }
1437    response.body.line = break_point.line();
1438    response.body.column = break_point.column();
1439  } else {
1440    response.body.type = 'function';
1441  }
1442};
1443
1444
1445DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) {
1446  // Check for legal request.
1447  if (!request.arguments) {
1448    response.failed('Missing arguments');
1449    return;
1450  }
1451
1452  // Pull out arguments.
1453  var break_point = %ToNumber(request.arguments.breakpoint);
1454  var enabled = request.arguments.enabled;
1455  var condition = request.arguments.condition;
1456  var ignoreCount = request.arguments.ignoreCount;
1457
1458  // Check for legal arguments.
1459  if (!break_point) {
1460    response.failed('Missing argument "breakpoint"');
1461    return;
1462  }
1463
1464  // Change enabled state if supplied.
1465  if (!IS_UNDEFINED(enabled)) {
1466    if (enabled) {
1467      Debug.enableBreakPoint(break_point);
1468    } else {
1469      Debug.disableBreakPoint(break_point);
1470    }
1471  }
1472
1473  // Change condition if supplied
1474  if (!IS_UNDEFINED(condition)) {
1475    Debug.changeBreakPointCondition(break_point, condition);
1476  }
1477
1478  // Change ignore count if supplied
1479  if (!IS_UNDEFINED(ignoreCount)) {
1480    Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
1481  }
1482}
1483
1484
1485DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) {
1486  // Check for legal request.
1487  if (!request.arguments) {
1488    response.failed('Missing arguments');
1489    return;
1490  }
1491
1492  // Pull out arguments.
1493  var group_id = request.arguments.groupId;
1494
1495  // Check for legal arguments.
1496  if (!group_id) {
1497    response.failed('Missing argument "groupId"');
1498    return;
1499  }
1500
1501  var cleared_break_points = [];
1502  var new_script_break_points = [];
1503  for (var i = 0; i < script_break_points.length; i++) {
1504    var next_break_point = script_break_points[i];
1505    if (next_break_point.groupId() == group_id) {
1506      cleared_break_points.push(next_break_point.number());
1507      next_break_point.clear();
1508    } else {
1509      new_script_break_points.push(next_break_point);
1510    }
1511  }
1512  script_break_points = new_script_break_points;
1513
1514  // Add the cleared break point numbers to the response.
1515  response.body = { breakpoints: cleared_break_points };
1516}
1517
1518
1519DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) {
1520  // Check for legal request.
1521  if (!request.arguments) {
1522    response.failed('Missing arguments');
1523    return;
1524  }
1525
1526  // Pull out arguments.
1527  var break_point = %ToNumber(request.arguments.breakpoint);
1528
1529  // Check for legal arguments.
1530  if (!break_point) {
1531    response.failed('Missing argument "breakpoint"');
1532    return;
1533  }
1534
1535  // Clear break point.
1536  Debug.clearBreakPoint(break_point);
1537
1538  // Add the cleared break point number to the response.
1539  response.body = { breakpoint: break_point }
1540}
1541
1542
1543DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) {
1544  // Get the number of frames.
1545  var total_frames = this.exec_state_.frameCount();
1546
1547  // Create simple response if there are no frames.
1548  if (total_frames == 0) {
1549    response.body = {
1550      totalFrames: total_frames
1551    }
1552    return;
1553  }
1554
1555  // Default frame range to include in backtrace.
1556  var from_index = 0
1557  var to_index = kDefaultBacktraceLength;
1558
1559  // Get the range from the arguments.
1560  if (request.arguments) {
1561    if (request.arguments.fromFrame) {
1562      from_index = request.arguments.fromFrame;
1563    }
1564    if (request.arguments.toFrame) {
1565      to_index = request.arguments.toFrame;
1566    }
1567    if (request.arguments.bottom) {
1568      var tmp_index = total_frames - from_index;
1569      from_index = total_frames - to_index
1570      to_index = tmp_index;
1571    }
1572    if (from_index < 0 || to_index < 0) {
1573      return response.failed('Invalid frame number');
1574    }
1575  }
1576
1577  // Adjust the index.
1578  to_index = Math.min(total_frames, to_index);
1579
1580  if (to_index <= from_index) {
1581    var error = 'Invalid frame range';
1582    return response.failed(error);
1583  }
1584
1585  // Create the response body.
1586  var frames = [];
1587  for (var i = from_index; i < to_index; i++) {
1588    frames.push(this.exec_state_.frame(i));
1589  }
1590  response.body = {
1591    fromFrame: from_index,
1592    toFrame: to_index,
1593    totalFrames: total_frames,
1594    frames: frames
1595  }
1596};
1597
1598
1599DebugCommandProcessor.prototype.backtracec = function(cmd, args) {
1600  return this.exec_state_.cframesValue();
1601};
1602
1603
1604DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
1605  // No frames no source.
1606  if (this.exec_state_.frameCount() == 0) {
1607    return response.failed('No frames');
1608  }
1609
1610  // With no arguments just keep the selected frame.
1611  if (request.arguments) {
1612    var index = request.arguments.number;
1613    if (index < 0 || this.exec_state_.frameCount() <= index) {
1614      return response.failed('Invalid frame number');
1615    }
1616
1617    this.exec_state_.setSelectedFrame(request.arguments.number);
1618  }
1619  response.body = this.exec_state_.frame();
1620};
1621
1622
1623DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) {
1624  // Get the frame for which the scope or scopes are requested. With no frameNumber
1625  // argument use the currently selected frame.
1626  if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) {
1627    frame_index = request.arguments.frameNumber;
1628    if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
1629      return response.failed('Invalid frame number');
1630    }
1631    return this.exec_state_.frame(frame_index);
1632  } else {
1633    return this.exec_state_.frame();
1634  }
1635}
1636
1637
1638DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
1639  // No frames no scopes.
1640  if (this.exec_state_.frameCount() == 0) {
1641    return response.failed('No scopes');
1642  }
1643
1644  // Get the frame for which the scopes are requested.
1645  var frame = this.frameForScopeRequest_(request);
1646
1647  // Fill all scopes for this frame.
1648  var total_scopes = frame.scopeCount();
1649  var scopes = [];
1650  for (var i = 0; i < total_scopes; i++) {
1651    scopes.push(frame.scope(i));
1652  }
1653  response.body = {
1654    fromScope: 0,
1655    toScope: total_scopes,
1656    totalScopes: total_scopes,
1657    scopes: scopes
1658  }
1659};
1660
1661
1662DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
1663  // No frames no scopes.
1664  if (this.exec_state_.frameCount() == 0) {
1665    return response.failed('No scopes');
1666  }
1667
1668  // Get the frame for which the scope is requested.
1669  var frame = this.frameForScopeRequest_(request);
1670
1671  // With no scope argument just return top scope.
1672  var scope_index = 0;
1673  if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
1674    scope_index = %ToNumber(request.arguments.number);
1675    if (scope_index < 0 || frame.scopeCount() <= scope_index) {
1676      return response.failed('Invalid scope number');
1677    }
1678  }
1679
1680  response.body = frame.scope(scope_index);
1681};
1682
1683
1684DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
1685  if (!request.arguments) {
1686    return response.failed('Missing arguments');
1687  }
1688
1689  // Pull out arguments.
1690  var expression = request.arguments.expression;
1691  var frame = request.arguments.frame;
1692  var global = request.arguments.global;
1693  var disable_break = request.arguments.disable_break;
1694
1695  // The expression argument could be an integer so we convert it to a
1696  // string.
1697  try {
1698    expression = String(expression);
1699  } catch(e) {
1700    return response.failed('Failed to convert expression argument to string');
1701  }
1702
1703  // Check for legal arguments.
1704  if (!IS_UNDEFINED(frame) && global) {
1705    return response.failed('Arguments "frame" and "global" are exclusive');
1706  }
1707
1708  // Global evaluate.
1709  if (global) {
1710    // Evaluate in the global context.
1711    response.body =
1712        this.exec_state_.evaluateGlobal(expression, Boolean(disable_break));
1713    return;
1714  }
1715
1716  // Default value for disable_break is true.
1717  if (IS_UNDEFINED(disable_break)) {
1718    disable_break = true;
1719  }
1720
1721  // No frames no evaluate in frame.
1722  if (this.exec_state_.frameCount() == 0) {
1723    return response.failed('No frames');
1724  }
1725
1726  // Check whether a frame was specified.
1727  if (!IS_UNDEFINED(frame)) {
1728    var frame_number = %ToNumber(frame);
1729    if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
1730      return response.failed('Invalid frame "' + frame + '"');
1731    }
1732    // Evaluate in the specified frame.
1733    response.body = this.exec_state_.frame(frame_number).evaluate(
1734        expression, Boolean(disable_break));
1735    return;
1736  } else {
1737    // Evaluate in the selected frame.
1738    response.body = this.exec_state_.frame().evaluate(
1739        expression, Boolean(disable_break));
1740    return;
1741  }
1742};
1743
1744
1745DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
1746  if (!request.arguments) {
1747    return response.failed('Missing arguments');
1748  }
1749
1750  // Pull out arguments.
1751  var handles = request.arguments.handles;
1752
1753  // Check for legal arguments.
1754  if (IS_UNDEFINED(handles)) {
1755    return response.failed('Argument "handles" missing');
1756  }
1757
1758  // Set 'includeSource' option for script lookup.
1759  if (!IS_UNDEFINED(request.arguments.includeSource)) {
1760    includeSource = %ToBoolean(request.arguments.includeSource);
1761    response.setOption('includeSource', includeSource);
1762  }
1763
1764  // Lookup handles.
1765  var mirrors = {};
1766  for (var i = 0; i < handles.length; i++) {
1767    var handle = handles[i];
1768    var mirror = LookupMirror(handle);
1769    if (!mirror) {
1770      return response.failed('Object #' + handle + '# not found');
1771    }
1772    mirrors[handle] = mirror;
1773  }
1774  response.body = mirrors;
1775};
1776
1777
1778DebugCommandProcessor.prototype.referencesRequest_ =
1779    function(request, response) {
1780  if (!request.arguments) {
1781    return response.failed('Missing arguments');
1782  }
1783
1784  // Pull out arguments.
1785  var type = request.arguments.type;
1786  var handle = request.arguments.handle;
1787
1788  // Check for legal arguments.
1789  if (IS_UNDEFINED(type)) {
1790    return response.failed('Argument "type" missing');
1791  }
1792  if (IS_UNDEFINED(handle)) {
1793    return response.failed('Argument "handle" missing');
1794  }
1795  if (type != 'referencedBy' && type != 'constructedBy') {
1796    return response.failed('Invalid type "' + type + '"');
1797  }
1798
1799  // Lookup handle and return objects with references the object.
1800  var mirror = LookupMirror(handle);
1801  if (mirror) {
1802    if (type == 'referencedBy') {
1803      response.body = mirror.referencedBy();
1804    } else {
1805      response.body = mirror.constructedBy();
1806    }
1807  } else {
1808    return response.failed('Object #' + handle + '# not found');
1809  }
1810};
1811
1812
1813DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
1814  // No frames no source.
1815  if (this.exec_state_.frameCount() == 0) {
1816    return response.failed('No source');
1817  }
1818
1819  var from_line;
1820  var to_line;
1821  var frame = this.exec_state_.frame();
1822  if (request.arguments) {
1823    // Pull out arguments.
1824    from_line = request.arguments.fromLine;
1825    to_line = request.arguments.toLine;
1826
1827    if (!IS_UNDEFINED(request.arguments.frame)) {
1828      var frame_number = %ToNumber(request.arguments.frame);
1829      if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
1830        return response.failed('Invalid frame "' + frame + '"');
1831      }
1832      frame = this.exec_state_.frame(frame_number);
1833    }
1834  }
1835
1836  // Get the script selected.
1837  var script = frame.func().script();
1838  if (!script) {
1839    return response.failed('No source');
1840  }
1841
1842  // Get the source slice and fill it into the response.
1843  var slice = script.sourceSlice(from_line, to_line);
1844  if (!slice) {
1845    return response.failed('Invalid line interval');
1846  }
1847  response.body = {};
1848  response.body.source = slice.sourceText();
1849  response.body.fromLine = slice.from_line;
1850  response.body.toLine = slice.to_line;
1851  response.body.fromPosition = slice.from_position;
1852  response.body.toPosition = slice.to_position;
1853  response.body.totalLines = script.lineCount();
1854};
1855
1856
1857DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
1858  var types = ScriptTypeFlag(Debug.ScriptType.Normal);
1859  var includeSource = false;
1860  var idsToInclude = null;
1861  if (request.arguments) {
1862    // Pull out arguments.
1863    if (!IS_UNDEFINED(request.arguments.types)) {
1864      types = %ToNumber(request.arguments.types);
1865      if (isNaN(types) || types < 0) {
1866        return response.failed('Invalid types "' + request.arguments.types + '"');
1867      }
1868    }
1869
1870    if (!IS_UNDEFINED(request.arguments.includeSource)) {
1871      includeSource = %ToBoolean(request.arguments.includeSource);
1872      response.setOption('includeSource', includeSource);
1873    }
1874
1875    if (IS_ARRAY(request.arguments.ids)) {
1876      idsToInclude = {};
1877      var ids = request.arguments.ids;
1878      for (var i = 0; i < ids.length; i++) {
1879        idsToInclude[ids[i]] = true;
1880      }
1881    }
1882  }
1883
1884  // Collect all scripts in the heap.
1885  var scripts = %DebugGetLoadedScripts();
1886
1887  response.body = [];
1888
1889  for (var i = 0; i < scripts.length; i++) {
1890    if (idsToInclude && !idsToInclude[scripts[i].id]) {
1891      continue;
1892    }
1893    if (types & ScriptTypeFlag(scripts[i].type)) {
1894      response.body.push(MakeMirror(scripts[i]));
1895    }
1896  }
1897};
1898
1899
1900DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
1901  // Get the number of threads.
1902  var total_threads = this.exec_state_.threadCount();
1903
1904  // Get information for all threads.
1905  var threads = [];
1906  for (var i = 0; i < total_threads; i++) {
1907    var details = %GetThreadDetails(this.exec_state_.break_id, i);
1908    var thread_info = { current: details[0],
1909                        id: details[1]
1910                      }
1911    threads.push(thread_info);
1912  }
1913
1914  // Create the response body.
1915  response.body = {
1916    totalThreads: total_threads,
1917    threads: threads
1918  }
1919};
1920
1921
1922DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
1923  response.running = false;
1924};
1925
1926
1927DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
1928  response.body = {
1929    V8Version: %GetV8Version()
1930  }
1931};
1932
1933
1934DebugCommandProcessor.prototype.profileRequest_ = function(request, response) {
1935  if (!request.arguments) {
1936    return response.failed('Missing arguments');
1937  }
1938  var modules = parseInt(request.arguments.modules);
1939  if (isNaN(modules)) {
1940    return response.failed('Modules is not an integer');
1941  }
1942  var tag = parseInt(request.arguments.tag);
1943  if (isNaN(tag)) {
1944    tag = 0;
1945  }
1946  if (request.arguments.command == 'resume') {
1947    %ProfilerResume(modules, tag);
1948  } else if (request.arguments.command == 'pause') {
1949    %ProfilerPause(modules, tag);
1950  } else {
1951    return response.failed('Unknown command');
1952  }
1953  response.body = {};
1954};
1955
1956
1957// Check whether the previously processed command caused the VM to become
1958// running.
1959DebugCommandProcessor.prototype.isRunning = function() {
1960  return this.running_;
1961}
1962
1963
1964DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
1965  return %SystemBreak();
1966};
1967
1968
1969function NumberToHex8Str(n) {
1970  var r = "";
1971  for (var i = 0; i < 8; ++i) {
1972    var c = hexCharArray[n & 0x0F];  // hexCharArray is defined in uri.js
1973    r = c + r;
1974    n = n >>> 4;
1975  }
1976  return r;
1977};
1978
1979DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) {
1980  var result = "";
1981  if (cframes_value == null || cframes_value.length == 0) {
1982    result += "(stack empty)";
1983  } else {
1984    for (var i = 0; i < cframes_value.length; ++i) {
1985      if (i != 0) result += "\n";
1986      result += this.formatCFrame(cframes_value[i]);
1987    }
1988  }
1989  return result;
1990};
1991
1992
1993DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) {
1994  var result = "";
1995  result += "0x" + NumberToHex8Str(cframe_value.address);
1996  if (!IS_UNDEFINED(cframe_value.text)) {
1997    result += " " + cframe_value.text;
1998  }
1999  return result;
2000}
2001
2002
2003/**
2004 * Convert an Object to its debugger protocol representation. The representation
2005 * may be serilized to a JSON object using JSON.stringify().
2006 * This implementation simply runs through all string property names, converts
2007 * each property value to a protocol value and adds the property to the result
2008 * object. For type "object" the function will be called recursively. Note that
2009 * circular structures will cause infinite recursion.
2010 * @param {Object} object The object to format as protocol object.
2011 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2012 *     mirror objects are encountered.
2013 * @return {Object} Protocol object value.
2014 */
2015function ObjectToProtocolObject_(object, mirror_serializer) {
2016  var content = {};
2017  for (var key in object) {
2018    // Only consider string keys.
2019    if (typeof key == 'string') {
2020      // Format the value based on its type.
2021      var property_value_json = ValueToProtocolValue_(object[key],
2022                                                      mirror_serializer);
2023      // Add the property if relevant.
2024      if (!IS_UNDEFINED(property_value_json)) {
2025        content[key] = property_value_json;
2026      }
2027    }
2028  }
2029
2030  return content;
2031}
2032
2033
2034/**
2035 * Convert an array to its debugger protocol representation. It will convert
2036 * each array element to a protocol value.
2037 * @param {Array} array The array to format as protocol array.
2038 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2039 *     mirror objects are encountered.
2040 * @return {Array} Protocol array value.
2041 */
2042function ArrayToProtocolArray_(array, mirror_serializer) {
2043  var json = [];
2044  for (var i = 0; i < array.length; i++) {
2045    json.push(ValueToProtocolValue_(array[i], mirror_serializer));
2046  }
2047  return json;
2048}
2049
2050
2051/**
2052 * Convert a value to its debugger protocol representation.
2053 * @param {*} value The value to format as protocol value.
2054 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2055 *     mirror objects are encountered.
2056 * @return {*} Protocol value.
2057 */
2058function ValueToProtocolValue_(value, mirror_serializer) {
2059  // Format the value based on its type.
2060  var json;
2061  switch (typeof value) {
2062    case 'object':
2063      if (value instanceof Mirror) {
2064        json = mirror_serializer.serializeValue(value);
2065      } else if (IS_ARRAY(value)){
2066        json = ArrayToProtocolArray_(value, mirror_serializer);
2067      } else {
2068        json = ObjectToProtocolObject_(value, mirror_serializer);
2069      }
2070      break;
2071
2072    case 'boolean':
2073    case 'string':
2074    case 'number':
2075      json = value;
2076      break
2077
2078    default:
2079      json = null;
2080  }
2081  return json;
2082}
2083