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