Home | History | Annotate | Download | only in debug
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 (function (global, utils) {
      6 "use strict";
      7 
      8 // ----------------------------------------------------------------------------
      9 // Imports
     10 
     11 var FrameMirror = global.FrameMirror;
     12 var GlobalArray = global.Array;
     13 var GlobalRegExp = global.RegExp;
     14 var IsNaN = global.isNaN;
     15 var MakeMirror = global.MakeMirror;
     16 var MathMin = global.Math.min;
     17 var Mirror = global.Mirror;
     18 var ValueMirror = global.ValueMirror;
     19 
     20 //----------------------------------------------------------------------------
     21 
     22 // Default number of frames to include in the response to backtrace request.
     23 var kDefaultBacktraceLength = 10;
     24 
     25 var Debug = {};
     26 
     27 // Regular expression to skip "crud" at the beginning of a source line which is
     28 // not really code. Currently the regular expression matches whitespace and
     29 // comments.
     30 var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
     31 
     32 // Debug events which can occour in the V8 JavaScript engine. These originate
     33 // from the API include file debug.h.
     34 Debug.DebugEvent = { Break: 1,
     35                      Exception: 2,
     36                      AfterCompile: 3,
     37                      CompileError: 4,
     38                      AsyncTaskEvent: 5 };
     39 
     40 // Types of exceptions that can be broken upon.
     41 Debug.ExceptionBreak = { Caught : 0,
     42                          Uncaught: 1 };
     43 
     44 // The different types of steps.
     45 Debug.StepAction = { StepOut: 0,
     46                      StepNext: 1,
     47                      StepIn: 2 };
     48 
     49 // The different types of scripts matching enum ScriptType in objects.h.
     50 Debug.ScriptType = { Native: 0,
     51                      Extension: 1,
     52                      Normal: 2,
     53                      Wasm: 3};
     54 
     55 // The different types of script compilations matching enum
     56 // Script::CompilationType in objects.h.
     57 Debug.ScriptCompilationType = { Host: 0,
     58                                 Eval: 1,
     59                                 JSON: 2 };
     60 
     61 // The different script break point types.
     62 Debug.ScriptBreakPointType = { ScriptId: 0,
     63                                ScriptName: 1,
     64                                ScriptRegExp: 2 };
     65 
     66 // The different types of breakpoint position alignments.
     67 // Must match BreakPositionAlignment in debug.h.
     68 Debug.BreakPositionAlignment = {
     69   Statement: 0,
     70   BreakPosition: 1
     71 };
     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 var debugger_flags = {
     83   breakPointsActive: {
     84     value: true,
     85     getValue: function() { return this.value; },
     86     setValue: function(value) {
     87       this.value = !!value;
     88       %SetBreakPointsActive(this.value);
     89     }
     90   },
     91   breakOnCaughtException: {
     92     getValue: function() { return Debug.isBreakOnException(); },
     93     setValue: function(value) {
     94       if (value) {
     95         Debug.setBreakOnException();
     96       } else {
     97         Debug.clearBreakOnException();
     98       }
     99     }
    100   },
    101   breakOnUncaughtException: {
    102     getValue: function() { return Debug.isBreakOnUncaughtException(); },
    103     setValue: function(value) {
    104       if (value) {
    105         Debug.setBreakOnUncaughtException();
    106       } else {
    107         Debug.clearBreakOnUncaughtException();
    108       }
    109     }
    110   },
    111 };
    112 
    113 
    114 // Create a new break point object and add it to the list of break points.
    115 function MakeBreakPoint(source_position, opt_script_break_point) {
    116   var break_point = new BreakPoint(source_position, opt_script_break_point);
    117   break_points.push(break_point);
    118   return break_point;
    119 }
    120 
    121 
    122 // Object representing a break point.
    123 // NOTE: This object does not have a reference to the function having break
    124 // point as this would cause function not to be garbage collected when it is
    125 // not used any more. We do not want break points to keep functions alive.
    126 function BreakPoint(source_position, opt_script_break_point) {
    127   this.source_position_ = source_position;
    128   if (opt_script_break_point) {
    129     this.script_break_point_ = opt_script_break_point;
    130   } else {
    131     this.number_ = next_break_point_number++;
    132   }
    133   this.active_ = true;
    134   this.condition_ = null;
    135 }
    136 
    137 
    138 BreakPoint.prototype.number = function() {
    139   return this.number_;
    140 };
    141 
    142 
    143 BreakPoint.prototype.func = function() {
    144   return this.func_;
    145 };
    146 
    147 
    148 BreakPoint.prototype.source_position = function() {
    149   return this.source_position_;
    150 };
    151 
    152 
    153 BreakPoint.prototype.active = function() {
    154   if (this.script_break_point()) {
    155     return this.script_break_point().active();
    156   }
    157   return this.active_;
    158 };
    159 
    160 
    161 BreakPoint.prototype.condition = function() {
    162   if (this.script_break_point() && this.script_break_point().condition()) {
    163     return this.script_break_point().condition();
    164   }
    165   return this.condition_;
    166 };
    167 
    168 
    169 BreakPoint.prototype.script_break_point = function() {
    170   return this.script_break_point_;
    171 };
    172 
    173 
    174 BreakPoint.prototype.enable = function() {
    175   this.active_ = true;
    176 };
    177 
    178 
    179 BreakPoint.prototype.disable = function() {
    180   this.active_ = false;
    181 };
    182 
    183 
    184 BreakPoint.prototype.setCondition = function(condition) {
    185   this.condition_ = condition;
    186 };
    187 
    188 
    189 BreakPoint.prototype.isTriggered = function(exec_state) {
    190   // Break point not active - not triggered.
    191   if (!this.active()) return false;
    192 
    193   // Check for conditional break point.
    194   if (this.condition()) {
    195     // If break point has condition try to evaluate it in the top frame.
    196     try {
    197       var mirror = exec_state.frame(0).evaluate(this.condition());
    198       // If no sensible mirror or non true value break point not triggered.
    199       if (!(mirror instanceof ValueMirror) || !mirror.value_) {
    200         return false;
    201       }
    202     } catch (e) {
    203       // Exception evaluating condition counts as not triggered.
    204       return false;
    205     }
    206   }
    207 
    208   // Break point triggered.
    209   return true;
    210 };
    211 
    212 
    213 // Function called from the runtime when a break point is hit. Returns true if
    214 // the break point is triggered and supposed to break execution.
    215 function IsBreakPointTriggered(break_id, break_point) {
    216   return break_point.isTriggered(MakeExecutionState(break_id));
    217 }
    218 
    219 
    220 // Object representing a script break point. The script is referenced by its
    221 // script name or script id and the break point is represented as line and
    222 // column.
    223 function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
    224                           opt_groupId, opt_position_alignment) {
    225   this.type_ = type;
    226   if (type == Debug.ScriptBreakPointType.ScriptId) {
    227     this.script_id_ = script_id_or_name;
    228   } else if (type == Debug.ScriptBreakPointType.ScriptName) {
    229     this.script_name_ = script_id_or_name;
    230   } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
    231     this.script_regexp_object_ = new GlobalRegExp(script_id_or_name);
    232   } else {
    233     throw %make_error(kDebugger, "Unexpected breakpoint type " + type);
    234   }
    235   this.line_ = opt_line || 0;
    236   this.column_ = opt_column;
    237   this.groupId_ = opt_groupId;
    238   this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
    239       ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
    240   this.active_ = true;
    241   this.condition_ = null;
    242   this.break_points_ = [];
    243 }
    244 
    245 
    246 ScriptBreakPoint.prototype.number = function() {
    247   return this.number_;
    248 };
    249 
    250 
    251 ScriptBreakPoint.prototype.groupId = function() {
    252   return this.groupId_;
    253 };
    254 
    255 
    256 ScriptBreakPoint.prototype.type = function() {
    257   return this.type_;
    258 };
    259 
    260 
    261 ScriptBreakPoint.prototype.script_id = function() {
    262   return this.script_id_;
    263 };
    264 
    265 
    266 ScriptBreakPoint.prototype.script_name = function() {
    267   return this.script_name_;
    268 };
    269 
    270 
    271 ScriptBreakPoint.prototype.script_regexp_object = function() {
    272   return this.script_regexp_object_;
    273 };
    274 
    275 
    276 ScriptBreakPoint.prototype.line = function() {
    277   return this.line_;
    278 };
    279 
    280 
    281 ScriptBreakPoint.prototype.column = function() {
    282   return this.column_;
    283 };
    284 
    285 
    286 ScriptBreakPoint.prototype.actual_locations = function() {
    287   var locations = [];
    288   for (var i = 0; i < this.break_points_.length; i++) {
    289     locations.push(this.break_points_[i].actual_location);
    290   }
    291   return locations;
    292 };
    293 
    294 
    295 ScriptBreakPoint.prototype.update_positions = function(line, column) {
    296   this.line_ = line;
    297   this.column_ = column;
    298 };
    299 
    300 
    301 ScriptBreakPoint.prototype.active = function() {
    302   return this.active_;
    303 };
    304 
    305 
    306 ScriptBreakPoint.prototype.condition = function() {
    307   return this.condition_;
    308 };
    309 
    310 
    311 ScriptBreakPoint.prototype.enable = function() {
    312   this.active_ = true;
    313 };
    314 
    315 
    316 ScriptBreakPoint.prototype.disable = function() {
    317   this.active_ = false;
    318 };
    319 
    320 
    321 ScriptBreakPoint.prototype.setCondition = function(condition) {
    322   this.condition_ = condition;
    323 };
    324 
    325 
    326 // Check whether a script matches this script break point. Currently this is
    327 // only based on script name.
    328 ScriptBreakPoint.prototype.matchesScript = function(script) {
    329   if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
    330     return this.script_id_ == script.id;
    331   } else {
    332     // We might want to account columns here as well.
    333     if (!(script.line_offset <= this.line_  &&
    334           this.line_ < script.line_offset + %ScriptLineCount(script))) {
    335       return false;
    336     }
    337     if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
    338       return this.script_name_ == script.nameOrSourceURL();
    339     } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
    340       return this.script_regexp_object_.test(script.nameOrSourceURL());
    341     } else {
    342       throw %make_error(kDebugger, "Unexpected breakpoint type " + this.type_);
    343     }
    344   }
    345 };
    346 
    347 
    348 // Set the script break point in a script.
    349 ScriptBreakPoint.prototype.set = function (script) {
    350   var column = this.column();
    351   var line = this.line();
    352   // If the column is undefined the break is on the line. To help locate the
    353   // first piece of breakable code on the line try to find the column on the
    354   // line which contains some source.
    355   if (IS_UNDEFINED(column)) {
    356     var source_line = %ScriptSourceLine(script, line || script.line_offset);
    357 
    358     // Allocate array for caching the columns where the actual source starts.
    359     if (!script.sourceColumnStart_) {
    360       script.sourceColumnStart_ = new GlobalArray(%ScriptLineCount(script));
    361     }
    362 
    363     // Fill cache if needed and get column where the actual source starts.
    364     if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
    365       script.sourceColumnStart_[line] =
    366           source_line.match(sourceLineBeginningSkip)[0].length;
    367     }
    368     column = script.sourceColumnStart_[line];
    369   }
    370 
    371   // Convert the line and column into an absolute position within the script.
    372   var position = Debug.findScriptSourcePosition(script, this.line(), column);
    373 
    374   // If the position is not found in the script (the script might be shorter
    375   // than it used to be) just ignore it.
    376   if (IS_NULL(position)) return;
    377 
    378   // Create a break point object and set the break point.
    379   var break_point = MakeBreakPoint(position, this);
    380   var actual_position = %SetScriptBreakPoint(script, position,
    381                                              this.position_alignment_,
    382                                              break_point);
    383   if (IS_UNDEFINED(actual_position)) {
    384     actual_position = position;
    385   }
    386   var actual_location = script.locationFromPosition(actual_position, true);
    387   break_point.actual_location = { line: actual_location.line,
    388                                   column: actual_location.column,
    389                                   script_id: script.id };
    390   this.break_points_.push(break_point);
    391   return break_point;
    392 };
    393 
    394 
    395 // Clear all the break points created from this script break point
    396 ScriptBreakPoint.prototype.clear = function () {
    397   var remaining_break_points = [];
    398   for (var i = 0; i < break_points.length; i++) {
    399     if (break_points[i].script_break_point() &&
    400         break_points[i].script_break_point() === this) {
    401       %ClearBreakPoint(break_points[i]);
    402     } else {
    403       remaining_break_points.push(break_points[i]);
    404     }
    405   }
    406   break_points = remaining_break_points;
    407   this.break_points_ = [];
    408 };
    409 
    410 
    411 Debug.setListener = function(listener, opt_data) {
    412   if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
    413     throw %make_type_error(kDebuggerType);
    414   }
    415   %SetDebugEventListener(listener, opt_data);
    416 };
    417 
    418 
    419 // Returns a Script object. If the parameter is a function the return value
    420 // is the script in which the function is defined. If the parameter is a string
    421 // the return value is the script for which the script name has that string
    422 // value.  If it is a regexp and there is a unique script whose name matches
    423 // we return that, otherwise undefined.
    424 Debug.findScript = function(func_or_script_name) {
    425   if (IS_FUNCTION(func_or_script_name)) {
    426     return %FunctionGetScript(func_or_script_name);
    427   } else if (%IsRegExp(func_or_script_name)) {
    428     var scripts = this.scripts();
    429     var last_result = null;
    430     var result_count = 0;
    431     for (var i in scripts) {
    432       var script = scripts[i];
    433       if (func_or_script_name.test(script.name)) {
    434         last_result = script;
    435         result_count++;
    436       }
    437     }
    438     // Return the unique script matching the regexp.  If there are more
    439     // than one we don't return a value since there is no good way to
    440     // decide which one to return.  Returning a "random" one, say the
    441     // first, would introduce nondeterminism (or something close to it)
    442     // because the order is the heap iteration order.
    443     if (result_count == 1) {
    444       return last_result;
    445     } else {
    446       return UNDEFINED;
    447     }
    448   } else {
    449     return %GetScript(func_or_script_name);
    450   }
    451 };
    452 
    453 // Returns the script source. If the parameter is a function the return value
    454 // is the script source for the script in which the function is defined. If the
    455 // parameter is a string the return value is the script for which the script
    456 // name has that string value.
    457 Debug.scriptSource = function(func_or_script_name) {
    458   return this.findScript(func_or_script_name).source;
    459 };
    460 
    461 
    462 Debug.source = function(f) {
    463   if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType);
    464   return %FunctionGetSourceCode(f);
    465 };
    466 
    467 
    468 Debug.sourcePosition = function(f) {
    469   if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType);
    470   return %FunctionGetScriptSourcePosition(f);
    471 };
    472 
    473 
    474 Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
    475   var script = %FunctionGetScript(func);
    476   var script_offset = %FunctionGetScriptSourcePosition(func);
    477   return %ScriptLocationFromLine(script, opt_line, opt_column, script_offset);
    478 };
    479 
    480 
    481 // Returns the character position in a script based on a line number and an
    482 // optional position within that line.
    483 Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
    484   var location = %ScriptLocationFromLine(script, opt_line, opt_column, 0);
    485   return location ? location.position : null;
    486 };
    487 
    488 
    489 Debug.findBreakPoint = function(break_point_number, remove) {
    490   var break_point;
    491   for (var i = 0; i < break_points.length; i++) {
    492     if (break_points[i].number() == break_point_number) {
    493       break_point = break_points[i];
    494       // Remove the break point from the list if requested.
    495       if (remove) {
    496         break_points.splice(i, 1);
    497       }
    498       break;
    499     }
    500   }
    501   if (break_point) {
    502     return break_point;
    503   } else {
    504     return this.findScriptBreakPoint(break_point_number, remove);
    505   }
    506 };
    507 
    508 Debug.findBreakPointActualLocations = function(break_point_number) {
    509   for (var i = 0; i < script_break_points.length; i++) {
    510     if (script_break_points[i].number() == break_point_number) {
    511       return script_break_points[i].actual_locations();
    512     }
    513   }
    514   for (var i = 0; i < break_points.length; i++) {
    515     if (break_points[i].number() == break_point_number) {
    516       return [break_points[i].actual_location];
    517     }
    518   }
    519   return [];
    520 };
    521 
    522 Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
    523   if (!IS_FUNCTION(func)) throw %make_type_error(kDebuggerType);
    524   // Break points in API functions are not supported.
    525   if (%FunctionIsAPIFunction(func)) {
    526     throw %make_error(kDebugger, 'Cannot set break point in native code.');
    527   }
    528   // Find source position.
    529   var source_position =
    530       this.findFunctionSourceLocation(func, opt_line, opt_column).position;
    531   // Find the script for the function.
    532   var script = %FunctionGetScript(func);
    533   // Break in builtin JavaScript code is not supported.
    534   if (script.type == Debug.ScriptType.Native) {
    535     throw %make_error(kDebugger, 'Cannot set break point in native code.');
    536   }
    537   // If the script for the function has a name convert this to a script break
    538   // point.
    539   if (script && script.id) {
    540     // Find line and column for the position in the script and set a script
    541     // break point from that.
    542     var location = script.locationFromPosition(source_position, false);
    543     return this.setScriptBreakPointById(script.id,
    544                                         location.line, location.column,
    545                                         opt_condition);
    546   } else {
    547     // Set a break point directly on the function.
    548     var break_point = MakeBreakPoint(source_position);
    549     var actual_position =
    550         %SetFunctionBreakPoint(func, source_position, break_point);
    551     var actual_location = script.locationFromPosition(actual_position, true);
    552     break_point.actual_location = { line: actual_location.line,
    553                                     column: actual_location.column,
    554                                     script_id: script.id };
    555     break_point.setCondition(opt_condition);
    556     return break_point.number();
    557   }
    558 };
    559 
    560 
    561 Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
    562                                                     condition, enabled,
    563                                                     opt_position_alignment)
    564 {
    565   var break_point = MakeBreakPoint(position);
    566   break_point.setCondition(condition);
    567   if (!enabled) {
    568     break_point.disable();
    569   }
    570   var script = scriptById(script_id);
    571   if (script) {
    572     var position_alignment = IS_UNDEFINED(opt_position_alignment)
    573         ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
    574     break_point.actual_position = %SetScriptBreakPoint(script, position,
    575         position_alignment, break_point);
    576   }
    577   return break_point;
    578 };
    579 
    580 
    581 Debug.enableBreakPoint = function(break_point_number) {
    582   var break_point = this.findBreakPoint(break_point_number, false);
    583   // Only enable if the breakpoint hasn't been deleted:
    584   if (break_point) {
    585     break_point.enable();
    586   }
    587 };
    588 
    589 
    590 Debug.disableBreakPoint = function(break_point_number) {
    591   var break_point = this.findBreakPoint(break_point_number, false);
    592   // Only enable if the breakpoint hasn't been deleted:
    593   if (break_point) {
    594     break_point.disable();
    595   }
    596 };
    597 
    598 
    599 Debug.changeBreakPointCondition = function(break_point_number, condition) {
    600   var break_point = this.findBreakPoint(break_point_number, false);
    601   break_point.setCondition(condition);
    602 };
    603 
    604 
    605 Debug.clearBreakPoint = function(break_point_number) {
    606   var break_point = this.findBreakPoint(break_point_number, true);
    607   if (break_point) {
    608     return %ClearBreakPoint(break_point);
    609   } else {
    610     break_point = this.findScriptBreakPoint(break_point_number, true);
    611     if (!break_point) throw %make_error(kDebugger, 'Invalid breakpoint');
    612   }
    613 };
    614 
    615 
    616 Debug.clearAllBreakPoints = function() {
    617   for (var i = 0; i < break_points.length; i++) {
    618     var break_point = break_points[i];
    619     %ClearBreakPoint(break_point);
    620   }
    621   break_points = [];
    622 };
    623 
    624 
    625 Debug.disableAllBreakPoints = function() {
    626   // Disable all user defined breakpoints:
    627   for (var i = 1; i < next_break_point_number; i++) {
    628     Debug.disableBreakPoint(i);
    629   }
    630   // Disable all exception breakpoints:
    631   %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
    632   %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
    633 };
    634 
    635 
    636 Debug.findScriptBreakPoint = function(break_point_number, remove) {
    637   var script_break_point;
    638   for (var i = 0; i < script_break_points.length; i++) {
    639     if (script_break_points[i].number() == break_point_number) {
    640       script_break_point = script_break_points[i];
    641       // Remove the break point from the list if requested.
    642       if (remove) {
    643         script_break_point.clear();
    644         script_break_points.splice(i,1);
    645       }
    646       break;
    647     }
    648   }
    649   return script_break_point;
    650 };
    651 
    652 
    653 // Sets a breakpoint in a script identified through id or name at the
    654 // specified source line and column within that line.
    655 Debug.setScriptBreakPoint = function(type, script_id_or_name,
    656                                      opt_line, opt_column, opt_condition,
    657                                      opt_groupId, opt_position_alignment) {
    658   // Create script break point object.
    659   var script_break_point =
    660       new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
    661                            opt_groupId, opt_position_alignment);
    662 
    663   // Assign number to the new script break point and add it.
    664   script_break_point.number_ = next_break_point_number++;
    665   script_break_point.setCondition(opt_condition);
    666   script_break_points.push(script_break_point);
    667 
    668   // Run through all scripts to see if this script break point matches any
    669   // loaded scripts.
    670   var scripts = this.scripts();
    671   for (var i = 0; i < scripts.length; i++) {
    672     if (script_break_point.matchesScript(scripts[i])) {
    673       script_break_point.set(scripts[i]);
    674     }
    675   }
    676 
    677   return script_break_point.number();
    678 };
    679 
    680 
    681 Debug.setScriptBreakPointById = function(script_id,
    682                                          opt_line, opt_column,
    683                                          opt_condition, opt_groupId,
    684                                          opt_position_alignment) {
    685   return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
    686                                   script_id, opt_line, opt_column,
    687                                   opt_condition, opt_groupId,
    688                                   opt_position_alignment);
    689 };
    690 
    691 
    692 Debug.setScriptBreakPointByName = function(script_name,
    693                                            opt_line, opt_column,
    694                                            opt_condition, opt_groupId) {
    695   return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
    696                                   script_name, opt_line, opt_column,
    697                                   opt_condition, opt_groupId);
    698 };
    699 
    700 
    701 Debug.setScriptBreakPointByRegExp = function(script_regexp,
    702                                              opt_line, opt_column,
    703                                              opt_condition, opt_groupId) {
    704   return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
    705                                   script_regexp, opt_line, opt_column,
    706                                   opt_condition, opt_groupId);
    707 };
    708 
    709 
    710 Debug.enableScriptBreakPoint = function(break_point_number) {
    711   var script_break_point = this.findScriptBreakPoint(break_point_number, false);
    712   script_break_point.enable();
    713 };
    714 
    715 
    716 Debug.disableScriptBreakPoint = function(break_point_number) {
    717   var script_break_point = this.findScriptBreakPoint(break_point_number, false);
    718   script_break_point.disable();
    719 };
    720 
    721 
    722 Debug.changeScriptBreakPointCondition = function(
    723     break_point_number, condition) {
    724   var script_break_point = this.findScriptBreakPoint(break_point_number, false);
    725   script_break_point.setCondition(condition);
    726 };
    727 
    728 
    729 Debug.scriptBreakPoints = function() {
    730   return script_break_points;
    731 };
    732 
    733 
    734 Debug.clearStepping = function() {
    735   %ClearStepping();
    736 };
    737 
    738 Debug.setBreakOnException = function() {
    739   return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
    740 };
    741 
    742 Debug.clearBreakOnException = function() {
    743   return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
    744 };
    745 
    746 Debug.isBreakOnException = function() {
    747   return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
    748 };
    749 
    750 Debug.setBreakOnUncaughtException = function() {
    751   return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
    752 };
    753 
    754 Debug.clearBreakOnUncaughtException = function() {
    755   return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
    756 };
    757 
    758 Debug.isBreakOnUncaughtException = function() {
    759   return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
    760 };
    761 
    762 Debug.showBreakPoints = function(f, full, opt_position_alignment) {
    763   if (!IS_FUNCTION(f)) throw %make_error(kDebuggerType);
    764   var source = full ? this.scriptSource(f) : this.source(f);
    765   var offset = full ? 0 : this.sourcePosition(f);
    766   var position_alignment = IS_UNDEFINED(opt_position_alignment)
    767       ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
    768   var locations = %GetBreakLocations(f, position_alignment);
    769   if (!locations) return source;
    770   locations.sort(function(x, y) { return x - y; });
    771   var result = "";
    772   var prev_pos = 0;
    773   var pos;
    774   for (var i = 0; i < locations.length; i++) {
    775     pos = locations[i] - offset;
    776     result += source.slice(prev_pos, pos);
    777     result += "[B" + i + "]";
    778     prev_pos = pos;
    779   }
    780   pos = source.length;
    781   result += source.substring(prev_pos, pos);
    782   return result;
    783 };
    784 
    785 
    786 // Get all the scripts currently loaded. Locating all the scripts is based on
    787 // scanning the heap.
    788 Debug.scripts = function() {
    789   // Collect all scripts in the heap.
    790   return %DebugGetLoadedScripts();
    791 };
    792 
    793 
    794 // Get a specific script currently loaded. This is based on scanning the heap.
    795 // TODO(clemensh): Create a runtime function for this.
    796 function scriptById(scriptId) {
    797   var scripts = Debug.scripts();
    798   for (var script of scripts) {
    799     if (script.id == scriptId) return script;
    800   }
    801   return UNDEFINED;
    802 };
    803 
    804 
    805 Debug.debuggerFlags = function() {
    806   return debugger_flags;
    807 };
    808 
    809 Debug.MakeMirror = MakeMirror;
    810 
    811 function MakeExecutionState(break_id) {
    812   return new ExecutionState(break_id);
    813 }
    814 
    815 function ExecutionState(break_id) {
    816   this.break_id = break_id;
    817   this.selected_frame = 0;
    818 }
    819 
    820 ExecutionState.prototype.prepareStep = function(action) {
    821   if (action === Debug.StepAction.StepIn ||
    822       action === Debug.StepAction.StepOut ||
    823       action === Debug.StepAction.StepNext) {
    824     return %PrepareStep(this.break_id, action);
    825   }
    826   throw %make_type_error(kDebuggerType);
    827 };
    828 
    829 ExecutionState.prototype.evaluateGlobal = function(source) {
    830   return MakeMirror(%DebugEvaluateGlobal(this.break_id, source));
    831 };
    832 
    833 ExecutionState.prototype.frameCount = function() {
    834   return %GetFrameCount(this.break_id);
    835 };
    836 
    837 ExecutionState.prototype.frame = function(opt_index) {
    838   // If no index supplied return the selected frame.
    839   if (opt_index == null) opt_index = this.selected_frame;
    840   if (opt_index < 0 || opt_index >= this.frameCount()) {
    841     throw %make_type_error(kDebuggerFrame);
    842   }
    843   return new FrameMirror(this.break_id, opt_index);
    844 };
    845 
    846 ExecutionState.prototype.setSelectedFrame = function(index) {
    847   var i = TO_NUMBER(index);
    848   if (i < 0 || i >= this.frameCount()) {
    849     throw %make_type_error(kDebuggerFrame);
    850   }
    851   this.selected_frame = i;
    852 };
    853 
    854 ExecutionState.prototype.selectedFrame = function() {
    855   return this.selected_frame;
    856 };
    857 
    858 function MakeBreakEvent(break_id, break_points_hit) {
    859   return new BreakEvent(break_id, break_points_hit);
    860 }
    861 
    862 
    863 function BreakEvent(break_id, break_points_hit) {
    864   this.frame_ = new FrameMirror(break_id, 0);
    865   this.break_points_hit_ = break_points_hit;
    866 }
    867 
    868 
    869 BreakEvent.prototype.eventType = function() {
    870   return Debug.DebugEvent.Break;
    871 };
    872 
    873 
    874 BreakEvent.prototype.func = function() {
    875   return this.frame_.func();
    876 };
    877 
    878 
    879 BreakEvent.prototype.sourceLine = function() {
    880   return this.frame_.sourceLine();
    881 };
    882 
    883 
    884 BreakEvent.prototype.sourceColumn = function() {
    885   return this.frame_.sourceColumn();
    886 };
    887 
    888 
    889 BreakEvent.prototype.sourceLineText = function() {
    890   return this.frame_.sourceLineText();
    891 };
    892 
    893 
    894 BreakEvent.prototype.breakPointsHit = function() {
    895   return this.break_points_hit_;
    896 };
    897 
    898 
    899 function MakeExceptionEvent(break_id, exception, uncaught, promise) {
    900   return new ExceptionEvent(break_id, exception, uncaught, promise);
    901 }
    902 
    903 
    904 function ExceptionEvent(break_id, exception, uncaught, promise) {
    905   this.exec_state_ = new ExecutionState(break_id);
    906   this.exception_ = exception;
    907   this.uncaught_ = uncaught;
    908   this.promise_ = promise;
    909 }
    910 
    911 
    912 ExceptionEvent.prototype.eventType = function() {
    913   return Debug.DebugEvent.Exception;
    914 };
    915 
    916 
    917 ExceptionEvent.prototype.exception = function() {
    918   return this.exception_;
    919 };
    920 
    921 
    922 ExceptionEvent.prototype.uncaught = function() {
    923   return this.uncaught_;
    924 };
    925 
    926 
    927 ExceptionEvent.prototype.promise = function() {
    928   return this.promise_;
    929 };
    930 
    931 
    932 ExceptionEvent.prototype.func = function() {
    933   return this.exec_state_.frame(0).func();
    934 };
    935 
    936 
    937 ExceptionEvent.prototype.sourceLine = function() {
    938   return this.exec_state_.frame(0).sourceLine();
    939 };
    940 
    941 
    942 ExceptionEvent.prototype.sourceColumn = function() {
    943   return this.exec_state_.frame(0).sourceColumn();
    944 };
    945 
    946 
    947 ExceptionEvent.prototype.sourceLineText = function() {
    948   return this.exec_state_.frame(0).sourceLineText();
    949 };
    950 
    951 
    952 function MakeCompileEvent(script, type) {
    953   return new CompileEvent(script, type);
    954 }
    955 
    956 
    957 function CompileEvent(script, type) {
    958   this.script_ = MakeMirror(script);
    959   this.type_ = type;
    960 }
    961 
    962 
    963 CompileEvent.prototype.eventType = function() {
    964   return this.type_;
    965 };
    966 
    967 
    968 CompileEvent.prototype.script = function() {
    969   return this.script_;
    970 };
    971 
    972 
    973 function MakeScriptObject_(script, include_source) {
    974   var o = { id: script.id(),
    975             name: script.name(),
    976             lineOffset: script.lineOffset(),
    977             columnOffset: script.columnOffset(),
    978             lineCount: script.lineCount(),
    979           };
    980   if (!IS_UNDEFINED(script.data())) {
    981     o.data = script.data();
    982   }
    983   if (include_source) {
    984     o.source = script.source();
    985   }
    986   return o;
    987 }
    988 
    989 
    990 function MakeAsyncTaskEvent(type, id) {
    991   return new AsyncTaskEvent(type, id);
    992 }
    993 
    994 
    995 function AsyncTaskEvent(type, id) {
    996   this.type_ = type;
    997   this.id_ = id;
    998 }
    999 
   1000 
   1001 AsyncTaskEvent.prototype.type = function() {
   1002   return this.type_;
   1003 }
   1004 
   1005 
   1006 AsyncTaskEvent.prototype.id = function() {
   1007   return this.id_;
   1008 }
   1009 
   1010 // -------------------------------------------------------------------
   1011 // Exports
   1012 
   1013 utils.InstallConstants(global, [
   1014   "Debug", Debug,
   1015   "BreakEvent", BreakEvent,
   1016   "CompileEvent", CompileEvent,
   1017   "BreakPoint", BreakPoint,
   1018 ]);
   1019 
   1020 // Functions needed by the debugger runtime.
   1021 utils.InstallFunctions(utils, DONT_ENUM, [
   1022   "MakeExecutionState", MakeExecutionState,
   1023   "MakeExceptionEvent", MakeExceptionEvent,
   1024   "MakeBreakEvent", MakeBreakEvent,
   1025   "MakeCompileEvent", MakeCompileEvent,
   1026   "MakeAsyncTaskEvent", MakeAsyncTaskEvent,
   1027   "IsBreakPointTriggered", IsBreakPointTriggered,
   1028 ]);
   1029 
   1030 })
   1031