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