Home | History | Annotate | Download | only in src
      1 // Copyright 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 String.prototype.startsWith = function (str) {
     29   if (str.length > this.length)
     30     return false;
     31   return this.substr(0, str.length) == str;
     32 }
     33 
     34 function log10(num) {
     35   return Math.log(num)/Math.log(10);
     36 }
     37 
     38 function ToInspectableObject(obj) {
     39   if (!obj && typeof obj === 'object') {
     40     return void 0;
     41   } else {
     42     return Object(obj);
     43   }
     44 }
     45 
     46 function GetCompletions(global, last, full) {
     47   var full_tokens = full.split();
     48   full = full_tokens.pop();
     49   var parts = full.split('.');
     50   parts.pop();
     51   var current = global;
     52   for (var i = 0; i < parts.length; i++) {
     53     var part = parts[i];
     54     var next = current[part];
     55     if (!next)
     56       return [];
     57     current = next;
     58   }
     59   var result = [];
     60   current = ToInspectableObject(current);
     61   while (typeof current !== 'undefined') {
     62     var mirror = new $debug.ObjectMirror(current);
     63     var properties = mirror.properties();
     64     for (var i = 0; i < properties.length; i++) {
     65       var name = properties[i].name();
     66       if (typeof name === 'string' && name.startsWith(last))
     67         result.push(name);
     68     }
     69     current = ToInspectableObject(current.__proto__);
     70   }
     71   return result;
     72 }
     73 
     74 
     75 // Global object holding debugger related constants and state.
     76 const Debug = {};
     77 
     78 
     79 // Debug events which can occour in the V8 JavaScript engine. These originate
     80 // from the API include file v8-debug.h.
     81 Debug.DebugEvent = { Break: 1,
     82                      Exception: 2,
     83                      NewFunction: 3,
     84                      BeforeCompile: 4,
     85                      AfterCompile: 5 };
     86 
     87 
     88 // The different types of scripts matching enum ScriptType in objects.h.
     89 Debug.ScriptType = { Native: 0,
     90                      Extension: 1,
     91                      Normal: 2 };
     92 
     93 
     94 // The different types of script compilations matching enum
     95 // Script::CompilationType in objects.h.
     96 Debug.ScriptCompilationType = { Host: 0,
     97                                 Eval: 1,
     98                                 JSON: 2 };
     99 
    100 
    101 // The different types of scopes matching constants runtime.cc.
    102 Debug.ScopeType = { Global: 0,
    103                     Local: 1,
    104                     With: 2,
    105                     Closure: 3,
    106                     Catch: 4 };
    107 
    108 
    109 // Current debug state.
    110 const kNoFrame = -1;
    111 Debug.State = {
    112   currentFrame: kNoFrame,
    113   displaySourceStartLine: -1,
    114   displaySourceEndLine: -1,
    115   currentSourceLine: -1
    116 }
    117 var trace_compile = false;  // Tracing all compile events?
    118 var trace_debug_json = false; // Tracing all debug json packets?
    119 var last_cmd_line = '';
    120 //var lol_is_enabled;  // Set to true in d8.cc if LIVE_OBJECT_LIST is defined.
    121 var lol_next_dump_index = 0;
    122 const kDefaultLolLinesToPrintAtATime = 10;
    123 const kMaxLolLinesToPrintAtATime = 1000;
    124 var repeat_cmd_line = '';
    125 var is_running = true;
    126 
    127 // Copied from debug-delay.js.  This is needed below:
    128 function ScriptTypeFlag(type) {
    129   return (1 << type);
    130 }
    131 
    132 
    133 // Process a debugger JSON message into a display text and a running status.
    134 // This function returns an object with properties "text" and "running" holding
    135 // this information.
    136 function DebugMessageDetails(message) {
    137   if (trace_debug_json) {
    138     print("received: '" + message + "'");
    139   }
    140   // Convert the JSON string to an object.
    141   var response = new ProtocolPackage(message);
    142   is_running = response.running();
    143 
    144   if (response.type() == 'event') {
    145     return DebugEventDetails(response);
    146   } else {
    147     return DebugResponseDetails(response);
    148   }
    149 }
    150 
    151 function DebugEventDetails(response) {
    152   details = {text:'', running:false}
    153 
    154   // Get the running state.
    155   details.running = response.running();
    156 
    157   var body = response.body();
    158   var result = '';
    159   switch (response.event()) {
    160     case 'break':
    161       if (body.breakpoints) {
    162         result += 'breakpoint';
    163         if (body.breakpoints.length > 1) {
    164           result += 's';
    165         }
    166         result += ' #';
    167         for (var i = 0; i < body.breakpoints.length; i++) {
    168           if (i > 0) {
    169             result += ', #';
    170           }
    171           result += body.breakpoints[i];
    172         }
    173       } else {
    174         result += 'break';
    175       }
    176       result += ' in ';
    177       result += body.invocationText;
    178       result += ', ';
    179       result += SourceInfo(body);
    180       result += '\n';
    181       result += SourceUnderline(body.sourceLineText, body.sourceColumn);
    182       Debug.State.currentSourceLine = body.sourceLine;
    183       Debug.State.displaySourceStartLine = -1;
    184       Debug.State.displaySourceEndLine = -1;
    185       Debug.State.currentFrame = 0;
    186       details.text = result;
    187       break;
    188 
    189     case 'exception':
    190       if (body.uncaught) {
    191         result += 'Uncaught: ';
    192       } else {
    193         result += 'Exception: ';
    194       }
    195       result += '"';
    196       result += body.exception.text;
    197       result += '"';
    198       if (body.sourceLine >= 0) {
    199         result += ', ';
    200         result += SourceInfo(body);
    201         result += '\n';
    202         result += SourceUnderline(body.sourceLineText, body.sourceColumn);
    203         Debug.State.currentSourceLine = body.sourceLine;
    204         Debug.State.displaySourceStartLine = -1;
    205         Debug.State.displaySourceEndLine = -1;
    206         Debug.State.currentFrame = 0;
    207       } else {
    208         result += ' (empty stack)';
    209         Debug.State.currentSourceLine = -1;
    210         Debug.State.displaySourceStartLine = -1;
    211         Debug.State.displaySourceEndLine = -1;
    212         Debug.State.currentFrame = kNoFrame;
    213       }
    214       details.text = result;
    215       break;
    216 
    217     case 'afterCompile':
    218       if (trace_compile) {
    219         result = 'Source ' + body.script.name + ' compiled:\n'
    220         var source = body.script.source;
    221         if (!(source[source.length - 1] == '\n')) {
    222           result += source;
    223         } else {
    224           result += source.substring(0, source.length - 1);
    225         }
    226       }
    227       details.text = result;
    228       break;
    229 
    230     case 'scriptCollected':
    231       details.text = result;
    232       break;
    233 
    234     default:
    235       details.text = 'Unknown debug event ' + response.event();
    236   }
    237 
    238   return details;
    239 };
    240 
    241 
    242 function SourceInfo(body) {
    243   var result = '';
    244 
    245   if (body.script) {
    246     if (body.script.name) {
    247       result += body.script.name;
    248     } else {
    249       result += '[unnamed]';
    250     }
    251   }
    252   result += ' line ';
    253   result += body.sourceLine + 1;
    254   result += ' column ';
    255   result += body.sourceColumn + 1;
    256 
    257   return result;
    258 }
    259 
    260 
    261 function SourceUnderline(source_text, position) {
    262   if (!source_text) {
    263     return;
    264   }
    265 
    266   // Create an underline with a caret pointing to the source position. If the
    267   // source contains a tab character the underline will have a tab character in
    268   // the same place otherwise the underline will have a space character.
    269   var underline = '';
    270   for (var i = 0; i < position; i++) {
    271     if (source_text[i] == '\t') {
    272       underline += '\t';
    273     } else {
    274       underline += ' ';
    275     }
    276   }
    277   underline += '^';
    278 
    279   // Return the source line text with the underline beneath.
    280   return source_text + '\n' + underline;
    281 };
    282 
    283 
    284 // Converts a text command to a JSON request.
    285 function DebugCommandToJSONRequest(cmd_line) {
    286   var result = new DebugRequest(cmd_line).JSONRequest();
    287   if (trace_debug_json && result) {
    288     print("sending: '" + result + "'");
    289   }
    290   return result;
    291 };
    292 
    293 
    294 function DebugRequest(cmd_line) {
    295   // If the very first character is a { assume that a JSON request have been
    296   // entered as a command. Converting that to a JSON request is trivial.
    297   if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') {
    298     this.request_ = cmd_line;
    299     return;
    300   }
    301 
    302   // Check for a simple carriage return to repeat the last command:
    303   var is_repeating = false;
    304   if (cmd_line == '\n') {
    305     if (is_running) {
    306       cmd_line = 'break'; // Not in debugger mode, break with a frame request.
    307     } else {
    308       cmd_line = repeat_cmd_line; // use command to repeat.
    309       is_repeating = true;
    310     }
    311   }
    312   if (!is_running) { // Only save the command if in debugger mode.
    313     repeat_cmd_line = cmd_line;   // save last command.
    314   }
    315 
    316   // Trim string for leading and trailing whitespace.
    317   cmd_line = cmd_line.replace(/^\s+|\s+$/g, '');
    318 
    319   // Find the command.
    320   var pos = cmd_line.indexOf(' ');
    321   var cmd;
    322   var args;
    323   if (pos == -1) {
    324     cmd = cmd_line;
    325     args = '';
    326   } else {
    327     cmd = cmd_line.slice(0, pos);
    328     args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, '');
    329   }
    330 
    331   if ((cmd === undefined) || !cmd) {
    332     this.request_ = void 0;
    333     return;
    334   }
    335 
    336   last_cmd = cmd;
    337 
    338   // Switch on command.
    339   switch (cmd) {
    340     case 'continue':
    341     case 'c':
    342       this.request_ = this.continueCommandToJSONRequest_(args);
    343       break;
    344 
    345     case 'step':
    346     case 's':
    347       this.request_ = this.stepCommandToJSONRequest_(args, 'in');
    348       break;
    349 
    350     case 'stepi':
    351     case 'si':
    352       this.request_ = this.stepCommandToJSONRequest_(args, 'min');
    353       break;
    354 
    355     case 'next':
    356     case 'n':
    357       this.request_ = this.stepCommandToJSONRequest_(args, 'next');
    358       break;
    359 
    360     case 'finish':
    361     case 'fin':
    362       this.request_ = this.stepCommandToJSONRequest_(args, 'out');
    363       break;
    364 
    365     case 'backtrace':
    366     case 'bt':
    367       this.request_ = this.backtraceCommandToJSONRequest_(args);
    368       break;
    369 
    370     case 'frame':
    371     case 'f':
    372       this.request_ = this.frameCommandToJSONRequest_(args);
    373       break;
    374 
    375     case 'scopes':
    376       this.request_ = this.scopesCommandToJSONRequest_(args);
    377       break;
    378 
    379     case 'scope':
    380       this.request_ = this.scopeCommandToJSONRequest_(args);
    381       break;
    382 
    383     case 'disconnect':
    384     case 'exit':
    385     case 'quit':
    386       this.request_ = this.disconnectCommandToJSONRequest_(args);
    387       break;
    388 
    389     case 'up':
    390       this.request_ =
    391           this.frameCommandToJSONRequest_('' +
    392                                           (Debug.State.currentFrame + 1));
    393       break;
    394       
    395     case 'down':
    396     case 'do':
    397       this.request_ =
    398           this.frameCommandToJSONRequest_('' +
    399                                           (Debug.State.currentFrame - 1));
    400       break;
    401       
    402     case 'set':
    403     case 'print':
    404     case 'p':
    405       this.request_ = this.printCommandToJSONRequest_(args);
    406       break;
    407 
    408     case 'dir':
    409       this.request_ = this.dirCommandToJSONRequest_(args);
    410       break;
    411 
    412     case 'references':
    413       this.request_ = this.referencesCommandToJSONRequest_(args);
    414       break;
    415 
    416     case 'instances':
    417       this.request_ = this.instancesCommandToJSONRequest_(args);
    418       break;
    419 
    420     case 'list':
    421     case 'l':
    422       this.request_ = this.listCommandToJSONRequest_(args);
    423       break;
    424     case 'source':
    425       this.request_ = this.sourceCommandToJSONRequest_(args);
    426       break;
    427 
    428     case 'scripts':
    429     case 'script':
    430     case 'scr':
    431       this.request_ = this.scriptsCommandToJSONRequest_(args);
    432       break;
    433 
    434     case 'break':
    435     case 'b':
    436       this.request_ = this.breakCommandToJSONRequest_(args);
    437       break;
    438 
    439     case 'breakpoints':
    440     case 'bb':
    441       this.request_ = this.breakpointsCommandToJSONRequest_(args);
    442       break;
    443 
    444     case 'clear':
    445     case 'delete':
    446     case 'd':
    447       this.request_ = this.clearCommandToJSONRequest_(args);
    448       break;
    449 
    450     case 'threads':
    451       this.request_ = this.threadsCommandToJSONRequest_(args);
    452       break;
    453 
    454     case 'cond':
    455       this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond');
    456       break;
    457 
    458     case 'enable':
    459     case 'en':
    460       this.request_ =
    461           this.changeBreakpointCommandToJSONRequest_(args, 'enable');
    462       break;
    463 
    464     case 'disable':
    465     case 'dis':
    466       this.request_ =
    467           this.changeBreakpointCommandToJSONRequest_(args, 'disable');
    468       break;
    469 
    470     case 'ignore':
    471       this.request_ =
    472           this.changeBreakpointCommandToJSONRequest_(args, 'ignore');
    473       break;
    474 
    475     case 'info':
    476     case 'inf':
    477       this.request_ = this.infoCommandToJSONRequest_(args);
    478       break;
    479 
    480     case 'flags':
    481       this.request_ = this.v8FlagsToJSONRequest_(args);
    482       break;
    483 
    484     case 'gc':
    485       this.request_ = this.gcToJSONRequest_(args);
    486       break;
    487 
    488     case 'trace':
    489     case 'tr':
    490       // Return undefined to indicate command handled internally (no JSON).
    491       this.request_ = void 0;
    492       this.traceCommand_(args);
    493       break;
    494 
    495     case 'help':
    496     case '?':
    497       this.helpCommand_(args);
    498       // Return undefined to indicate command handled internally (no JSON).
    499       this.request_ = void 0;
    500       break;
    501 
    502     case 'liveobjectlist':
    503     case 'lol':
    504       if (lol_is_enabled) {
    505         this.request_ = this.lolToJSONRequest_(args, is_repeating);
    506         break;
    507       }
    508 
    509     default:
    510       throw new Error('Unknown command "' + cmd + '"');
    511   }
    512 }
    513 
    514 DebugRequest.prototype.JSONRequest = function() {
    515   return this.request_;
    516 }
    517 
    518 
    519 function RequestPacket(command) {
    520   this.seq = 0;
    521   this.type = 'request';
    522   this.command = command;
    523 }
    524 
    525 
    526 RequestPacket.prototype.toJSONProtocol = function() {
    527   // Encode the protocol header.
    528   var json = '{';
    529   json += '"seq":' + this.seq;
    530   json += ',"type":"' + this.type + '"';
    531   if (this.command) {
    532     json += ',"command":' + StringToJSON_(this.command);
    533   }
    534   if (this.arguments) {
    535     json += ',"arguments":';
    536     // Encode the arguments part.
    537     if (this.arguments.toJSONProtocol) {
    538       json += this.arguments.toJSONProtocol()
    539     } else {
    540       json += SimpleObjectToJSON_(this.arguments);
    541     }
    542   }
    543   json += '}';
    544   return json;
    545 }
    546 
    547 
    548 DebugRequest.prototype.createRequest = function(command) {
    549   return new RequestPacket(command);
    550 };
    551 
    552 
    553 // Note: we use detected command repetition as a signal for continuation here.
    554 DebugRequest.prototype.createLOLRequest = function(command,
    555                                                    start_index,
    556                                                    lines_to_dump,
    557                                                    is_continuation) {
    558   if (is_continuation) {
    559     start_index = lol_next_dump_index;
    560   }
    561 
    562   if (lines_to_dump) {
    563     lines_to_dump = parseInt(lines_to_dump);
    564   } else {
    565     lines_to_dump = kDefaultLolLinesToPrintAtATime;
    566   }
    567   if (lines_to_dump > kMaxLolLinesToPrintAtATime) {
    568     lines_to_dump = kMaxLolLinesToPrintAtATime;
    569   }
    570 
    571   // Save the next start_index to dump from:
    572   lol_next_dump_index = start_index + lines_to_dump;
    573 
    574   var request = this.createRequest(command);
    575   request.arguments = {};
    576   request.arguments.start = start_index;
    577   request.arguments.count = lines_to_dump;
    578 
    579   return request;
    580 };
    581 
    582 
    583 // Create a JSON request for the evaluation command.
    584 DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
    585   // Global varaible used to store whether a handle was requested.
    586   lookup_handle = null;
    587 
    588   if (lol_is_enabled) {
    589     // Check if the expression is a obj id in the form @<obj id>.
    590     var obj_id_match = expression.match(/^@([0-9]+)$/);
    591     if (obj_id_match) {
    592       var obj_id = parseInt(obj_id_match[1]);
    593       // Build a dump request.
    594       var request = this.createRequest('getobj');
    595       request.arguments = {};
    596       request.arguments.obj_id = obj_id;
    597       return request.toJSONProtocol();
    598     }
    599   }
    600 
    601   // Check if the expression is a handle id in the form #<handle>#.
    602   var handle_match = expression.match(/^#([0-9]*)#$/);
    603   if (handle_match) {
    604     // Remember the handle requested in a global variable.
    605     lookup_handle = parseInt(handle_match[1]);
    606     // Build a lookup request.
    607     var request = this.createRequest('lookup');
    608     request.arguments = {};
    609     request.arguments.handles = [ lookup_handle ];
    610     return request.toJSONProtocol();
    611   } else {
    612     // Build an evaluate request.
    613     var request = this.createRequest('evaluate');
    614     request.arguments = {};
    615     request.arguments.expression = expression;
    616     // Request a global evaluation if there is no current frame.
    617     if (Debug.State.currentFrame == kNoFrame) {
    618       request.arguments.global = true;
    619     }
    620     return request.toJSONProtocol();
    621   }
    622 };
    623 
    624 
    625 // Create a JSON request for the references/instances command.
    626 DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
    627   // Build a references request.
    628   var handle_match = handle.match(/^#([0-9]*)#$/);
    629   if (handle_match) {
    630     var request = this.createRequest('references');
    631     request.arguments = {};
    632     request.arguments.type = type;
    633     request.arguments.handle = parseInt(handle_match[1]);
    634     return request.toJSONProtocol();
    635   } else {
    636     throw new Error('Invalid object id.');
    637   }
    638 };
    639 
    640 
    641 // Create a JSON request for the continue command.
    642 DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
    643   var request = this.createRequest('continue');
    644   return request.toJSONProtocol();
    645 };
    646 
    647 
    648 // Create a JSON request for the step command.
    649 DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) {
    650   // Requesting a step is through the continue command with additional
    651   // arguments.
    652   var request = this.createRequest('continue');
    653   request.arguments = {};
    654 
    655   // Process arguments if any.
    656 
    657   // Only process args if the command is 'step' which is indicated by type being
    658   // set to 'in'.  For all other commands, ignore the args.
    659   if (args && args.length > 0) {
    660     args = args.split(/\s+/g);
    661 
    662     if (args.length > 2) {
    663       throw new Error('Invalid step arguments.');
    664     }
    665 
    666     if (args.length > 0) {
    667       // Check if we have a gdb stype step command.  If so, the 1st arg would
    668       // be the step count.  If it's not a number, then assume that we're
    669       // parsing for the legacy v8 step command.
    670       var stepcount = Number(args[0]);
    671       if (stepcount == Number.NaN) {
    672         // No step count at arg 1.  Process as legacy d8 step command:
    673         if (args.length == 2) {
    674           var stepcount = parseInt(args[1]);
    675           if (isNaN(stepcount) || stepcount <= 0) {
    676             throw new Error('Invalid step count argument "' + args[0] + '".');
    677           }
    678           request.arguments.stepcount = stepcount;
    679         }
    680 
    681         // Get the step action.
    682         switch (args[0]) {
    683           case 'in':
    684           case 'i':
    685             request.arguments.stepaction = 'in';
    686             break;
    687 
    688           case 'min':
    689           case 'm':
    690             request.arguments.stepaction = 'min';
    691             break;
    692 
    693           case 'next':
    694           case 'n':
    695             request.arguments.stepaction = 'next';
    696             break;
    697 
    698           case 'out':
    699           case 'o':
    700             request.arguments.stepaction = 'out';
    701             break;
    702 
    703           default:
    704             throw new Error('Invalid step argument "' + args[0] + '".');
    705         }
    706 
    707       } else {
    708         // gdb style step commands:
    709         request.arguments.stepaction = type;
    710         request.arguments.stepcount = stepcount;
    711       }
    712     }
    713   } else {
    714     // Default is step of the specified type.
    715     request.arguments.stepaction = type;
    716   }
    717 
    718   return request.toJSONProtocol();
    719 };
    720 
    721 
    722 // Create a JSON request for the backtrace command.
    723 DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) {
    724   // Build a backtrace request from the text command.
    725   var request = this.createRequest('backtrace');
    726 
    727   // Default is to show top 10 frames.
    728   request.arguments = {};
    729   request.arguments.fromFrame = 0;
    730   request.arguments.toFrame = 10;
    731 
    732   args = args.split(/\s*[ ]+\s*/g);
    733   if (args.length == 1 && args[0].length > 0) {
    734     var frameCount = parseInt(args[0]);
    735     if (frameCount > 0) {
    736       // Show top frames.
    737       request.arguments.fromFrame = 0;
    738       request.arguments.toFrame = frameCount;
    739     } else {
    740       // Show bottom frames.
    741       request.arguments.fromFrame = 0;
    742       request.arguments.toFrame = -frameCount;
    743       request.arguments.bottom = true;
    744     }
    745   } else if (args.length == 2) {
    746     var fromFrame = parseInt(args[0]);
    747     var toFrame = parseInt(args[1]);
    748     if (isNaN(fromFrame) || fromFrame < 0) {
    749       throw new Error('Invalid start frame argument "' + args[0] + '".');
    750     }
    751     if (isNaN(toFrame) || toFrame < 0) {
    752       throw new Error('Invalid end frame argument "' + args[1] + '".');
    753     }
    754     if (fromFrame > toFrame) {
    755       throw new Error('Invalid arguments start frame cannot be larger ' +
    756                       'than end frame.');
    757     }
    758     // Show frame range.
    759     request.arguments.fromFrame = fromFrame;
    760     request.arguments.toFrame = toFrame + 1;
    761   } else if (args.length > 2) {
    762     throw new Error('Invalid backtrace arguments.');
    763   }
    764 
    765   return request.toJSONProtocol();
    766 };
    767 
    768 
    769 // Create a JSON request for the frame command.
    770 DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) {
    771   // Build a frame request from the text command.
    772   var request = this.createRequest('frame');
    773   args = args.split(/\s*[ ]+\s*/g);
    774   if (args.length > 0 && args[0].length > 0) {
    775     request.arguments = {};
    776     request.arguments.number = args[0];
    777   }
    778   return request.toJSONProtocol();
    779 };
    780 
    781 
    782 // Create a JSON request for the scopes command.
    783 DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) {
    784   // Build a scopes request from the text command.
    785   var request = this.createRequest('scopes');
    786   return request.toJSONProtocol();
    787 };
    788 
    789 
    790 // Create a JSON request for the scope command.
    791 DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) {
    792   // Build a scope request from the text command.
    793   var request = this.createRequest('scope');
    794   args = args.split(/\s*[ ]+\s*/g);
    795   if (args.length > 0 && args[0].length > 0) {
    796     request.arguments = {};
    797     request.arguments.number = args[0];
    798   }
    799   return request.toJSONProtocol();
    800 };
    801 
    802 
    803 // Create a JSON request for the print command.
    804 DebugRequest.prototype.printCommandToJSONRequest_ = function(args) {
    805   // Build an evaluate request from the text command.
    806   if (args.length == 0) {
    807     throw new Error('Missing expression.');
    808   }
    809   return this.makeEvaluateJSONRequest_(args);
    810 };
    811 
    812 
    813 // Create a JSON request for the dir command.
    814 DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
    815   // Build an evaluate request from the text command.
    816   if (args.length == 0) {
    817     throw new Error('Missing expression.');
    818   }
    819   return this.makeEvaluateJSONRequest_(args);
    820 };
    821 
    822 
    823 // Create a JSON request for the references command.
    824 DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
    825   // Build an evaluate request from the text command.
    826   if (args.length == 0) {
    827     throw new Error('Missing object id.');
    828   }
    829 
    830   return this.makeReferencesJSONRequest_(args, 'referencedBy');
    831 };
    832 
    833 
    834 // Create a JSON request for the instances command.
    835 DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
    836   // Build an evaluate request from the text command.
    837   if (args.length == 0) {
    838     throw new Error('Missing object id.');
    839   }
    840 
    841   // Build a references request.
    842   return this.makeReferencesJSONRequest_(args, 'constructedBy');
    843 };
    844 
    845 
    846 // Create a JSON request for the list command.
    847 DebugRequest.prototype.listCommandToJSONRequest_ = function(args) {
    848 
    849   // Default is ten lines starting five lines before the current location.
    850   if (Debug.State.displaySourceEndLine == -1) {
    851     // If we list forwards, we will start listing after the last source end
    852     // line.  Set it to start from 5 lines before the current location.
    853     Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5;
    854     // If we list backwards, we will start listing backwards from the last
    855     // source start line.  Set it to start from 1 lines before the current
    856     // location.
    857     Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1;
    858   }
    859 
    860   var from = Debug.State.displaySourceEndLine + 1;
    861   var lines = 10;
    862 
    863   // Parse the arguments.
    864   args = args.split(/\s*,\s*/g);
    865   if (args == '') {
    866   } else if ((args.length == 1) && (args[0] == '-')) {
    867     from = Debug.State.displaySourceStartLine - lines;
    868   } else if (args.length == 2) {
    869     from = parseInt(args[0]);
    870     lines = parseInt(args[1]) - from + 1; // inclusive of the ending line.
    871   } else {
    872     throw new Error('Invalid list arguments.');
    873   }
    874   Debug.State.displaySourceStartLine = from;
    875   Debug.State.displaySourceEndLine = from + lines - 1;
    876   var sourceArgs = '' + from + ' ' + lines;
    877   return this.sourceCommandToJSONRequest_(sourceArgs);
    878 };
    879 
    880 
    881 // Create a JSON request for the source command.
    882 DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
    883   // Build a evaluate request from the text command.
    884   var request = this.createRequest('source');
    885 
    886   // Default is ten lines starting five lines before the current location.
    887   var from = Debug.State.currentSourceLine - 5;
    888   var lines = 10;
    889 
    890   // Parse the arguments.
    891   args = args.split(/\s*[ ]+\s*/g);
    892   if (args.length > 1 && args[0].length > 0 && args[1].length > 0) {
    893     from = parseInt(args[0]) - 1;
    894     lines = parseInt(args[1]);
    895   } else if (args.length > 0 && args[0].length > 0) {
    896     from = parseInt(args[0]) - 1;
    897   }
    898 
    899   if (from < 0) from = 0;
    900   if (lines < 0) lines = 10;
    901 
    902   // Request source arround current source location.
    903   request.arguments = {};
    904   request.arguments.fromLine = from;
    905   request.arguments.toLine = from + lines;
    906 
    907   return request.toJSONProtocol();
    908 };
    909 
    910 
    911 // Create a JSON request for the scripts command.
    912 DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) {
    913   // Build a evaluate request from the text command.
    914   var request = this.createRequest('scripts');
    915 
    916   // Process arguments if any.
    917   if (args && args.length > 0) {
    918     args = args.split(/\s*[ ]+\s*/g);
    919 
    920     if (args.length > 1) {
    921       throw new Error('Invalid scripts arguments.');
    922     }
    923 
    924     request.arguments = {};
    925     switch (args[0]) {
    926       case 'natives':
    927         request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native);
    928         break;
    929 
    930       case 'extensions':
    931         request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension);
    932         break;
    933 
    934       case 'all':
    935         request.arguments.types =
    936             ScriptTypeFlag(Debug.ScriptType.Normal) |
    937             ScriptTypeFlag(Debug.ScriptType.Native) |
    938             ScriptTypeFlag(Debug.ScriptType.Extension);
    939         break;
    940 
    941       default:
    942         // If the arg is not one of the know one aboves, then it must be a
    943         // filter used for filtering the results:
    944         request.arguments.filter = args[0];
    945         break;
    946     }
    947   }
    948 
    949   return request.toJSONProtocol();
    950 };
    951 
    952 
    953 // Create a JSON request for the break command.
    954 DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) {
    955   // Build a evaluate request from the text command.
    956   // Process arguments if any.
    957   if (args && args.length > 0) {
    958     var target = args;
    959     var type = 'function';
    960     var line;
    961     var column;
    962     var condition;
    963     var pos;
    964 
    965     var request = this.createRequest('setbreakpoint');
    966 
    967     // Break the args into target spec and condition if appropriate.
    968 
    969     // Check for breakpoint condition.
    970     pos = args.indexOf(' ');
    971     if (pos > 0) {
    972       target = args.substring(0, pos);
    973       condition = args.substring(pos + 1, args.length);
    974     }
    975 
    976     // Check for script breakpoint (name:line[:column]). If no ':' in break
    977     // specification it is considered a function break point.
    978     pos = target.indexOf(':');
    979     if (pos > 0) {
    980       type = 'script';
    981       var tmp = target.substring(pos + 1, target.length);
    982       target = target.substring(0, pos);
    983 
    984       // Check for both line and column.
    985       pos = tmp.indexOf(':');
    986       if (pos > 0) {
    987         column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1;
    988         line = parseInt(tmp.substring(0, pos)) - 1;
    989       } else {
    990         line = parseInt(tmp) - 1;
    991       }
    992     } else if (target[0] == '#' && target[target.length - 1] == '#') {
    993       type = 'handle';
    994       target = target.substring(1, target.length - 1);
    995     } else {
    996       type = 'function';
    997     }
    998 
    999     request.arguments = {};
   1000     request.arguments.type = type;
   1001     request.arguments.target = target;
   1002     request.arguments.line = line;
   1003     request.arguments.column = column;
   1004     request.arguments.condition = condition;
   1005   } else {
   1006     var request = this.createRequest('suspend');
   1007   }
   1008 
   1009   return request.toJSONProtocol();
   1010 };
   1011 
   1012 
   1013 DebugRequest.prototype.breakpointsCommandToJSONRequest_ = function(args) {
   1014   if (args && args.length > 0) {
   1015     throw new Error('Unexpected arguments.');
   1016   }
   1017   var request = this.createRequest('listbreakpoints');
   1018   return request.toJSONProtocol();
   1019 };
   1020 
   1021 
   1022 // Create a JSON request for the clear command.
   1023 DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) {
   1024   // Build a evaluate request from the text command.
   1025   var request = this.createRequest('clearbreakpoint');
   1026 
   1027   // Process arguments if any.
   1028   if (args && args.length > 0) {
   1029     request.arguments = {};
   1030     request.arguments.breakpoint = parseInt(args);
   1031   } else {
   1032     throw new Error('Invalid break arguments.');
   1033   }
   1034 
   1035   return request.toJSONProtocol();
   1036 };
   1037 
   1038 
   1039 // Create a JSON request for the change breakpoint command.
   1040 DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ =
   1041     function(args, command) {
   1042 
   1043   var request;
   1044 
   1045   // Check for exception breaks first:
   1046   //   en[able] exc[eptions] [all|unc[aught]]
   1047   //   en[able] [all|unc[aught]] exc[eptions]
   1048   //   dis[able] exc[eptions] [all|unc[aught]]
   1049   //   dis[able] [all|unc[aught]] exc[eptions]
   1050   if ((command == 'enable' || command == 'disable') &&
   1051       args && args.length > 1) {
   1052     var nextPos = args.indexOf(' ');
   1053     var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args;
   1054     var excType = null;
   1055 
   1056     // Check for:
   1057     //   en[able] exc[eptions] [all|unc[aught]]
   1058     //   dis[able] exc[eptions] [all|unc[aught]]
   1059     if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
   1060 
   1061       var arg2 = (nextPos > 0) ?
   1062           args.substring(nextPos + 1, args.length) : 'all';
   1063       if (!arg2) {
   1064         arg2 = 'all'; // if unspecified, set for all.
   1065       } if (arg2 == 'unc') { // check for short cut.
   1066         arg2 = 'uncaught';
   1067       }
   1068       excType = arg2;
   1069       
   1070     // Check for:
   1071     //   en[able] [all|unc[aught]] exc[eptions]
   1072     //   dis[able] [all|unc[aught]] exc[eptions]
   1073     } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') {
   1074 
   1075       var arg2 = (nextPos > 0) ?
   1076           args.substring(nextPos + 1, args.length) : null;
   1077       if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
   1078         excType = arg1;
   1079         if (excType == 'unc') {
   1080           excType = 'uncaught';
   1081         }
   1082       }
   1083     }
   1084 
   1085     // If we matched one of the command formats, then excType will be non-null:
   1086     if (excType) {
   1087       // Build a evaluate request from the text command.
   1088       request = this.createRequest('setexceptionbreak');
   1089 
   1090       request.arguments = {};
   1091       request.arguments.type = excType;
   1092       request.arguments.enabled = (command == 'enable');
   1093 
   1094       return request.toJSONProtocol();
   1095     }
   1096   }
   1097 
   1098   // Build a evaluate request from the text command.
   1099   request = this.createRequest('changebreakpoint');
   1100 
   1101   // Process arguments if any.
   1102   if (args && args.length > 0) {
   1103     request.arguments = {};
   1104     var pos = args.indexOf(' ');
   1105     var breakpointArg = args;
   1106     var otherArgs;
   1107     if (pos > 0) {
   1108       breakpointArg = args.substring(0, pos);
   1109       otherArgs = args.substring(pos + 1, args.length);
   1110     }
   1111 
   1112     request.arguments.breakpoint = parseInt(breakpointArg);
   1113 
   1114     switch(command) {
   1115       case 'cond':
   1116         request.arguments.condition = otherArgs ? otherArgs : null;
   1117         break;
   1118       case 'enable':
   1119         request.arguments.enabled = true;
   1120         break;
   1121       case 'disable':
   1122         request.arguments.enabled = false;
   1123         break;
   1124       case 'ignore':
   1125         request.arguments.ignoreCount = parseInt(otherArgs);
   1126         break;
   1127       default:
   1128         throw new Error('Invalid arguments.');  
   1129     }
   1130   } else {
   1131     throw new Error('Invalid arguments.');
   1132   }
   1133 
   1134   return request.toJSONProtocol();
   1135 };
   1136 
   1137 
   1138 // Create a JSON request for the disconnect command.
   1139 DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) {
   1140   var request;
   1141   request = this.createRequest('disconnect');
   1142   return request.toJSONProtocol();
   1143 };
   1144 
   1145 
   1146 // Create a JSON request for the info command.
   1147 DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) {
   1148   var request;
   1149   if (args && (args == 'break' || args == 'br')) {
   1150     // Build a evaluate request from the text command.
   1151     request = this.createRequest('listbreakpoints');
   1152     last_cmd = 'info break';
   1153   } else if (args && (args == 'locals' || args == 'lo')) {
   1154     // Build a evaluate request from the text command.
   1155     request = this.createRequest('frame');
   1156     last_cmd = 'info locals';
   1157   } else if (args && (args == 'args' || args == 'ar')) {
   1158     // Build a evaluate request from the text command.
   1159     request = this.createRequest('frame');
   1160     last_cmd = 'info args';
   1161   } else if (lol_is_enabled &&
   1162              args && (args == 'liveobjectlist' || args == 'lol')) {
   1163     // Build a evaluate request from the text command.
   1164     return this.liveObjectListToJSONRequest_(null);
   1165   } else {
   1166     throw new Error('Invalid info arguments.');
   1167   }
   1168 
   1169   return request.toJSONProtocol();
   1170 };
   1171 
   1172 
   1173 DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) {
   1174   var request;
   1175   request = this.createRequest('v8flags');
   1176   request.arguments = {};
   1177   request.arguments.flags = args;
   1178   return request.toJSONProtocol();
   1179 };
   1180 
   1181 
   1182 DebugRequest.prototype.gcToJSONRequest_ = function(args) {
   1183   var request;
   1184   if (!args) {
   1185     args = 'all';
   1186   }
   1187   var args = args.split(/\s+/g);
   1188   var cmd = args[0];
   1189 
   1190   switch(cmd) {
   1191     case 'all':
   1192     case 'quick':
   1193     case 'full':
   1194     case 'young':
   1195     case 'old':
   1196     case 'compact':
   1197     case 'sweep':
   1198     case 'scavenge': {
   1199       if (cmd == 'young') { cmd = 'quick'; }
   1200       else if (cmd == 'old') { cmd = 'full'; }
   1201 
   1202       request = this.createRequest('gc');
   1203       request.arguments = {};
   1204       request.arguments.type = cmd;
   1205       break;
   1206     }
   1207       // Else fall thru to the default case below to report the error.
   1208     default:
   1209       throw new Error('Missing arguments after ' + cmd + '.');
   1210   }
   1211   return request.toJSONProtocol();
   1212 };
   1213 
   1214 
   1215 // Args: [v[erbose]] [<N>] [i[ndex] <i>] [t[ype] <type>] [sp[ace] <space>]
   1216 DebugRequest.prototype.lolMakeListRequest =
   1217     function(cmd, args, first_arg_index, is_repeating) {
   1218 
   1219   var request;
   1220   var start_index = 0;
   1221   var dump_limit = void 0;
   1222   var type_filter = void 0;
   1223   var space_filter = void 0;
   1224   var prop_filter = void 0;
   1225   var is_verbose = false;
   1226   var i;
   1227 
   1228   for (i = first_arg_index; i < args.length; i++) {
   1229     var arg = args[i];
   1230     // Check for [v[erbose]]:
   1231     if (arg === 'verbose' || arg === 'v') {
   1232       // Nothing to do.  This is already implied by args.length > 3.
   1233       is_verbose = true;
   1234 
   1235     // Check for [<N>]:
   1236     } else if (arg.match(/^[0-9]+$/)) {
   1237       dump_limit = arg;
   1238       is_verbose = true;
   1239 
   1240     // Check for i[ndex] <i>:
   1241     } else if (arg === 'index' || arg === 'i') {
   1242       i++;
   1243       if (args.length < i) {
   1244         throw new Error('Missing index after ' + arg + '.');
   1245       }
   1246       start_index = parseInt(args[i]);
   1247       // The user input start index starts at 1:
   1248       if (start_index <= 0) {
   1249         throw new Error('Invalid index ' + args[i] + '.');                
   1250       }
   1251       start_index -= 1;
   1252       is_verbose = true;
   1253 
   1254     // Check for t[ype] <type>:
   1255     } else if (arg === 'type' || arg === 't') {
   1256       i++;
   1257       if (args.length < i) {
   1258         throw new Error('Missing type after ' + arg + '.');
   1259       }
   1260       type_filter = args[i];
   1261 
   1262     // Check for space <heap space name>:
   1263     } else if (arg === 'space' || arg === 'sp') {
   1264       i++;
   1265       if (args.length < i) {
   1266         throw new Error('Missing space name after ' + arg + '.');
   1267       }
   1268       space_filter = args[i];
   1269 
   1270     // Check for property <prop name>:
   1271     } else if (arg === 'property' || arg === 'prop') {
   1272       i++;
   1273       if (args.length < i) {
   1274         throw new Error('Missing property name after ' + arg + '.');
   1275       }
   1276       prop_filter = args[i];
   1277 
   1278     } else {
   1279       throw new Error('Unknown args at ' + arg + '.');
   1280     }
   1281   }
   1282 
   1283   // Build the verbose request:
   1284   if (is_verbose) {
   1285     request = this.createLOLRequest('lol-'+cmd,
   1286                                     start_index,
   1287                                     dump_limit,
   1288                                     is_repeating);
   1289     request.arguments.verbose = true;
   1290   } else {
   1291     request = this.createRequest('lol-'+cmd);
   1292     request.arguments = {};
   1293   }
   1294 
   1295   request.arguments.filter = {};
   1296   if (type_filter) {
   1297     request.arguments.filter.type = type_filter;
   1298   }
   1299   if (space_filter) {
   1300     request.arguments.filter.space = space_filter;
   1301   }
   1302   if (prop_filter) {
   1303     request.arguments.filter.prop = prop_filter;
   1304   }
   1305 
   1306   return request;
   1307 }
   1308 
   1309 
   1310 function extractObjId(args) {
   1311   var id = args;
   1312   id = id.match(/^@([0-9]+)$/);
   1313   if (id) {
   1314     id = id[1];
   1315   } else {
   1316     throw new Error('Invalid obj id ' + args + '.');
   1317   }
   1318   return parseInt(id);
   1319 }
   1320 
   1321 
   1322 DebugRequest.prototype.lolToJSONRequest_ = function(args, is_repeating) {
   1323   var request;
   1324   // Use default command if one is not specified:
   1325   if (!args) {
   1326     args = 'info';
   1327   }
   1328 
   1329   var orig_args = args;
   1330   var first_arg_index;
   1331 
   1332   var arg, i;
   1333   var args = args.split(/\s+/g);
   1334   var cmd = args[0];
   1335   var id;
   1336 
   1337   // Command: <id> [v[erbose]] ...
   1338   if (cmd.match(/^[0-9]+$/)) {
   1339     // Convert to the padded list command:
   1340     // Command: l[ist] <dummy> <id> [v[erbose]] ...
   1341 
   1342     // Insert the implicit 'list' in front and process as normal:
   1343     cmd = 'list';
   1344     args.unshift(cmd);
   1345   }
   1346 
   1347   switch(cmd) {
   1348     // Command: c[apture]
   1349     case 'capture':
   1350     case 'c':
   1351       request = this.createRequest('lol-capture');
   1352       break;
   1353 
   1354     // Command: clear|d[elete] <id>|all
   1355     case 'clear':
   1356     case 'delete':
   1357     case 'del': {
   1358       if (args.length < 2) {
   1359         throw new Error('Missing argument after ' + cmd + '.');
   1360       } else if (args.length > 2) {
   1361         throw new Error('Too many arguments after ' + cmd + '.');
   1362       }
   1363       id = args[1];
   1364       if (id.match(/^[0-9]+$/)) {
   1365         // Delete a specific lol record:
   1366         request = this.createRequest('lol-delete');
   1367         request.arguments = {};
   1368         request.arguments.id = parseInt(id);
   1369       } else if (id === 'all') {
   1370         // Delete all:
   1371         request = this.createRequest('lol-reset');
   1372       } else {
   1373         throw new Error('Invalid argument after ' + cmd + '.');
   1374       }
   1375       break;
   1376     }
   1377 
   1378     // Command: diff <id1> <id2> [<dump options>]
   1379     case 'diff':
   1380       first_arg_index = 3;
   1381 
   1382     // Command: list <dummy> <id> [<dump options>]
   1383     case 'list':
   1384 
   1385     // Command: ret[ainers] <obj id> [<dump options>]
   1386     case 'retainers':
   1387     case 'ret':
   1388     case 'retaining-paths':
   1389     case 'rp': {
   1390       if (cmd === 'ret') cmd = 'retainers';
   1391       else if (cmd === 'rp') cmd = 'retaining-paths';
   1392 
   1393       if (!first_arg_index) first_arg_index = 2;
   1394 
   1395       if (args.length < first_arg_index) {
   1396         throw new Error('Too few arguments after ' + cmd + '.');
   1397       }
   1398 
   1399       var request_cmd = (cmd === 'list') ? 'diff':cmd;
   1400       request = this.lolMakeListRequest(request_cmd,
   1401                                         args,
   1402                                         first_arg_index,
   1403                                         is_repeating);
   1404 
   1405       if (cmd === 'diff') {
   1406         request.arguments.id1 = parseInt(args[1]);
   1407         request.arguments.id2 = parseInt(args[2]);
   1408       } else if (cmd == 'list') {
   1409         request.arguments.id1 = 0;
   1410         request.arguments.id2 = parseInt(args[1]);
   1411       } else {
   1412         request.arguments.id = extractObjId(args[1]);
   1413       }
   1414       break;
   1415     }
   1416 
   1417     // Command: getid
   1418     case 'getid': {
   1419       request = this.createRequest('lol-getid');
   1420       request.arguments = {};
   1421       request.arguments.address = args[1];
   1422       break;
   1423     }
   1424 
   1425     // Command: inf[o] [<N>]
   1426     case 'info':
   1427     case 'inf': {
   1428       if (args.length > 2) {
   1429         throw new Error('Too many arguments after ' + cmd + '.');
   1430       }
   1431       // Built the info request:
   1432       request = this.createLOLRequest('lol-info', 0, args[1], is_repeating);
   1433       break;
   1434     }
   1435 
   1436     // Command: path <obj id 1> <obj id 2>
   1437     case 'path': {
   1438       request = this.createRequest('lol-path');
   1439       request.arguments = {};
   1440       if (args.length > 2) {
   1441         request.arguments.id1 = extractObjId(args[1]);
   1442         request.arguments.id2 = extractObjId(args[2]);
   1443       } else {
   1444         request.arguments.id1 = 0;
   1445         request.arguments.id2 = extractObjId(args[1]);
   1446       }
   1447       break;
   1448     }
   1449 
   1450     // Command: print
   1451     case 'print': {
   1452       request = this.createRequest('lol-print');
   1453       request.arguments = {};
   1454       request.arguments.id = extractObjId(args[1]);
   1455       break;
   1456     }
   1457 
   1458     // Command: reset
   1459     case 'reset': {
   1460       request = this.createRequest('lol-reset');
   1461       break;
   1462     }
   1463 
   1464     default:
   1465       throw new Error('Invalid arguments.');
   1466   }
   1467   return request.toJSONProtocol();
   1468 };
   1469 
   1470 
   1471 // Create a JSON request for the threads command.
   1472 DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
   1473   // Build a threads request from the text command.
   1474   var request = this.createRequest('threads');
   1475   return request.toJSONProtocol();
   1476 };
   1477 
   1478 
   1479 // Handle the trace command.
   1480 DebugRequest.prototype.traceCommand_ = function(args) {
   1481   // Process arguments.
   1482   if (args && args.length > 0) {
   1483     if (args == 'compile') {
   1484       trace_compile = !trace_compile;
   1485       print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off'));
   1486     } else if (args === 'debug json' || args === 'json' || args === 'packets') {
   1487       trace_debug_json = !trace_debug_json;
   1488       print('Tracing of debug json packets ' +
   1489             (trace_debug_json ? 'on' : 'off'));
   1490     } else {
   1491       throw new Error('Invalid trace arguments.');
   1492     }
   1493   } else {
   1494     throw new Error('Invalid trace arguments.');
   1495   }
   1496 }
   1497 
   1498 // Handle the help command.
   1499 DebugRequest.prototype.helpCommand_ = function(args) {
   1500   // Help os quite simple.
   1501   if (args && args.length > 0) {
   1502     print('warning: arguments to \'help\' are ignored');
   1503   }
   1504 
   1505   print('Note: <> denotes symbollic values to be replaced with real values.');
   1506   print('Note: [] denotes optional parts of commands, or optional options / arguments.');
   1507   print('      e.g. d[elete] - you get the same command if you type d or delete.');
   1508   print('');
   1509   print('[break] - break as soon as possible');
   1510   print('b[reak] location [condition]');
   1511   print('        - break on named function: location is a function name');
   1512   print('        - break on function: location is #<id>#');
   1513   print('        - break on script position: location is name:line[:column]');
   1514   print('');
   1515   print('clear <breakpoint #>       - deletes the specified user defined breakpoint');
   1516   print('d[elete]  <breakpoint #>   - deletes the specified user defined breakpoint');
   1517   print('dis[able] <breakpoint #>   - disables the specified user defined breakpoint');
   1518   print('dis[able] exc[eptions] [[all] | unc[aught]]');
   1519   print('                           - disables breaking on exceptions');
   1520   print('en[able]  <breakpoint #>   - enables the specified user defined breakpoint');
   1521   print('en[able]  exc[eptions] [[all] | unc[aught]]');
   1522   print('                           - enables breaking on exceptions');
   1523   print('');
   1524   print('b[ack]t[race] [n] | [-n] | [from to]');
   1525   print('                           - prints the stack back trace');
   1526   print('f[rame]                    - prints info about the current frame context');
   1527   print('f[rame] <frame #>          - set context to specified frame #');
   1528   print('scopes');
   1529   print('scope <scope #>');
   1530   print('');
   1531   print('up                         - set context to caller of current frame');
   1532   print('do[wn]                     - set context to callee of current frame');
   1533   print('inf[o] br[eak]             - prints info about breakpoints in use');
   1534   print('inf[o] ar[gs]              - prints info about arguments of the current function');
   1535   print('inf[o] lo[cals]            - prints info about locals in the current function');
   1536   print('inf[o] liveobjectlist|lol  - same as \'lol info\'');
   1537   print('');
   1538   print('step [in | next | out| min [step count]]');
   1539   print('c[ontinue]                 - continue executing after a breakpoint');
   1540   print('s[tep]   [<N>]             - step into the next N callees (default N is 1)');
   1541   print('s[tep]i  [<N>]             - step into the next N callees (default N is 1)');
   1542   print('n[ext]   [<N>]             - step over the next N callees (default N is 1)');
   1543   print('fin[ish] [<N>]             - step out of N frames (default N is 1)');
   1544   print('');
   1545   print('p[rint] <expression>       - prints the result of the specified expression');
   1546   print('dir <expression>           - prints the object structure of the result');
   1547   print('set <var> = <expression>   - executes the specified statement');
   1548   print('');
   1549   print('l[ist]                     - list the source code around for the current pc');
   1550   print('l[ist] [- | <start>,<end>] - list the specified range of source code');
   1551   print('source [from line [num lines]]');
   1552   print('scr[ipts] [native|extensions|all]');
   1553   print('scr[ipts] [<filter text>]  - list scripts with the specified text in its description');
   1554   print('');
   1555   print('gc                         - runs the garbage collector');
   1556   print('');
   1557 
   1558   if (lol_is_enabled) {
   1559     print('liveobjectlist|lol <command> - live object list tracking.');
   1560     print('  where <command> can be:');
   1561     print('  c[apture]               - captures a LOL list.');
   1562     print('  clear|del[ete] <id>|all - clears LOL of id <id>.');
   1563     print('                            If \'all\' is unspecified instead, will clear all.');
   1564     print('  diff <id1> <id2> [<dump options>]');
   1565     print('                          - prints the diff between LOLs id1 and id2.');
   1566     print('                          - also see <dump options> below.');
   1567     print('  getid <address>         - gets the obj id for the specified address if available.');
   1568     print('                            The address must be in hex form prefixed with 0x.');
   1569     print('  inf[o] [<N>]            - lists summary info of all LOL lists.');
   1570     print('                            If N is specified, will print N items at a time.');
   1571     print('  [l[ist]] <id> [<dump options>]');
   1572     print('                          - prints the listing of objects in LOL id.');
   1573     print('                          - also see <dump options> below.');
   1574     print('  reset                   - clears all LOL lists.');
   1575     print('  ret[ainers] <id> [<dump options>]');
   1576     print('                          - prints the list of retainers of obj id.');
   1577     print('                          - also see <dump options> below.');
   1578     print('  path <id1> <id2>        - prints the retaining path from obj id1 to id2.');
   1579     print('                            If only one id is specified, will print the path from');
   1580     print('                            roots to the specified object if available.');
   1581     print('  print <id>              - prints the obj for the specified obj id if available.');
   1582     print('');
   1583     print('  <dump options> includes:');
   1584     print('     [v[erbose]]            - do verbose dump.');
   1585     print('     [<N>]                  - dump N items at a time.  Implies verbose dump.');
   1586     print('                             If unspecified, N will default to '+
   1587           kDefaultLolLinesToPrintAtATime+'.  Max N is '+
   1588           kMaxLolLinesToPrintAtATime+'.');
   1589     print('     [i[ndex] <i>]          - start dump from index i.  Implies verbose dump.');
   1590     print('     [t[ype] <type>]        - filter by type.');
   1591     print('     [sp[ace] <space name>] - filter by heap space where <space name> is one of');
   1592     print('                              { cell, code, lo, map, new, old-data, old-pointer }.');
   1593     print('');
   1594     print('     If the verbose option, or an option that implies a verbose dump');
   1595     print('     is specified, then a verbose dump will requested.  Else, a summary dump');
   1596     print('     will be requested.');
   1597     print('');
   1598   }
   1599 
   1600   print('trace compile');
   1601   // hidden command: trace debug json - toggles tracing of debug json packets
   1602   print('');
   1603   print('disconnect|exit|quit       - disconnects and quits the debugger');
   1604   print('help                       - prints this help information');
   1605 }
   1606 
   1607 
   1608 function formatHandleReference_(value) {
   1609   if (value.handle() >= 0) {
   1610     return '#' + value.handle() + '#';
   1611   } else {
   1612     return '#Transient#';
   1613   }
   1614 }
   1615 
   1616 
   1617 function formatObject_(value, include_properties) {
   1618   var result = '';
   1619   result += formatHandleReference_(value);
   1620   result += ', type: object'
   1621   result += ', constructor ';
   1622   var ctor = value.constructorFunctionValue();
   1623   result += formatHandleReference_(ctor);
   1624   result += ', __proto__ ';
   1625   var proto = value.protoObjectValue();
   1626   result += formatHandleReference_(proto);
   1627   result += ', ';
   1628   result += value.propertyCount();
   1629   result +=  ' properties.';
   1630   if (include_properties) {
   1631     result +=  '\n';
   1632     for (var i = 0; i < value.propertyCount(); i++) {
   1633       result += '  ';
   1634       result += value.propertyName(i);
   1635       result += ': ';
   1636       var property_value = value.propertyValue(i);
   1637       if (property_value instanceof ProtocolReference) {
   1638         result += '<no type>';
   1639       } else {
   1640         if (property_value && property_value.type()) {
   1641           result += property_value.type();
   1642         } else {
   1643           result += '<no type>';
   1644         }
   1645       }
   1646       result += ' ';
   1647       result += formatHandleReference_(property_value);
   1648       result += '\n';
   1649     }
   1650   }
   1651   return result;
   1652 }
   1653 
   1654 
   1655 function formatScope_(scope) {
   1656   var result = '';
   1657   var index = scope.index;
   1658   result += '#' + (index <= 9 ? '0' : '') + index;
   1659   result += ' ';
   1660   switch (scope.type) {
   1661     case Debug.ScopeType.Global:
   1662       result += 'Global, ';
   1663       result += '#' + scope.object.ref + '#';
   1664       break;
   1665     case Debug.ScopeType.Local:
   1666       result += 'Local';
   1667       break;
   1668     case Debug.ScopeType.With:
   1669       result += 'With, ';
   1670       result += '#' + scope.object.ref + '#';
   1671       break;
   1672     case Debug.ScopeType.Catch:
   1673       result += 'Catch, ';
   1674       result += '#' + scope.object.ref + '#';
   1675       break;
   1676     case Debug.ScopeType.Closure:
   1677       result += 'Closure';
   1678       break;
   1679     default:
   1680       result += 'UNKNOWN';
   1681   }
   1682   return result;
   1683 }
   1684 
   1685 
   1686 function refObjectToString_(protocolPackage, handle) {
   1687   var value = protocolPackage.lookup(handle);
   1688   var result = '';
   1689   if (value.isString()) {
   1690     result = '"' + value.value() + '"';
   1691   } else if (value.isPrimitive()) {
   1692     result = value.valueString();
   1693   } else if (value.isObject()) {
   1694     result += formatObject_(value, true);
   1695   }
   1696   return result;
   1697 }
   1698 
   1699 
   1700 function decodeLolCaptureResponse(body) {
   1701   var result;
   1702   result = 'Captured live object list '+ body.id +
   1703            ': count '+ body.count + ' size ' + body.size;
   1704   return result;
   1705 }
   1706 
   1707 
   1708 function decodeLolDeleteResponse(body) {
   1709   var result;
   1710   result = 'Deleted live object list '+ body.id;
   1711   return result;
   1712 }
   1713 
   1714 
   1715 function digitsIn(value) {
   1716   var digits = 0;
   1717   if (value === 0) value = 1;
   1718   while (value >= 1) {
   1719     digits++;
   1720     value /= 10;
   1721   }
   1722   return digits;
   1723 }
   1724 
   1725 
   1726 function padding(value, max_digits) {
   1727   var padding_digits = max_digits - digitsIn(value);
   1728   var padding = '';
   1729   while (padding_digits > 0) {
   1730     padding += ' ';
   1731     padding_digits--;
   1732   }
   1733   return padding;
   1734 }
   1735 
   1736 
   1737 function decodeLolInfoResponse(body) {
   1738   var result;
   1739   var lists = body.lists;
   1740   var length = lists.length;
   1741   var first_index = body.first_index + 1;
   1742   var has_more = ((first_index + length) <= body.count);
   1743   result = 'captured live object lists';
   1744   if (has_more || (first_index != 1)) {
   1745     result += ' ['+ length +' of '+ body.count +
   1746               ': starting from '+ first_index +']';
   1747   }
   1748   result += ':\n';
   1749   var max_digits = digitsIn(body.count);
   1750   var last_count = 0;
   1751   var last_size = 0;
   1752   for (var i = 0; i < length; i++) {
   1753     var entry = lists[i];
   1754     var count = entry.count;
   1755     var size = entry.size;
   1756     var index = first_index + i;
   1757     result += '  [' + padding(index, max_digits) + index + '] id '+ entry.id +
   1758               ': count '+ count;
   1759     if (last_count > 0) {
   1760       result += '(+' + (count - last_count) + ')';
   1761     }
   1762     result += ' size '+ size;
   1763     if (last_size > 0) {
   1764       result += '(+' + (size - last_size) + ')';
   1765     }
   1766     result += '\n';
   1767     last_count = count;
   1768     last_size = size;
   1769   }
   1770   result += '  total: '+length+' lists\n';
   1771   if (has_more) {
   1772     result += '  -- press <enter> for more --\n';
   1773   } else {
   1774     repeat_cmd_line = '';
   1775   }
   1776   if (length === 0) result += '  none\n';
   1777 
   1778   return result;
   1779 }
   1780 
   1781 
   1782 function decodeLolListResponse(body, title) {
   1783   
   1784   var result;
   1785   var total_count = body.count;
   1786   var total_size = body.size;
   1787   var length;
   1788   var max_digits;
   1789   var i;
   1790   var entry;
   1791   var index;
   1792 
   1793   var max_count_digits = digitsIn(total_count);
   1794   var max_size_digits;
   1795 
   1796   var summary = body.summary;
   1797   if (summary) {
   1798 
   1799     var roots_count = 0;
   1800     var found_root = body.found_root || 0;
   1801     var found_weak_root = body.found_weak_root || 0;
   1802 
   1803     // Print the summary result:
   1804     result = 'summary of objects:\n';
   1805     length = summary.length;
   1806     if (found_root !== 0) {
   1807       roots_count++;
   1808     }
   1809     if (found_weak_root !== 0) {
   1810       roots_count++;
   1811     }
   1812     max_digits = digitsIn(length + roots_count);
   1813     max_size_digits = digitsIn(total_size);
   1814 
   1815     index = 1;
   1816     if (found_root !== 0) {
   1817       result += '  [' + padding(index, max_digits) + index + '] ' +
   1818                 ' count '+ 1 + padding(0, max_count_digits) +
   1819                 '      '+ padding(0, max_size_digits+1) +
   1820                 ' : <root>\n';
   1821       index++;
   1822     }
   1823     if (found_weak_root !== 0) {
   1824       result += '  [' + padding(index, max_digits) + index + '] ' +
   1825                 ' count '+ 1 + padding(0, max_count_digits) +
   1826                 '      '+ padding(0, max_size_digits+1) +
   1827                 ' : <weak root>\n';
   1828       index++;
   1829     }
   1830 
   1831     for (i = 0; i < length; i++) {
   1832       entry = summary[i];
   1833       var count = entry.count;
   1834       var size = entry.size;
   1835       result += '  [' + padding(index, max_digits) + index + '] ' +
   1836                 ' count '+ count + padding(count, max_count_digits) +
   1837                 ' size '+ size + padding(size, max_size_digits) +
   1838                 ' : <' + entry.desc + '>\n';
   1839       index++;
   1840     }
   1841     result += '\n  total count: '+(total_count+roots_count)+'\n';
   1842     if (body.size) {
   1843       result += '  total size:  '+body.size+'\n';
   1844     }
   1845 
   1846   } else {
   1847     // Print the full dump result:
   1848     var first_index = body.first_index + 1;
   1849     var elements = body.elements;
   1850     length = elements.length;
   1851     var has_more = ((first_index + length) <= total_count);
   1852     result = title;
   1853     if (has_more || (first_index != 1)) {
   1854       result += ' ['+ length +' of '+ total_count +
   1855                 ': starting from '+ first_index +']';
   1856     }
   1857     result += ':\n';
   1858     if (length === 0) result += '  none\n';
   1859     max_digits = digitsIn(length);
   1860 
   1861     var max_id = 0;
   1862     var max_size = 0;
   1863     for (i = 0; i < length; i++) {
   1864       entry = elements[i];
   1865       if (entry.id > max_id) max_id = entry.id;
   1866       if (entry.size > max_size) max_size = entry.size;
   1867     }
   1868     var max_id_digits = digitsIn(max_id);
   1869     max_size_digits = digitsIn(max_size);
   1870 
   1871     for (i = 0; i < length; i++) {
   1872       entry = elements[i];
   1873       index = first_index + i;
   1874       result += '  ['+ padding(index, max_digits) + index +']';
   1875       if (entry.id !== 0) {
   1876         result += ' @' + entry.id + padding(entry.id, max_id_digits) +
   1877                   ': size ' + entry.size + ', ' +
   1878                   padding(entry.size, max_size_digits) +  entry.desc + '\n';
   1879       } else {
   1880         // Must be a root or weak root:
   1881         result += ' ' + entry.desc + '\n';
   1882       }
   1883     }
   1884     if (has_more) {
   1885       result += '  -- press <enter> for more --\n';
   1886     } else {
   1887       repeat_cmd_line = '';
   1888     }
   1889     if (length === 0) result += '  none\n';
   1890   }
   1891 
   1892   return result;
   1893 }
   1894 
   1895 
   1896 function decodeLolDiffResponse(body) {
   1897   var title = 'objects';
   1898   return decodeLolListResponse(body, title);
   1899 }
   1900 
   1901 
   1902 function decodeLolRetainersResponse(body) {
   1903   var title = 'retainers for @' + body.id;
   1904   return decodeLolListResponse(body, title);
   1905 }
   1906 
   1907 
   1908 function decodeLolPathResponse(body) {
   1909   return body.path;
   1910 }
   1911 
   1912 
   1913 function decodeLolResetResponse(body) {
   1914   return 'Reset all live object lists.';
   1915 }
   1916 
   1917 
   1918 function decodeLolGetIdResponse(body) {
   1919   if (body.id == 0) {
   1920     return 'Address is invalid, or object has been moved or collected';
   1921   }
   1922   return 'obj id is @' + body.id;
   1923 }
   1924 
   1925 
   1926 function decodeLolPrintResponse(body) {
   1927   return body.dump;
   1928 }
   1929 
   1930 
   1931 // Rounds number 'num' to 'length' decimal places.
   1932 function roundNumber(num, length) {
   1933   var factor = Math.pow(10, length);
   1934   return Math.round(num * factor) / factor;
   1935 }
   1936 
   1937 
   1938 // Convert a JSON response to text for display in a text based debugger.
   1939 function DebugResponseDetails(response) {
   1940   details = {text:'', running:false}
   1941 
   1942   try {
   1943     if (!response.success()) {
   1944       details.text = response.message();
   1945       return details;
   1946     }
   1947 
   1948     // Get the running state.
   1949     details.running = response.running();
   1950 
   1951     var body = response.body();
   1952     var result = '';
   1953     switch (response.command()) {
   1954       case 'suspend':
   1955         details.text = 'stopped';
   1956         break;
   1957 
   1958       case 'setbreakpoint':
   1959         result = 'set breakpoint #';
   1960         result += body.breakpoint;
   1961         details.text = result;
   1962         break;
   1963 
   1964       case 'clearbreakpoint':
   1965         result = 'cleared breakpoint #';
   1966         result += body.breakpoint;
   1967         details.text = result;
   1968         break;
   1969 
   1970       case 'changebreakpoint':
   1971         result = 'successfully changed breakpoint';
   1972         details.text = result;
   1973         break;
   1974 
   1975       case 'listbreakpoints':
   1976         result = 'breakpoints: (' + body.breakpoints.length + ')';
   1977         for (var i = 0; i < body.breakpoints.length; i++) {
   1978           var breakpoint = body.breakpoints[i];
   1979           result += '\n id=' + breakpoint.number;
   1980           result += ' type=' + breakpoint.type;
   1981           if (breakpoint.script_id) {
   1982               result += ' script_id=' + breakpoint.script_id;
   1983           }
   1984           if (breakpoint.script_name) {
   1985               result += ' script_name=' + breakpoint.script_name;
   1986           }
   1987           result += ' line=' + (breakpoint.line + 1);
   1988           if (breakpoint.column != null) {
   1989             result += ' column=' + (breakpoint.column + 1);
   1990           }
   1991           if (breakpoint.groupId) {
   1992             result += ' groupId=' + breakpoint.groupId;
   1993           }
   1994           if (breakpoint.ignoreCount) {
   1995               result += ' ignoreCount=' + breakpoint.ignoreCount;
   1996           }
   1997           if (breakpoint.active === false) {
   1998             result += ' inactive';
   1999           }
   2000           if (breakpoint.condition) {
   2001             result += ' condition=' + breakpoint.condition;
   2002           }
   2003           result += ' hit_count=' + breakpoint.hit_count;
   2004         }
   2005         if (body.breakpoints.length === 0) {
   2006           result = "No user defined breakpoints\n";
   2007         } else {
   2008           result += '\n';
   2009         }
   2010         if (body.breakOnExceptions) {
   2011           result += '* breaking on ALL exceptions is enabled\n';
   2012         } else if (body.breakOnUncaughtExceptions) {
   2013           result += '* breaking on UNCAUGHT exceptions is enabled\n';
   2014         } else {
   2015           result += '* all exception breakpoints are disabled\n';            
   2016         }
   2017         details.text = result;
   2018         break;
   2019 
   2020       case 'setexceptionbreak':
   2021         result = 'Break on ' + body.type + ' exceptions: ';
   2022         result += body.enabled ? 'enabled' : 'disabled';
   2023         details.text = result;
   2024         break;
   2025 
   2026       case 'backtrace':
   2027         if (body.totalFrames == 0) {
   2028           result = '(empty stack)';
   2029         } else {
   2030           var result = 'Frames #' + body.fromFrame + ' to #' +
   2031               (body.toFrame - 1) + ' of ' + body.totalFrames + '\n';
   2032           for (i = 0; i < body.frames.length; i++) {
   2033             if (i != 0) result += '\n';
   2034             result += body.frames[i].text;
   2035           }
   2036         }
   2037         details.text = result;
   2038         break;
   2039 
   2040       case 'frame':
   2041         if (last_cmd === 'info locals') {
   2042           var locals = body.locals;
   2043           if (locals.length === 0) {
   2044             result = 'No locals';
   2045           } else {
   2046             for (var i = 0; i < locals.length; i++) {
   2047               var local = locals[i];
   2048               result += local.name + ' = ';
   2049               result += refObjectToString_(response, local.value.ref);
   2050               result += '\n';
   2051             }
   2052           }
   2053         } else if (last_cmd === 'info args') {
   2054           var args = body.arguments;
   2055           if (args.length === 0) {
   2056             result = 'No arguments';
   2057           } else {
   2058             for (var i = 0; i < args.length; i++) {
   2059               var arg = args[i];
   2060               result += arg.name + ' = ';
   2061               result += refObjectToString_(response, arg.value.ref);
   2062               result += '\n';
   2063             }
   2064           }
   2065         } else {
   2066           result = SourceUnderline(body.sourceLineText,
   2067                                    body.column);
   2068           Debug.State.currentSourceLine = body.line;
   2069           Debug.State.currentFrame = body.index;
   2070           Debug.State.displaySourceStartLine = -1;
   2071           Debug.State.displaySourceEndLine = -1;
   2072         }
   2073         details.text = result;
   2074         break;
   2075 
   2076       case 'scopes':
   2077         if (body.totalScopes == 0) {
   2078           result = '(no scopes)';
   2079         } else {
   2080           result = 'Scopes #' + body.fromScope + ' to #' +
   2081                    (body.toScope - 1) + ' of ' + body.totalScopes + '\n';
   2082           for (i = 0; i < body.scopes.length; i++) {
   2083             if (i != 0) {
   2084               result += '\n';
   2085             }
   2086             result += formatScope_(body.scopes[i]);
   2087           }
   2088         }
   2089         details.text = result;
   2090         break;
   2091 
   2092       case 'scope':
   2093         result += formatScope_(body);
   2094         result += '\n';
   2095         var scope_object_value = response.lookup(body.object.ref);
   2096         result += formatObject_(scope_object_value, true);
   2097         details.text = result;
   2098         break;
   2099 
   2100       case 'evaluate':
   2101       case 'lookup':
   2102       case 'getobj':
   2103         if (last_cmd == 'p' || last_cmd == 'print') {
   2104           result = body.text;
   2105         } else {
   2106           var value;
   2107           if (lookup_handle) {
   2108             value = response.bodyValue(lookup_handle);
   2109           } else {
   2110             value = response.bodyValue();
   2111           }
   2112           if (value.isObject()) {
   2113             result += formatObject_(value, true);
   2114           } else {
   2115             result += 'type: ';
   2116             result += value.type();
   2117             if (!value.isUndefined() && !value.isNull()) {
   2118               result += ', ';
   2119               if (value.isString()) {
   2120                 result += '"';
   2121               }
   2122               result += value.value();
   2123               if (value.isString()) {
   2124                 result += '"';
   2125               }
   2126             }
   2127             result += '\n';
   2128           }
   2129         }
   2130         details.text = result;
   2131         break;
   2132 
   2133       case 'references':
   2134         var count = body.length;
   2135         result += 'found ' + count + ' objects';
   2136         result += '\n';
   2137         for (var i = 0; i < count; i++) {
   2138           var value = response.bodyValue(i);
   2139           result += formatObject_(value, false);
   2140           result += '\n';
   2141         }
   2142         details.text = result;
   2143         break;
   2144 
   2145       case 'source':
   2146         // Get the source from the response.
   2147         var source = body.source;
   2148         var from_line = body.fromLine + 1;
   2149         var lines = source.split('\n');
   2150         var maxdigits = 1 + Math.floor(log10(from_line + lines.length));
   2151         if (maxdigits < 3) {
   2152           maxdigits = 3;
   2153         }
   2154         var result = '';
   2155         for (var num = 0; num < lines.length; num++) {
   2156           // Check if there's an extra newline at the end.
   2157           if (num == (lines.length - 1) && lines[num].length == 0) {
   2158             break;
   2159           }
   2160 
   2161           var current_line = from_line + num;
   2162           spacer = maxdigits - (1 + Math.floor(log10(current_line)));
   2163           if (current_line == Debug.State.currentSourceLine + 1) {
   2164             for (var i = 0; i < maxdigits; i++) {
   2165               result += '>';
   2166             }
   2167             result += '  ';
   2168           } else {
   2169             for (var i = 0; i < spacer; i++) {
   2170               result += ' ';
   2171             }
   2172             result += current_line + ': ';
   2173           }
   2174           result += lines[num];
   2175           result += '\n';
   2176         }
   2177         details.text = result;
   2178         break;
   2179 
   2180       case 'scripts':
   2181         var result = '';
   2182         for (i = 0; i < body.length; i++) {
   2183           if (i != 0) result += '\n';
   2184           if (body[i].id) {
   2185             result += body[i].id;
   2186           } else {
   2187             result += '[no id]';
   2188           }
   2189           result += ', ';
   2190           if (body[i].name) {
   2191             result += body[i].name;
   2192           } else {
   2193             if (body[i].compilationType == Debug.ScriptCompilationType.Eval
   2194                 && body[i].evalFromScript
   2195                 ) {
   2196               result += 'eval from ';
   2197               var script_value = response.lookup(body[i].evalFromScript.ref);
   2198               result += ' ' + script_value.field('name');
   2199               result += ':' + (body[i].evalFromLocation.line + 1);
   2200               result += ':' + body[i].evalFromLocation.column;
   2201             } else if (body[i].compilationType ==
   2202                        Debug.ScriptCompilationType.JSON) {
   2203               result += 'JSON ';
   2204             } else {  // body[i].compilation == Debug.ScriptCompilationType.Host
   2205               result += '[unnamed] ';
   2206             }
   2207           }
   2208           result += ' (lines: ';
   2209           result += body[i].lineCount;
   2210           result += ', length: ';
   2211           result += body[i].sourceLength;
   2212           if (body[i].type == Debug.ScriptType.Native) {
   2213             result += ', native';
   2214           } else if (body[i].type == Debug.ScriptType.Extension) {
   2215             result += ', extension';
   2216           }
   2217           result += '), [';
   2218           var sourceStart = body[i].sourceStart;
   2219           if (sourceStart.length > 40) {
   2220             sourceStart = sourceStart.substring(0, 37) + '...';
   2221           }
   2222           result += sourceStart;
   2223           result += ']';
   2224         }
   2225         if (body.length == 0) {
   2226           result = "no matching scripts found";
   2227         }
   2228         details.text = result;
   2229         break;
   2230 
   2231       case 'threads':
   2232         var result = 'Active V8 threads: ' + body.totalThreads + '\n';
   2233         body.threads.sort(function(a, b) { return a.id - b.id; });
   2234         for (i = 0; i < body.threads.length; i++) {
   2235           result += body.threads[i].current ? '*' : ' ';
   2236           result += ' ';
   2237           result += body.threads[i].id;
   2238           result += '\n';
   2239         }
   2240         details.text = result;
   2241         break;
   2242 
   2243       case 'continue':
   2244         details.text = "(running)";
   2245         break;
   2246 
   2247       case 'v8flags':
   2248         details.text = "flags set";
   2249         break;
   2250 
   2251       case 'gc':
   2252         details.text = "GC " + body.before + " => " + body.after;
   2253         if (body.after > (1024*1024)) {
   2254           details.text +=
   2255               " (" + roundNumber(body.before/(1024*1024), 1) + "M => " +
   2256                      roundNumber(body.after/(1024*1024), 1) + "M)";
   2257         } else if (body.after > 1024) {
   2258           details.text +=
   2259               " (" + roundNumber(body.before/1024, 1) + "K => " +
   2260                      roundNumber(body.after/1024, 1) + "K)";
   2261         }
   2262         break;
   2263 
   2264       case 'lol-capture':
   2265         details.text = decodeLolCaptureResponse(body);
   2266         break;
   2267       case 'lol-delete':
   2268         details.text = decodeLolDeleteResponse(body);
   2269         break;
   2270       case 'lol-diff':
   2271         details.text = decodeLolDiffResponse(body);
   2272         break;
   2273       case 'lol-getid':
   2274         details.text = decodeLolGetIdResponse(body);
   2275         break;
   2276       case 'lol-info':
   2277         details.text = decodeLolInfoResponse(body);
   2278         break;
   2279       case 'lol-print':
   2280         details.text = decodeLolPrintResponse(body);
   2281         break;
   2282       case 'lol-reset':
   2283         details.text = decodeLolResetResponse(body);
   2284         break;
   2285       case 'lol-retainers':
   2286         details.text = decodeLolRetainersResponse(body);
   2287         break;
   2288       case 'lol-path':
   2289         details.text = decodeLolPathResponse(body);
   2290         break;
   2291 
   2292       default:
   2293         details.text =
   2294             'Response for unknown command \'' + response.command() + '\'' +
   2295             ' (' + response.raw_json() + ')';
   2296     }
   2297   } catch (e) {
   2298     details.text = 'Error: "' + e + '" formatting response';
   2299   }
   2300 
   2301   return details;
   2302 };
   2303 
   2304 
   2305 /**
   2306  * Protocol packages send from the debugger.
   2307  * @param {string} json - raw protocol packet as JSON string.
   2308  * @constructor
   2309  */
   2310 function ProtocolPackage(json) {
   2311   this.raw_json_ = json;
   2312   this.packet_ = JSON.parse(json);
   2313   this.refs_ = [];
   2314   if (this.packet_.refs) {
   2315     for (var i = 0; i < this.packet_.refs.length; i++) {
   2316       this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
   2317     }
   2318   }
   2319 }
   2320 
   2321 
   2322 /**
   2323  * Get the packet type.
   2324  * @return {String} the packet type
   2325  */
   2326 ProtocolPackage.prototype.type = function() {
   2327   return this.packet_.type;
   2328 }
   2329 
   2330 
   2331 /**
   2332  * Get the packet event.
   2333  * @return {Object} the packet event
   2334  */
   2335 ProtocolPackage.prototype.event = function() {
   2336   return this.packet_.event;
   2337 }
   2338 
   2339 
   2340 /**
   2341  * Get the packet request sequence.
   2342  * @return {number} the packet request sequence
   2343  */
   2344 ProtocolPackage.prototype.requestSeq = function() {
   2345   return this.packet_.request_seq;
   2346 }
   2347 
   2348 
   2349 /**
   2350  * Get the packet request sequence.
   2351  * @return {number} the packet request sequence
   2352  */
   2353 ProtocolPackage.prototype.running = function() {
   2354   return this.packet_.running ? true : false;
   2355 }
   2356 
   2357 
   2358 ProtocolPackage.prototype.success = function() {
   2359   return this.packet_.success ? true : false;
   2360 }
   2361 
   2362 
   2363 ProtocolPackage.prototype.message = function() {
   2364   return this.packet_.message;
   2365 }
   2366 
   2367 
   2368 ProtocolPackage.prototype.command = function() {
   2369   return this.packet_.command;
   2370 }
   2371 
   2372 
   2373 ProtocolPackage.prototype.body = function() {
   2374   return this.packet_.body;
   2375 }
   2376 
   2377 
   2378 ProtocolPackage.prototype.bodyValue = function(index) {
   2379   if (index != null) {
   2380     return new ProtocolValue(this.packet_.body[index], this);
   2381   } else {
   2382     return new ProtocolValue(this.packet_.body, this);
   2383   }
   2384 }
   2385 
   2386 
   2387 ProtocolPackage.prototype.body = function() {
   2388   return this.packet_.body;
   2389 }
   2390 
   2391 
   2392 ProtocolPackage.prototype.lookup = function(handle) {
   2393   var value = this.refs_[handle];
   2394   if (value) {
   2395     return new ProtocolValue(value, this);
   2396   } else {
   2397     return new ProtocolReference(handle);
   2398   }
   2399 }
   2400 
   2401 
   2402 ProtocolPackage.prototype.raw_json = function() {
   2403   return this.raw_json_;
   2404 }
   2405 
   2406 
   2407 function ProtocolValue(value, packet) {
   2408   this.value_ = value;
   2409   this.packet_ = packet;
   2410 }
   2411 
   2412 
   2413 /**
   2414  * Get the value type.
   2415  * @return {String} the value type
   2416  */
   2417 ProtocolValue.prototype.type = function() {
   2418   return this.value_.type;
   2419 }
   2420 
   2421 
   2422 /**
   2423  * Get a metadata field from a protocol value.
   2424  * @return {Object} the metadata field value
   2425  */
   2426 ProtocolValue.prototype.field = function(name) {
   2427   return this.value_[name];
   2428 }
   2429 
   2430 
   2431 /**
   2432  * Check is the value is a primitive value.
   2433  * @return {boolean} true if the value is primitive
   2434  */
   2435 ProtocolValue.prototype.isPrimitive = function() {
   2436   return this.isUndefined() || this.isNull() || this.isBoolean() ||
   2437          this.isNumber() || this.isString();
   2438 }
   2439 
   2440 
   2441 /**
   2442  * Get the object handle.
   2443  * @return {number} the value handle
   2444  */
   2445 ProtocolValue.prototype.handle = function() {
   2446   return this.value_.handle;
   2447 }
   2448 
   2449 
   2450 /**
   2451  * Check is the value is undefined.
   2452  * @return {boolean} true if the value is undefined
   2453  */
   2454 ProtocolValue.prototype.isUndefined = function() {
   2455   return this.value_.type == 'undefined';
   2456 }
   2457 
   2458 
   2459 /**
   2460  * Check is the value is null.
   2461  * @return {boolean} true if the value is null
   2462  */
   2463 ProtocolValue.prototype.isNull = function() {
   2464   return this.value_.type == 'null';
   2465 }
   2466 
   2467 
   2468 /**
   2469  * Check is the value is a boolean.
   2470  * @return {boolean} true if the value is a boolean
   2471  */
   2472 ProtocolValue.prototype.isBoolean = function() {
   2473   return this.value_.type == 'boolean';
   2474 }
   2475 
   2476 
   2477 /**
   2478  * Check is the value is a number.
   2479  * @return {boolean} true if the value is a number
   2480  */
   2481 ProtocolValue.prototype.isNumber = function() {
   2482   return this.value_.type == 'number';
   2483 }
   2484 
   2485 
   2486 /**
   2487  * Check is the value is a string.
   2488  * @return {boolean} true if the value is a string
   2489  */
   2490 ProtocolValue.prototype.isString = function() {
   2491   return this.value_.type == 'string';
   2492 }
   2493 
   2494 
   2495 /**
   2496  * Check is the value is an object.
   2497  * @return {boolean} true if the value is an object
   2498  */
   2499 ProtocolValue.prototype.isObject = function() {
   2500   return this.value_.type == 'object' || this.value_.type == 'function' ||
   2501          this.value_.type == 'error' || this.value_.type == 'regexp';
   2502 }
   2503 
   2504 
   2505 /**
   2506  * Get the constructor function
   2507  * @return {ProtocolValue} constructor function
   2508  */
   2509 ProtocolValue.prototype.constructorFunctionValue = function() {
   2510   var ctor = this.value_.constructorFunction;
   2511   return this.packet_.lookup(ctor.ref);
   2512 }
   2513 
   2514 
   2515 /**
   2516  * Get the __proto__ value
   2517  * @return {ProtocolValue} __proto__ value
   2518  */
   2519 ProtocolValue.prototype.protoObjectValue = function() {
   2520   var proto = this.value_.protoObject;
   2521   return this.packet_.lookup(proto.ref);
   2522 }
   2523 
   2524 
   2525 /**
   2526  * Get the number og properties.
   2527  * @return {number} the number of properties
   2528  */
   2529 ProtocolValue.prototype.propertyCount = function() {
   2530   return this.value_.properties ? this.value_.properties.length : 0;
   2531 }
   2532 
   2533 
   2534 /**
   2535  * Get the specified property name.
   2536  * @return {string} property name
   2537  */
   2538 ProtocolValue.prototype.propertyName = function(index) {
   2539   var property = this.value_.properties[index];
   2540   return property.name;
   2541 }
   2542 
   2543 
   2544 /**
   2545  * Return index for the property name.
   2546  * @param name The property name to look for
   2547  * @return {number} index for the property name
   2548  */
   2549 ProtocolValue.prototype.propertyIndex = function(name) {
   2550   for (var i = 0; i < this.propertyCount(); i++) {
   2551     if (this.value_.properties[i].name == name) {
   2552       return i;
   2553     }
   2554   }
   2555   return null;
   2556 }
   2557 
   2558 
   2559 /**
   2560  * Get the specified property value.
   2561  * @return {ProtocolValue} property value
   2562  */
   2563 ProtocolValue.prototype.propertyValue = function(index) {
   2564   var property = this.value_.properties[index];
   2565   return this.packet_.lookup(property.ref);
   2566 }
   2567 
   2568 
   2569 /**
   2570  * Check is the value is a string.
   2571  * @return {boolean} true if the value is a string
   2572  */
   2573 ProtocolValue.prototype.value = function() {
   2574   return this.value_.value;
   2575 }
   2576 
   2577 
   2578 ProtocolValue.prototype.valueString = function() {
   2579   return this.value_.text;
   2580 }
   2581 
   2582 
   2583 function ProtocolReference(handle) {
   2584   this.handle_ = handle;
   2585 }
   2586 
   2587 
   2588 ProtocolReference.prototype.handle = function() {
   2589   return this.handle_;
   2590 }
   2591 
   2592 
   2593 function MakeJSONPair_(name, value) {
   2594   return '"' + name + '":' + value;
   2595 }
   2596 
   2597 
   2598 function ArrayToJSONObject_(content) {
   2599   return '{' + content.join(',') + '}';
   2600 }
   2601 
   2602 
   2603 function ArrayToJSONArray_(content) {
   2604   return '[' + content.join(',') + ']';
   2605 }
   2606 
   2607 
   2608 function BooleanToJSON_(value) {
   2609   return String(value);
   2610 }
   2611 
   2612 
   2613 function NumberToJSON_(value) {
   2614   return String(value);
   2615 }
   2616 
   2617 
   2618 // Mapping of some control characters to avoid the \uXXXX syntax for most
   2619 // commonly used control cahracters.
   2620 const ctrlCharMap_ = {
   2621   '\b': '\\b',
   2622   '\t': '\\t',
   2623   '\n': '\\n',
   2624   '\f': '\\f',
   2625   '\r': '\\r',
   2626   '"' : '\\"',
   2627   '\\': '\\\\'
   2628 };
   2629 
   2630 
   2631 // Regular expression testing for ", \ and control characters (0x00 - 0x1F).
   2632 const ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]');
   2633 
   2634 
   2635 // Regular expression matching ", \ and control characters (0x00 - 0x1F)
   2636 // globally.
   2637 const ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g');
   2638 
   2639 
   2640 /**
   2641  * Convert a String to its JSON representation (see http://www.json.org/). To
   2642  * avoid depending on the String object this method calls the functions in
   2643  * string.js directly and not through the value.
   2644  * @param {String} value The String value to format as JSON
   2645  * @return {string} JSON formatted String value
   2646  */
   2647 function StringToJSON_(value) {
   2648   // Check for" , \ and control characters (0x00 - 0x1F). No need to call
   2649   // RegExpTest as ctrlchar is constructed using RegExp.
   2650   if (ctrlCharTest_.test(value)) {
   2651     // Replace ", \ and control characters (0x00 - 0x1F).
   2652     return '"' +
   2653       value.replace(ctrlCharMatch_, function (char) {
   2654         // Use charmap if possible.
   2655         var mapped = ctrlCharMap_[char];
   2656         if (mapped) return mapped;
   2657         mapped = char.charCodeAt();
   2658         // Convert control character to unicode escape sequence.
   2659         return '\\u00' +
   2660           '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) +
   2661           '0' // TODO %NumberToRadixString(mapped % 16, 16);
   2662       })
   2663     + '"';
   2664   }
   2665 
   2666   // Simple string with no special characters.
   2667   return '"' + value + '"';
   2668 }
   2669 
   2670 
   2671 /**
   2672  * Convert a Date to ISO 8601 format. To avoid depending on the Date object
   2673  * this method calls the functions in date.js directly and not through the
   2674  * value.
   2675  * @param {Date} value The Date value to format as JSON
   2676  * @return {string} JSON formatted Date value
   2677  */
   2678 function DateToISO8601_(value) {
   2679   function f(n) {
   2680     return n < 10 ? '0' + n : n;
   2681   }
   2682   function g(n) {
   2683     return n < 10 ? '00' + n : n < 100 ? '0' + n : n;
   2684   }
   2685   return builtins.GetUTCFullYearFrom(value)         + '-' +
   2686           f(builtins.GetUTCMonthFrom(value) + 1)    + '-' +
   2687           f(builtins.GetUTCDateFrom(value))         + 'T' +
   2688           f(builtins.GetUTCHoursFrom(value))        + ':' +
   2689           f(builtins.GetUTCMinutesFrom(value))      + ':' +
   2690           f(builtins.GetUTCSecondsFrom(value))      + '.' +
   2691           g(builtins.GetUTCMillisecondsFrom(value)) + 'Z';
   2692 }
   2693 
   2694 
   2695 /**
   2696  * Convert a Date to ISO 8601 format. To avoid depending on the Date object
   2697  * this method calls the functions in date.js directly and not through the
   2698  * value.
   2699  * @param {Date} value The Date value to format as JSON
   2700  * @return {string} JSON formatted Date value
   2701  */
   2702 function DateToJSON_(value) {
   2703   return '"' + DateToISO8601_(value) + '"';
   2704 }
   2705 
   2706 
   2707 /**
   2708  * Convert an Object to its JSON representation (see http://www.json.org/).
   2709  * This implementation simply runs through all string property names and adds
   2710  * each property to the JSON representation for some predefined types. For type
   2711  * "object" the function calls itself recursively unless the object has the
   2712  * function property "toJSONProtocol" in which case that is used. This is not
   2713  * a general implementation but sufficient for the debugger. Note that circular
   2714  * structures will cause infinite recursion.
   2715  * @param {Object} object The object to format as JSON
   2716  * @return {string} JSON formatted object value
   2717  */
   2718 function SimpleObjectToJSON_(object) {
   2719   var content = [];
   2720   for (var key in object) {
   2721     // Only consider string keys.
   2722     if (typeof key == 'string') {
   2723       var property_value = object[key];
   2724 
   2725       // Format the value based on its type.
   2726       var property_value_json;
   2727       switch (typeof property_value) {
   2728         case 'object':
   2729           if (property_value === null) {
   2730             property_value_json = 'null';
   2731           } else if (typeof property_value.toJSONProtocol == 'function') {
   2732             property_value_json = property_value.toJSONProtocol(true)
   2733           } else if (property_value.constructor.name == 'Array'){
   2734             property_value_json = SimpleArrayToJSON_(property_value);
   2735           } else {
   2736             property_value_json = SimpleObjectToJSON_(property_value);
   2737           }
   2738           break;
   2739 
   2740         case 'boolean':
   2741           property_value_json = BooleanToJSON_(property_value);
   2742           break;
   2743 
   2744         case 'number':
   2745           property_value_json = NumberToJSON_(property_value);
   2746           break;
   2747 
   2748         case 'string':
   2749           property_value_json = StringToJSON_(property_value);
   2750           break;
   2751 
   2752         default:
   2753           property_value_json = null;
   2754       }
   2755 
   2756       // Add the property if relevant.
   2757       if (property_value_json) {
   2758         content.push(StringToJSON_(key) + ':' + property_value_json);
   2759       }
   2760     }
   2761   }
   2762 
   2763   // Make JSON object representation.
   2764   return '{' + content.join(',') + '}';
   2765 }
   2766 
   2767 
   2768 /**
   2769  * Convert an array to its JSON representation. This is a VERY simple
   2770  * implementation just to support what is needed for the debugger.
   2771  * @param {Array} arrya The array to format as JSON
   2772  * @return {string} JSON formatted array value
   2773  */
   2774 function SimpleArrayToJSON_(array) {
   2775   // Make JSON array representation.
   2776   var json = '[';
   2777   for (var i = 0; i < array.length; i++) {
   2778     if (i != 0) {
   2779       json += ',';
   2780     }
   2781     var elem = array[i];
   2782     if (elem.toJSONProtocol) {
   2783       json += elem.toJSONProtocol(true)
   2784     } else if (typeof(elem) === 'object')  {
   2785       json += SimpleObjectToJSON_(elem);
   2786     } else if (typeof(elem) === 'boolean')  {
   2787       json += BooleanToJSON_(elem);
   2788     } else if (typeof(elem) === 'number')  {
   2789       json += NumberToJSON_(elem);
   2790     } else if (typeof(elem) === 'string')  {
   2791       json += StringToJSON_(elem);
   2792     } else {
   2793       json += elem;
   2794     }
   2795   }
   2796   json += ']';
   2797   return json;
   2798 }
   2799