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