Home | History | Annotate | Download | only in src
      1 // Copyright 2008 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 "use strict";
      6 
      7 String.prototype.startsWith = function (str) {
      8   if (str.length > this.length) {
      9     return false;
     10   }
     11   return this.substr(0, str.length) == str;
     12 };
     13 
     14 function log10(num) {
     15   return Math.log(num)/Math.log(10);
     16 }
     17 
     18 function ToInspectableObject(obj) {
     19   if (!obj && typeof obj === 'object') {
     20     return UNDEFINED;
     21   } else {
     22     return Object(obj);
     23   }
     24 }
     25 
     26 function GetCompletions(global, last, full) {
     27   var full_tokens = full.split();
     28   full = full_tokens.pop();
     29   var parts = full.split('.');
     30   parts.pop();
     31   var current = global;
     32   for (var i = 0; i < parts.length; i++) {
     33     var part = parts[i];
     34     var next = current[part];
     35     if (!next) {
     36       return [];
     37     }
     38     current = next;
     39   }
     40   var result = [];
     41   current = ToInspectableObject(current);
     42   while (typeof current !== 'undefined') {
     43     var mirror = new $debug.ObjectMirror(current);
     44     var properties = mirror.properties();
     45     for (var i = 0; i < properties.length; i++) {
     46       var name = properties[i].name();
     47       if (typeof name === 'string' && name.startsWith(last)) {
     48         result.push(name);
     49       }
     50     }
     51     current = ToInspectableObject(Object.getPrototypeOf(current));
     52   }
     53   return result;
     54 }
     55 
     56 
     57 // Global object holding debugger related constants and state.
     58 var Debug = {};
     59 
     60 
     61 // Debug events which can occour in the V8 JavaScript engine. These originate
     62 // from the API include file v8-debug.h.
     63 Debug.DebugEvent = { Break: 1,
     64                      Exception: 2,
     65                      NewFunction: 3,
     66                      BeforeCompile: 4,
     67                      AfterCompile: 5 };
     68 
     69 
     70 // The different types of scripts matching enum ScriptType in objects.h.
     71 Debug.ScriptType = { Native: 0,
     72                      Extension: 1,
     73                      Normal: 2 };
     74 
     75 
     76 // The different types of script compilations matching enum
     77 // Script::CompilationType in objects.h.
     78 Debug.ScriptCompilationType = { Host: 0,
     79                                 Eval: 1,
     80                                 JSON: 2 };
     81 
     82 
     83 // The different types of scopes matching constants runtime.cc.
     84 Debug.ScopeType = { Global: 0,
     85                     Local: 1,
     86                     With: 2,
     87                     Closure: 3,
     88                     Catch: 4,
     89                     Block: 5 };
     90 
     91 
     92 // Current debug state.
     93 var kNoFrame = -1;
     94 Debug.State = {
     95   currentFrame: kNoFrame,
     96   displaySourceStartLine: -1,
     97   displaySourceEndLine: -1,
     98   currentSourceLine: -1
     99 };
    100 var trace_compile = false;  // Tracing all compile events?
    101 var trace_debug_json = false; // Tracing all debug json packets?
    102 var last_cmd = '';
    103 var repeat_cmd_line = '';
    104 var is_running = true;
    105 // Global variable used to store whether a handle was requested.
    106 var lookup_handle = null;
    107 
    108 // Copied from debug-delay.js.  This is needed below:
    109 function ScriptTypeFlag(type) {
    110   return (1 << type);
    111 }
    112 
    113 
    114 // Process a debugger JSON message into a display text and a running status.
    115 // This function returns an object with properties "text" and "running" holding
    116 // this information.
    117 function DebugMessageDetails(message) {
    118   if (trace_debug_json) {
    119     print("received: '" + message + "'");
    120   }
    121   // Convert the JSON string to an object.
    122   var response = new ProtocolPackage(message);
    123   is_running = response.running();
    124 
    125   if (response.type() == 'event') {
    126     return DebugEventDetails(response);
    127   } else {
    128     return DebugResponseDetails(response);
    129   }
    130 }
    131 
    132 function DebugEventDetails(response) {
    133   var details = {text:'', running:false};
    134 
    135   // Get the running state.
    136   details.running = response.running();
    137 
    138   var body = response.body();
    139   var result = '';
    140   switch (response.event()) {
    141     case 'break':
    142       if (body.breakpoints) {
    143         result += 'breakpoint';
    144         if (body.breakpoints.length > 1) {
    145           result += 's';
    146         }
    147         result += ' #';
    148         for (var i = 0; i < body.breakpoints.length; i++) {
    149           if (i > 0) {
    150             result += ', #';
    151           }
    152           result += body.breakpoints[i];
    153         }
    154       } else {
    155         result += 'break';
    156       }
    157       result += ' in ';
    158       result += body.invocationText;
    159       result += ', ';
    160       result += SourceInfo(body);
    161       result += '\n';
    162       result += SourceUnderline(body.sourceLineText, body.sourceColumn);
    163       Debug.State.currentSourceLine = body.sourceLine;
    164       Debug.State.displaySourceStartLine = -1;
    165       Debug.State.displaySourceEndLine = -1;
    166       Debug.State.currentFrame = 0;
    167       details.text = result;
    168       break;
    169 
    170     case 'exception':
    171       if (body.uncaught) {
    172         result += 'Uncaught: ';
    173       } else {
    174         result += 'Exception: ';
    175       }
    176       result += '"';
    177       result += body.exception.text;
    178       result += '"';
    179       if (body.sourceLine >= 0) {
    180         result += ', ';
    181         result += SourceInfo(body);
    182         result += '\n';
    183         result += SourceUnderline(body.sourceLineText, body.sourceColumn);
    184         Debug.State.currentSourceLine = body.sourceLine;
    185         Debug.State.displaySourceStartLine = -1;
    186         Debug.State.displaySourceEndLine = -1;
    187         Debug.State.currentFrame = 0;
    188       } else {
    189         result += ' (empty stack)';
    190         Debug.State.currentSourceLine = -1;
    191         Debug.State.displaySourceStartLine = -1;
    192         Debug.State.displaySourceEndLine = -1;
    193         Debug.State.currentFrame = kNoFrame;
    194       }
    195       details.text = result;
    196       break;
    197 
    198     case 'afterCompile':
    199       if (trace_compile) {
    200         result = 'Source ' + body.script.name + ' compiled:\n';
    201         var source = body.script.source;
    202         if (!(source[source.length - 1] == '\n')) {
    203           result += source;
    204         } else {
    205           result += source.substring(0, source.length - 1);
    206         }
    207       }
    208       details.text = result;
    209       break;
    210 
    211     default:
    212       details.text = 'Unknown debug event ' + response.event();
    213   }
    214 
    215   return details;
    216 }
    217 
    218 
    219 function SourceInfo(body) {
    220   var result = '';
    221 
    222   if (body.script) {
    223     if (body.script.name) {
    224       result += body.script.name;
    225     } else {
    226       result += '[unnamed]';
    227     }
    228   }
    229   result += ' line ';
    230   result += body.sourceLine + 1;
    231   result += ' column ';
    232   result += body.sourceColumn + 1;
    233 
    234   return result;
    235 }
    236 
    237 
    238 function SourceUnderline(source_text, position) {
    239   if (!source_text) {
    240     return;
    241   }
    242 
    243   // Create an underline with a caret pointing to the source position. If the
    244   // source contains a tab character the underline will have a tab character in
    245   // the same place otherwise the underline will have a space character.
    246   var underline = '';
    247   for (var i = 0; i < position; i++) {
    248     if (source_text[i] == '\t') {
    249       underline += '\t';
    250     } else {
    251       underline += ' ';
    252     }
    253   }
    254   underline += '^';
    255 
    256   // Return the source line text with the underline beneath.
    257   return source_text + '\n' + underline;
    258 }
    259 
    260 
    261 // Converts a text command to a JSON request.
    262 function DebugCommandToJSONRequest(cmd_line) {
    263   var result = new DebugRequest(cmd_line).JSONRequest();
    264   if (trace_debug_json && result) {
    265     print("sending: '" + result + "'");
    266   }
    267   return result;
    268 }
    269 
    270 
    271 function DebugRequest(cmd_line) {
    272   // If the very first character is a { assume that a JSON request have been
    273   // entered as a command. Converting that to a JSON request is trivial.
    274   if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') {
    275     this.request_ = cmd_line;
    276     return;
    277   }
    278 
    279   // Check for a simple carriage return to repeat the last command:
    280   var is_repeating = false;
    281   if (cmd_line == '\n') {
    282     if (is_running) {
    283       cmd_line = 'break'; // Not in debugger mode, break with a frame request.
    284     } else {
    285       cmd_line = repeat_cmd_line; // use command to repeat.
    286       is_repeating = true;
    287     }
    288   }
    289   if (!is_running) { // Only save the command if in debugger mode.
    290     repeat_cmd_line = cmd_line;   // save last command.
    291   }
    292 
    293   // Trim string for leading and trailing whitespace.
    294   cmd_line = cmd_line.replace(/^\s+|\s+$/g, '');
    295 
    296   // Find the command.
    297   var pos = cmd_line.indexOf(' ');
    298   var cmd;
    299   var args;
    300   if (pos == -1) {
    301     cmd = cmd_line;
    302     args = '';
    303   } else {
    304     cmd = cmd_line.slice(0, pos);
    305     args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, '');
    306   }
    307 
    308   if ((cmd === undefined) || !cmd) {
    309     this.request_ = UNDEFINED;
    310     return;
    311   }
    312 
    313   last_cmd = cmd;
    314 
    315   // Switch on command.
    316   switch (cmd) {
    317     case 'continue':
    318     case 'c':
    319       this.request_ = this.continueCommandToJSONRequest_(args);
    320       break;
    321 
    322     case 'step':
    323     case 's':
    324       this.request_ = this.stepCommandToJSONRequest_(args, 'in');
    325       break;
    326 
    327     case 'stepi':
    328     case 'si':
    329       this.request_ = this.stepCommandToJSONRequest_(args, 'min');
    330       break;
    331 
    332     case 'next':
    333     case 'n':
    334       this.request_ = this.stepCommandToJSONRequest_(args, 'next');
    335       break;
    336 
    337     case 'finish':
    338     case 'fin':
    339       this.request_ = this.stepCommandToJSONRequest_(args, 'out');
    340       break;
    341 
    342     case 'backtrace':
    343     case 'bt':
    344       this.request_ = this.backtraceCommandToJSONRequest_(args);
    345       break;
    346 
    347     case 'frame':
    348     case 'f':
    349       this.request_ = this.frameCommandToJSONRequest_(args);
    350       break;
    351 
    352     case 'scopes':
    353       this.request_ = this.scopesCommandToJSONRequest_(args);
    354       break;
    355 
    356     case 'scope':
    357       this.request_ = this.scopeCommandToJSONRequest_(args);
    358       break;
    359 
    360     case 'disconnect':
    361     case 'exit':
    362     case 'quit':
    363       this.request_ = this.disconnectCommandToJSONRequest_(args);
    364       break;
    365 
    366     case 'up':
    367       this.request_ =
    368           this.frameCommandToJSONRequest_('' +
    369                                           (Debug.State.currentFrame + 1));
    370       break;
    371 
    372     case 'down':
    373     case 'do':
    374       this.request_ =
    375           this.frameCommandToJSONRequest_('' +
    376                                           (Debug.State.currentFrame - 1));
    377       break;
    378 
    379     case 'set':
    380     case 'print':
    381     case 'p':
    382       this.request_ = this.printCommandToJSONRequest_(args);
    383       break;
    384 
    385     case 'dir':
    386       this.request_ = this.dirCommandToJSONRequest_(args);
    387       break;
    388 
    389     case 'references':
    390       this.request_ = this.referencesCommandToJSONRequest_(args);
    391       break;
    392 
    393     case 'instances':
    394       this.request_ = this.instancesCommandToJSONRequest_(args);
    395       break;
    396 
    397     case 'list':
    398     case 'l':
    399       this.request_ = this.listCommandToJSONRequest_(args);
    400       break;
    401     case 'source':
    402       this.request_ = this.sourceCommandToJSONRequest_(args);
    403       break;
    404 
    405     case 'scripts':
    406     case 'script':
    407     case 'scr':
    408       this.request_ = this.scriptsCommandToJSONRequest_(args);
    409       break;
    410 
    411     case 'break':
    412     case 'b':
    413       this.request_ = this.breakCommandToJSONRequest_(args);
    414       break;
    415 
    416     case 'breakpoints':
    417     case 'bb':
    418       this.request_ = this.breakpointsCommandToJSONRequest_(args);
    419       break;
    420 
    421     case 'clear':
    422     case 'delete':
    423     case 'd':
    424       this.request_ = this.clearCommandToJSONRequest_(args);
    425       break;
    426 
    427     case 'threads':
    428       this.request_ = this.threadsCommandToJSONRequest_(args);
    429       break;
    430 
    431     case 'cond':
    432       this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond');
    433       break;
    434 
    435     case 'enable':
    436     case 'en':
    437       this.request_ =
    438           this.changeBreakpointCommandToJSONRequest_(args, 'enable');
    439       break;
    440 
    441     case 'disable':
    442     case 'dis':
    443       this.request_ =
    444           this.changeBreakpointCommandToJSONRequest_(args, 'disable');
    445       break;
    446 
    447     case 'ignore':
    448       this.request_ =
    449           this.changeBreakpointCommandToJSONRequest_(args, 'ignore');
    450       break;
    451 
    452     case 'info':
    453     case 'inf':
    454       this.request_ = this.infoCommandToJSONRequest_(args);
    455       break;
    456 
    457     case 'flags':
    458       this.request_ = this.v8FlagsToJSONRequest_(args);
    459       break;
    460 
    461     case 'gc':
    462       this.request_ = this.gcToJSONRequest_(args);
    463       break;
    464 
    465     case 'trace':
    466     case 'tr':
    467       // Return undefined to indicate command handled internally (no JSON).
    468       this.request_ = UNDEFINED;
    469       this.traceCommand_(args);
    470       break;
    471 
    472     case 'help':
    473     case '?':
    474       this.helpCommand_(args);
    475       // Return undefined to indicate command handled internally (no JSON).
    476       this.request_ = UNDEFINED;
    477       break;
    478 
    479     default:
    480       throw new Error('Unknown command "' + cmd + '"');
    481   }
    482 }
    483 
    484 DebugRequest.prototype.JSONRequest = function() {
    485   return this.request_;
    486 };
    487 
    488 
    489 function RequestPacket(command) {
    490   this.seq = 0;
    491   this.type = 'request';
    492   this.command = command;
    493 }
    494 
    495 
    496 RequestPacket.prototype.toJSONProtocol = function() {
    497   // Encode the protocol header.
    498   var json = '{';
    499   json += '"seq":' + this.seq;
    500   json += ',"type":"' + this.type + '"';
    501   if (this.command) {
    502     json += ',"command":' + JSON.stringify(this.command);
    503   }
    504   if (this.arguments) {
    505     json += ',"arguments":';
    506     // Encode the arguments part.
    507     if (this.arguments.toJSONProtocol) {
    508       json += this.arguments.toJSONProtocol();
    509     } else {
    510       json += JSON.stringify(this.arguments);
    511     }
    512   }
    513   json += '}';
    514   return json;
    515 };
    516 
    517 
    518 DebugRequest.prototype.createRequest = function(command) {
    519   return new RequestPacket(command);
    520 };
    521 
    522 
    523 // Create a JSON request for the evaluation command.
    524 DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
    525   lookup_handle = null;
    526 
    527   // Check if the expression is a handle id in the form #<handle>#.
    528   var handle_match = expression.match(/^#([0-9]*)#$/);
    529   if (handle_match) {
    530     // Remember the handle requested in a global variable.
    531     lookup_handle = parseInt(handle_match[1]);
    532     // Build a lookup request.
    533     var request = this.createRequest('lookup');
    534     request.arguments = {};
    535     request.arguments.handles = [ lookup_handle ];
    536     return request.toJSONProtocol();
    537   } else {
    538     // Build an evaluate request.
    539     var request = this.createRequest('evaluate');
    540     request.arguments = {};
    541     request.arguments.expression = expression;
    542     // Request a global evaluation if there is no current frame.
    543     if (Debug.State.currentFrame == kNoFrame) {
    544       request.arguments.global = true;
    545     }
    546     return request.toJSONProtocol();
    547   }
    548 };
    549 
    550 
    551 // Create a JSON request for the references/instances command.
    552 DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
    553   // Build a references request.
    554   var handle_match = handle.match(/^#([0-9]*)#$/);
    555   if (handle_match) {
    556     var request = this.createRequest('references');
    557     request.arguments = {};
    558     request.arguments.type = type;
    559     request.arguments.handle = parseInt(handle_match[1]);
    560     return request.toJSONProtocol();
    561   } else {
    562     throw new Error('Invalid object id.');
    563   }
    564 };
    565 
    566 
    567 // Create a JSON request for the continue command.
    568 DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
    569   var request = this.createRequest('continue');
    570   return request.toJSONProtocol();
    571 };
    572 
    573 
    574 // Create a JSON request for the step command.
    575 DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) {
    576   // Requesting a step is through the continue command with additional
    577   // arguments.
    578   var request = this.createRequest('continue');
    579   request.arguments = {};
    580 
    581   // Process arguments if any.
    582 
    583   // Only process args if the command is 'step' which is indicated by type being
    584   // set to 'in'.  For all other commands, ignore the args.
    585   if (args && args.length > 0) {
    586     args = args.split(/\s+/g);
    587 
    588     if (args.length > 2) {
    589       throw new Error('Invalid step arguments.');
    590     }
    591 
    592     if (args.length > 0) {
    593       // Check if we have a gdb stype step command.  If so, the 1st arg would
    594       // be the step count.  If it's not a number, then assume that we're
    595       // parsing for the legacy v8 step command.
    596       var stepcount = Number(args[0]);
    597       if (stepcount == Number.NaN) {
    598         // No step count at arg 1.  Process as legacy d8 step command:
    599         if (args.length == 2) {
    600           var stepcount = parseInt(args[1]);
    601           if (isNaN(stepcount) || stepcount <= 0) {
    602             throw new Error('Invalid step count argument "' + args[0] + '".');
    603           }
    604           request.arguments.stepcount = stepcount;
    605         }
    606 
    607         // Get the step action.
    608         switch (args[0]) {
    609           case 'in':
    610           case 'i':
    611             request.arguments.stepaction = 'in';
    612             break;
    613 
    614           case 'min':
    615           case 'm':
    616             request.arguments.stepaction = 'min';
    617             break;
    618 
    619           case 'next':
    620           case 'n':
    621             request.arguments.stepaction = 'next';
    622             break;
    623 
    624           case 'out':
    625           case 'o':
    626             request.arguments.stepaction = 'out';
    627             break;
    628 
    629           default:
    630             throw new Error('Invalid step argument "' + args[0] + '".');
    631         }
    632 
    633       } else {
    634         // gdb style step commands:
    635         request.arguments.stepaction = type;
    636         request.arguments.stepcount = stepcount;
    637       }
    638     }
    639   } else {
    640     // Default is step of the specified type.
    641     request.arguments.stepaction = type;
    642   }
    643 
    644   return request.toJSONProtocol();
    645 };
    646 
    647 
    648 // Create a JSON request for the backtrace command.
    649 DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) {
    650   // Build a backtrace request from the text command.
    651   var request = this.createRequest('backtrace');
    652 
    653   // Default is to show top 10 frames.
    654   request.arguments = {};
    655   request.arguments.fromFrame = 0;
    656   request.arguments.toFrame = 10;
    657 
    658   args = args.split(/\s*[ ]+\s*/g);
    659   if (args.length == 1 && args[0].length > 0) {
    660     var frameCount = parseInt(args[0]);
    661     if (frameCount > 0) {
    662       // Show top frames.
    663       request.arguments.fromFrame = 0;
    664       request.arguments.toFrame = frameCount;
    665     } else {
    666       // Show bottom frames.
    667       request.arguments.fromFrame = 0;
    668       request.arguments.toFrame = -frameCount;
    669       request.arguments.bottom = true;
    670     }
    671   } else if (args.length == 2) {
    672     var fromFrame = parseInt(args[0]);
    673     var toFrame = parseInt(args[1]);
    674     if (isNaN(fromFrame) || fromFrame < 0) {
    675       throw new Error('Invalid start frame argument "' + args[0] + '".');
    676     }
    677     if (isNaN(toFrame) || toFrame < 0) {
    678       throw new Error('Invalid end frame argument "' + args[1] + '".');
    679     }
    680     if (fromFrame > toFrame) {
    681       throw new Error('Invalid arguments start frame cannot be larger ' +
    682                       'than end frame.');
    683     }
    684     // Show frame range.
    685     request.arguments.fromFrame = fromFrame;
    686     request.arguments.toFrame = toFrame + 1;
    687   } else if (args.length > 2) {
    688     throw new Error('Invalid backtrace arguments.');
    689   }
    690 
    691   return request.toJSONProtocol();
    692 };
    693 
    694 
    695 // Create a JSON request for the frame command.
    696 DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) {
    697   // Build a frame request from the text command.
    698   var request = this.createRequest('frame');
    699   args = args.split(/\s*[ ]+\s*/g);
    700   if (args.length > 0 && args[0].length > 0) {
    701     request.arguments = {};
    702     request.arguments.number = args[0];
    703   }
    704   return request.toJSONProtocol();
    705 };
    706 
    707 
    708 // Create a JSON request for the scopes command.
    709 DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) {
    710   // Build a scopes request from the text command.
    711   var request = this.createRequest('scopes');
    712   return request.toJSONProtocol();
    713 };
    714 
    715 
    716 // Create a JSON request for the scope command.
    717 DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) {
    718   // Build a scope request from the text command.
    719   var request = this.createRequest('scope');
    720   args = args.split(/\s*[ ]+\s*/g);
    721   if (args.length > 0 && args[0].length > 0) {
    722     request.arguments = {};
    723     request.arguments.number = args[0];
    724   }
    725   return request.toJSONProtocol();
    726 };
    727 
    728 
    729 // Create a JSON request for the print command.
    730 DebugRequest.prototype.printCommandToJSONRequest_ = function(args) {
    731   // Build an evaluate request from the text command.
    732   if (args.length == 0) {
    733     throw new Error('Missing expression.');
    734   }
    735   return this.makeEvaluateJSONRequest_(args);
    736 };
    737 
    738 
    739 // Create a JSON request for the dir command.
    740 DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
    741   // Build an evaluate request from the text command.
    742   if (args.length == 0) {
    743     throw new Error('Missing expression.');
    744   }
    745   return this.makeEvaluateJSONRequest_(args);
    746 };
    747 
    748 
    749 // Create a JSON request for the references command.
    750 DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
    751   // Build an evaluate request from the text command.
    752   if (args.length == 0) {
    753     throw new Error('Missing object id.');
    754   }
    755 
    756   return this.makeReferencesJSONRequest_(args, 'referencedBy');
    757 };
    758 
    759 
    760 // Create a JSON request for the instances command.
    761 DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
    762   // Build an evaluate request from the text command.
    763   if (args.length == 0) {
    764     throw new Error('Missing object id.');
    765   }
    766 
    767   // Build a references request.
    768   return this.makeReferencesJSONRequest_(args, 'constructedBy');
    769 };
    770 
    771 
    772 // Create a JSON request for the list command.
    773 DebugRequest.prototype.listCommandToJSONRequest_ = function(args) {
    774 
    775   // Default is ten lines starting five lines before the current location.
    776   if (Debug.State.displaySourceEndLine == -1) {
    777     // If we list forwards, we will start listing after the last source end
    778     // line.  Set it to start from 5 lines before the current location.
    779     Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5;
    780     // If we list backwards, we will start listing backwards from the last
    781     // source start line.  Set it to start from 1 lines before the current
    782     // location.
    783     Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1;
    784   }
    785 
    786   var from = Debug.State.displaySourceEndLine + 1;
    787   var lines = 10;
    788 
    789   // Parse the arguments.
    790   args = args.split(/\s*,\s*/g);
    791   if (args == '') {
    792   } else if ((args.length == 1) && (args[0] == '-')) {
    793     from = Debug.State.displaySourceStartLine - lines;
    794   } else if (args.length == 2) {
    795     from = parseInt(args[0]);
    796     lines = parseInt(args[1]) - from + 1; // inclusive of the ending line.
    797   } else {
    798     throw new Error('Invalid list arguments.');
    799   }
    800   Debug.State.displaySourceStartLine = from;
    801   Debug.State.displaySourceEndLine = from + lines - 1;
    802   var sourceArgs = '' + from + ' ' + lines;
    803   return this.sourceCommandToJSONRequest_(sourceArgs);
    804 };
    805 
    806 
    807 // Create a JSON request for the source command.
    808 DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
    809   // Build a evaluate request from the text command.
    810   var request = this.createRequest('source');
    811 
    812   // Default is ten lines starting five lines before the current location.
    813   var from = Debug.State.currentSourceLine - 5;
    814   var lines = 10;
    815 
    816   // Parse the arguments.
    817   args = args.split(/\s*[ ]+\s*/g);
    818   if (args.length > 1 && args[0].length > 0 && args[1].length > 0) {
    819     from = parseInt(args[0]) - 1;
    820     lines = parseInt(args[1]);
    821   } else if (args.length > 0 && args[0].length > 0) {
    822     from = parseInt(args[0]) - 1;
    823   }
    824 
    825   if (from < 0) from = 0;
    826   if (lines < 0) lines = 10;
    827 
    828   // Request source arround current source location.
    829   request.arguments = {};
    830   request.arguments.fromLine = from;
    831   request.arguments.toLine = from + lines;
    832 
    833   return request.toJSONProtocol();
    834 };
    835 
    836 
    837 // Create a JSON request for the scripts command.
    838 DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) {
    839   // Build a evaluate request from the text command.
    840   var request = this.createRequest('scripts');
    841 
    842   // Process arguments if any.
    843   if (args && args.length > 0) {
    844     args = args.split(/\s*[ ]+\s*/g);
    845 
    846     if (args.length > 1) {
    847       throw new Error('Invalid scripts arguments.');
    848     }
    849 
    850     request.arguments = {};
    851     switch (args[0]) {
    852       case 'natives':
    853         request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native);
    854         break;
    855 
    856       case 'extensions':
    857         request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension);
    858         break;
    859 
    860       case 'all':
    861         request.arguments.types =
    862             ScriptTypeFlag(Debug.ScriptType.Normal) |
    863             ScriptTypeFlag(Debug.ScriptType.Native) |
    864             ScriptTypeFlag(Debug.ScriptType.Extension);
    865         break;
    866 
    867       default:
    868         // If the arg is not one of the know one aboves, then it must be a
    869         // filter used for filtering the results:
    870         request.arguments.filter = args[0];
    871         break;
    872     }
    873   }
    874 
    875   return request.toJSONProtocol();
    876 };
    877 
    878 
    879 // Create a JSON request for the break command.
    880 DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) {
    881   // Build a evaluate request from the text command.
    882   // Process arguments if any.
    883   if (args && args.length > 0) {
    884     var target = args;
    885     var type = 'function';
    886     var line;
    887     var column;
    888     var condition;
    889     var pos;
    890 
    891     var request = this.createRequest('setbreakpoint');
    892 
    893     // Break the args into target spec and condition if appropriate.
    894 
    895     // Check for breakpoint condition.
    896     pos = args.indexOf(' ');
    897     if (pos > 0) {
    898       target = args.substring(0, pos);
    899       condition = args.substring(pos + 1, args.length);
    900     }
    901 
    902     // Check for script breakpoint (name:line[:column]). If no ':' in break
    903     // specification it is considered a function break point.
    904     pos = target.indexOf(':');
    905     if (pos > 0) {
    906       var tmp = target.substring(pos + 1, target.length);
    907       target = target.substring(0, pos);
    908       if (target[0] == '/' && target[target.length - 1] == '/') {
    909         type = 'scriptRegExp';
    910         target = target.substring(1, target.length - 1);
    911       } else {
    912         type = 'script';
    913       }
    914 
    915       // Check for both line and column.
    916       pos = tmp.indexOf(':');
    917       if (pos > 0) {
    918         column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1;
    919         line = parseInt(tmp.substring(0, pos)) - 1;
    920       } else {
    921         line = parseInt(tmp) - 1;
    922       }
    923     } else if (target[0] == '#' && target[target.length - 1] == '#') {
    924       type = 'handle';
    925       target = target.substring(1, target.length - 1);
    926     } else {
    927       type = 'function';
    928     }
    929 
    930     request.arguments = {};
    931     request.arguments.type = type;
    932     request.arguments.target = target;
    933     request.arguments.line = line;
    934     request.arguments.column = column;
    935     request.arguments.condition = condition;
    936   } else {
    937     var request = this.createRequest('suspend');
    938   }
    939 
    940   return request.toJSONProtocol();
    941 };
    942 
    943 
    944 DebugRequest.prototype.breakpointsCommandToJSONRequest_ = function(args) {
    945   if (args && args.length > 0) {
    946     throw new Error('Unexpected arguments.');
    947   }
    948   var request = this.createRequest('listbreakpoints');
    949   return request.toJSONProtocol();
    950 };
    951 
    952 
    953 // Create a JSON request for the clear command.
    954 DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) {
    955   // Build a evaluate request from the text command.
    956   var request = this.createRequest('clearbreakpoint');
    957 
    958   // Process arguments if any.
    959   if (args && args.length > 0) {
    960     request.arguments = {};
    961     request.arguments.breakpoint = parseInt(args);
    962   } else {
    963     throw new Error('Invalid break arguments.');
    964   }
    965 
    966   return request.toJSONProtocol();
    967 };
    968 
    969 
    970 // Create a JSON request for the change breakpoint command.
    971 DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ =
    972     function(args, command) {
    973 
    974   var request;
    975 
    976   // Check for exception breaks first:
    977   //   en[able] exc[eptions] [all|unc[aught]]
    978   //   en[able] [all|unc[aught]] exc[eptions]
    979   //   dis[able] exc[eptions] [all|unc[aught]]
    980   //   dis[able] [all|unc[aught]] exc[eptions]
    981   if ((command == 'enable' || command == 'disable') &&
    982       args && args.length > 1) {
    983     var nextPos = args.indexOf(' ');
    984     var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args;
    985     var excType = null;
    986 
    987     // Check for:
    988     //   en[able] exc[eptions] [all|unc[aught]]
    989     //   dis[able] exc[eptions] [all|unc[aught]]
    990     if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
    991 
    992       var arg2 = (nextPos > 0) ?
    993           args.substring(nextPos + 1, args.length) : 'all';
    994       if (!arg2) {
    995         arg2 = 'all'; // if unspecified, set for all.
    996       } else if (arg2 == 'unc') { // check for short cut.
    997         arg2 = 'uncaught';
    998       }
    999       excType = arg2;
   1000 
   1001     // Check for:
   1002     //   en[able] [all|unc[aught]] exc[eptions]
   1003     //   dis[able] [all|unc[aught]] exc[eptions]
   1004     } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') {
   1005 
   1006       var arg2 = (nextPos > 0) ?
   1007           args.substring(nextPos + 1, args.length) : null;
   1008       if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
   1009         excType = arg1;
   1010         if (excType == 'unc') {
   1011           excType = 'uncaught';
   1012         }
   1013       }
   1014     }
   1015 
   1016     // If we matched one of the command formats, then excType will be non-null:
   1017     if (excType) {
   1018       // Build a evaluate request from the text command.
   1019       request = this.createRequest('setexceptionbreak');
   1020 
   1021       request.arguments = {};
   1022       request.arguments.type = excType;
   1023       request.arguments.enabled = (command == 'enable');
   1024 
   1025       return request.toJSONProtocol();
   1026     }
   1027   }
   1028 
   1029   // Build a evaluate request from the text command.
   1030   request = this.createRequest('changebreakpoint');
   1031 
   1032   // Process arguments if any.
   1033   if (args && args.length > 0) {
   1034     request.arguments = {};
   1035     var pos = args.indexOf(' ');
   1036     var breakpointArg = args;
   1037     var otherArgs;
   1038     if (pos > 0) {
   1039       breakpointArg = args.substring(0, pos);
   1040       otherArgs = args.substring(pos + 1, args.length);
   1041     }
   1042 
   1043     request.arguments.breakpoint = parseInt(breakpointArg);
   1044 
   1045     switch(command) {
   1046       case 'cond':
   1047         request.arguments.condition = otherArgs ? otherArgs : null;
   1048         break;
   1049       case 'enable':
   1050         request.arguments.enabled = true;
   1051         break;
   1052       case 'disable':
   1053         request.arguments.enabled = false;
   1054         break;
   1055       case 'ignore':
   1056         request.arguments.ignoreCount = parseInt(otherArgs);
   1057         break;
   1058       default:
   1059         throw new Error('Invalid arguments.');
   1060     }
   1061   } else {
   1062     throw new Error('Invalid arguments.');
   1063   }
   1064 
   1065   return request.toJSONProtocol();
   1066 };
   1067 
   1068 
   1069 // Create a JSON request for the disconnect command.
   1070 DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) {
   1071   var request;
   1072   request = this.createRequest('disconnect');
   1073   return request.toJSONProtocol();
   1074 };
   1075 
   1076 
   1077 // Create a JSON request for the info command.
   1078 DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) {
   1079   var request;
   1080   if (args && (args == 'break' || args == 'br')) {
   1081     // Build a evaluate request from the text command.
   1082     request = this.createRequest('listbreakpoints');
   1083     last_cmd = 'info break';
   1084   } else if (args && (args == 'locals' || args == 'lo')) {
   1085     // Build a evaluate request from the text command.
   1086     request = this.createRequest('frame');
   1087     last_cmd = 'info locals';
   1088   } else if (args && (args == 'args' || args == 'ar')) {
   1089     // Build a evaluate request from the text command.
   1090     request = this.createRequest('frame');
   1091     last_cmd = 'info args';
   1092   } else {
   1093     throw new Error('Invalid info arguments.');
   1094   }
   1095 
   1096   return request.toJSONProtocol();
   1097 };
   1098 
   1099 
   1100 DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) {
   1101   var request;
   1102   request = this.createRequest('v8flags');
   1103   request.arguments = {};
   1104   request.arguments.flags = args;
   1105   return request.toJSONProtocol();
   1106 };
   1107 
   1108 
   1109 DebugRequest.prototype.gcToJSONRequest_ = function(args) {
   1110   var request;
   1111   if (!args) {
   1112     args = 'all';
   1113   }
   1114   var args = args.split(/\s+/g);
   1115   var cmd = args[0];
   1116 
   1117   switch(cmd) {
   1118     case 'all':
   1119     case 'quick':
   1120     case 'full':
   1121     case 'young':
   1122     case 'old':
   1123     case 'compact':
   1124     case 'sweep':
   1125     case 'scavenge': {
   1126       if (cmd == 'young') { cmd = 'quick'; }
   1127       else if (cmd == 'old') { cmd = 'full'; }
   1128 
   1129       request = this.createRequest('gc');
   1130       request.arguments = {};
   1131       request.arguments.type = cmd;
   1132       break;
   1133     }
   1134       // Else fall thru to the default case below to report the error.
   1135     default:
   1136       throw new Error('Missing arguments after ' + cmd + '.');
   1137   }
   1138   return request.toJSONProtocol();
   1139 };
   1140 
   1141 
   1142 // Create a JSON request for the threads command.
   1143 DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
   1144   // Build a threads request from the text command.
   1145   var request = this.createRequest('threads');
   1146   return request.toJSONProtocol();
   1147 };
   1148 
   1149 
   1150 // Handle the trace command.
   1151 DebugRequest.prototype.traceCommand_ = function(args) {
   1152   // Process arguments.
   1153   if (args && args.length > 0) {
   1154     if (args == 'compile') {
   1155       trace_compile = !trace_compile;
   1156       print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off'));
   1157     } else if (args === 'debug json' || args === 'json' || args === 'packets') {
   1158       trace_debug_json = !trace_debug_json;
   1159       print('Tracing of debug json packets ' +
   1160             (trace_debug_json ? 'on' : 'off'));
   1161     } else {
   1162       throw new Error('Invalid trace arguments.');
   1163     }
   1164   } else {
   1165     throw new Error('Invalid trace arguments.');
   1166   }
   1167 };
   1168 
   1169 // Handle the help command.
   1170 DebugRequest.prototype.helpCommand_ = function(args) {
   1171   // Help os quite simple.
   1172   if (args && args.length > 0) {
   1173     print('warning: arguments to \'help\' are ignored');
   1174   }
   1175 
   1176   print('Note: <> denotes symbollic values to be replaced with real values.');
   1177   print('Note: [] denotes optional parts of commands, or optional options / arguments.');
   1178   print('      e.g. d[elete] - you get the same command if you type d or delete.');
   1179   print('');
   1180   print('[break] - break as soon as possible');
   1181   print('b[reak] location [condition]');
   1182   print('        - break on named function: location is a function name');
   1183   print('        - break on function: location is #<id>#');
   1184   print('        - break on script position: location is name:line[:column]');
   1185   print('');
   1186   print('clear <breakpoint #>       - deletes the specified user defined breakpoint');
   1187   print('d[elete]  <breakpoint #>   - deletes the specified user defined breakpoint');
   1188   print('dis[able] <breakpoint #>   - disables the specified user defined breakpoint');
   1189   print('dis[able] exc[eptions] [[all] | unc[aught]]');
   1190   print('                           - disables breaking on exceptions');
   1191   print('en[able]  <breakpoint #>   - enables the specified user defined breakpoint');
   1192   print('en[able]  exc[eptions] [[all] | unc[aught]]');
   1193   print('                           - enables breaking on exceptions');
   1194   print('');
   1195   print('b[ack]t[race] [n] | [-n] | [from to]');
   1196   print('                           - prints the stack back trace');
   1197   print('f[rame]                    - prints info about the current frame context');
   1198   print('f[rame] <frame #>          - set context to specified frame #');
   1199   print('scopes');
   1200   print('scope <scope #>');
   1201   print('');
   1202   print('up                         - set context to caller of current frame');
   1203   print('do[wn]                     - set context to callee of current frame');
   1204   print('inf[o] br[eak]             - prints info about breakpoints in use');
   1205   print('inf[o] ar[gs]              - prints info about arguments of the current function');
   1206   print('inf[o] lo[cals]            - prints info about locals in the current function');
   1207   print('');
   1208   print('step [in | next | out| min [step count]]');
   1209   print('c[ontinue]                 - continue executing after a breakpoint');
   1210   print('s[tep]   [<N>]             - step into the next N callees (default N is 1)');
   1211   print('s[tep]i  [<N>]             - step into the next N callees (default N is 1)');
   1212   print('n[ext]   [<N>]             - step over the next N callees (default N is 1)');
   1213   print('fin[ish] [<N>]             - step out of N frames (default N is 1)');
   1214   print('');
   1215   print('p[rint] <expression>       - prints the result of the specified expression');
   1216   print('dir <expression>           - prints the object structure of the result');
   1217   print('set <var> = <expression>   - executes the specified statement');
   1218   print('');
   1219   print('l[ist]                     - list the source code around for the current pc');
   1220   print('l[ist] [- | <start>,<end>] - list the specified range of source code');
   1221   print('source [from line [num lines]]');
   1222   print('scr[ipts] [native|extensions|all]');
   1223   print('scr[ipts] [<filter text>]  - list scripts with the specified text in its description');
   1224   print('');
   1225   print('gc                         - runs the garbage collector');
   1226   print('');
   1227   print('trace compile');
   1228   // hidden command: trace debug json - toggles tracing of debug json packets
   1229   print('');
   1230   print('disconnect|exit|quit       - disconnects and quits the debugger');
   1231   print('help                       - prints this help information');
   1232 };
   1233 
   1234 
   1235 function formatHandleReference_(value) {
   1236   if (value.handle() >= 0) {
   1237     return '#' + value.handle() + '#';
   1238   } else {
   1239     return '#Transient#';
   1240   }
   1241 }
   1242 
   1243 
   1244 function formatObject_(value, include_properties) {
   1245   var result = '';
   1246   result += formatHandleReference_(value);
   1247   result += ', type: object';
   1248   result += ', constructor ';
   1249   var ctor = value.constructorFunctionValue();
   1250   result += formatHandleReference_(ctor);
   1251   result += ', __proto__ ';
   1252   var proto = value.protoObjectValue();
   1253   result += formatHandleReference_(proto);
   1254   result += ', ';
   1255   result += value.propertyCount();
   1256   result +=  ' properties.';
   1257   if (include_properties) {
   1258     result +=  '\n';
   1259     for (var i = 0; i < value.propertyCount(); i++) {
   1260       result += '  ';
   1261       result += value.propertyName(i);
   1262       result += ': ';
   1263       var property_value = value.propertyValue(i);
   1264       if (property_value instanceof ProtocolReference) {
   1265         result += '<no type>';
   1266       } else {
   1267         if (property_value && property_value.type()) {
   1268           result += property_value.type();
   1269         } else {
   1270           result += '<no type>';
   1271         }
   1272       }
   1273       result += ' ';
   1274       result += formatHandleReference_(property_value);
   1275       result += '\n';
   1276     }
   1277   }
   1278   return result;
   1279 }
   1280 
   1281 
   1282 function formatScope_(scope) {
   1283   var result = '';
   1284   var index = scope.index;
   1285   result += '#' + (index <= 9 ? '0' : '') + index;
   1286   result += ' ';
   1287   switch (scope.type) {
   1288     case Debug.ScopeType.Global:
   1289       result += 'Global, ';
   1290       result += '#' + scope.object.ref + '#';
   1291       break;
   1292     case Debug.ScopeType.Local:
   1293       result += 'Local';
   1294       break;
   1295     case Debug.ScopeType.With:
   1296       result += 'With, ';
   1297       result += '#' + scope.object.ref + '#';
   1298       break;
   1299     case Debug.ScopeType.Catch:
   1300       result += 'Catch, ';
   1301       result += '#' + scope.object.ref + '#';
   1302       break;
   1303     case Debug.ScopeType.Closure:
   1304       result += 'Closure';
   1305       break;
   1306     default:
   1307       result += 'UNKNOWN';
   1308   }
   1309   return result;
   1310 }
   1311 
   1312 
   1313 function refObjectToString_(protocolPackage, handle) {
   1314   var value = protocolPackage.lookup(handle);
   1315   var result = '';
   1316   if (value.isString()) {
   1317     result = '"' + value.value() + '"';
   1318   } else if (value.isPrimitive()) {
   1319     result = value.valueString();
   1320   } else if (value.isObject()) {
   1321     result += formatObject_(value, true);
   1322   }
   1323   return result;
   1324 }
   1325 
   1326 
   1327 // Rounds number 'num' to 'length' decimal places.
   1328 function roundNumber(num, length) {
   1329   var factor = Math.pow(10, length);
   1330   return Math.round(num * factor) / factor;
   1331 }
   1332 
   1333 
   1334 // Convert a JSON response to text for display in a text based debugger.
   1335 function DebugResponseDetails(response) {
   1336   var details = { text: '', running: false };
   1337 
   1338   try {
   1339     if (!response.success()) {
   1340       details.text = response.message();
   1341       return details;
   1342     }
   1343 
   1344     // Get the running state.
   1345     details.running = response.running();
   1346 
   1347     var body = response.body();
   1348     var result = '';
   1349     switch (response.command()) {
   1350       case 'suspend':
   1351         details.text = 'stopped';
   1352         break;
   1353 
   1354       case 'setbreakpoint':
   1355         result = 'set breakpoint #';
   1356         result += body.breakpoint;
   1357         details.text = result;
   1358         break;
   1359 
   1360       case 'clearbreakpoint':
   1361         result = 'cleared breakpoint #';
   1362         result += body.breakpoint;
   1363         details.text = result;
   1364         break;
   1365 
   1366       case 'changebreakpoint':
   1367         result = 'successfully changed breakpoint';
   1368         details.text = result;
   1369         break;
   1370 
   1371       case 'listbreakpoints':
   1372         result = 'breakpoints: (' + body.breakpoints.length + ')';
   1373         for (var i = 0; i < body.breakpoints.length; i++) {
   1374           var breakpoint = body.breakpoints[i];
   1375           result += '\n id=' + breakpoint.number;
   1376           result += ' type=' + breakpoint.type;
   1377           if (breakpoint.script_id) {
   1378               result += ' script_id=' + breakpoint.script_id;
   1379           }
   1380           if (breakpoint.script_name) {
   1381               result += ' script_name=' + breakpoint.script_name;
   1382           }
   1383           if (breakpoint.script_regexp) {
   1384               result += ' script_regexp=' + breakpoint.script_regexp;
   1385           }
   1386           result += ' line=' + (breakpoint.line + 1);
   1387           if (breakpoint.column != null) {
   1388             result += ' column=' + (breakpoint.column + 1);
   1389           }
   1390           if (breakpoint.groupId) {
   1391             result += ' groupId=' + breakpoint.groupId;
   1392           }
   1393           if (breakpoint.ignoreCount) {
   1394               result += ' ignoreCount=' + breakpoint.ignoreCount;
   1395           }
   1396           if (breakpoint.active === false) {
   1397             result += ' inactive';
   1398           }
   1399           if (breakpoint.condition) {
   1400             result += ' condition=' + breakpoint.condition;
   1401           }
   1402           result += ' hit_count=' + breakpoint.hit_count;
   1403         }
   1404         if (body.breakpoints.length === 0) {
   1405           result = "No user defined breakpoints\n";
   1406         } else {
   1407           result += '\n';
   1408         }
   1409         if (body.breakOnExceptions) {
   1410           result += '* breaking on ALL exceptions is enabled\n';
   1411         } else if (body.breakOnUncaughtExceptions) {
   1412           result += '* breaking on UNCAUGHT exceptions is enabled\n';
   1413         } else {
   1414           result += '* all exception breakpoints are disabled\n';
   1415         }
   1416         details.text = result;
   1417         break;
   1418 
   1419       case 'setexceptionbreak':
   1420         result = 'Break on ' + body.type + ' exceptions: ';
   1421         result += body.enabled ? 'enabled' : 'disabled';
   1422         details.text = result;
   1423         break;
   1424 
   1425       case 'backtrace':
   1426         if (body.totalFrames == 0) {
   1427           result = '(empty stack)';
   1428         } else {
   1429           var result = 'Frames #' + body.fromFrame + ' to #' +
   1430               (body.toFrame - 1) + ' of ' + body.totalFrames + '\n';
   1431           for (i = 0; i < body.frames.length; i++) {
   1432             if (i != 0) result += '\n';
   1433             result += body.frames[i].text;
   1434           }
   1435         }
   1436         details.text = result;
   1437         break;
   1438 
   1439       case 'frame':
   1440         if (last_cmd === 'info locals') {
   1441           var locals = body.locals;
   1442           if (locals.length === 0) {
   1443             result = 'No locals';
   1444           } else {
   1445             for (var i = 0; i < locals.length; i++) {
   1446               var local = locals[i];
   1447               result += local.name + ' = ';
   1448               result += refObjectToString_(response, local.value.ref);
   1449               result += '\n';
   1450             }
   1451           }
   1452         } else if (last_cmd === 'info args') {
   1453           var args = body.arguments;
   1454           if (args.length === 0) {
   1455             result = 'No arguments';
   1456           } else {
   1457             for (var i = 0; i < args.length; i++) {
   1458               var arg = args[i];
   1459               result += arg.name + ' = ';
   1460               result += refObjectToString_(response, arg.value.ref);
   1461               result += '\n';
   1462             }
   1463           }
   1464         } else {
   1465           result = SourceUnderline(body.sourceLineText,
   1466                                    body.column);
   1467           Debug.State.currentSourceLine = body.line;
   1468           Debug.State.currentFrame = body.index;
   1469           Debug.State.displaySourceStartLine = -1;
   1470           Debug.State.displaySourceEndLine = -1;
   1471         }
   1472         details.text = result;
   1473         break;
   1474 
   1475       case 'scopes':
   1476         if (body.totalScopes == 0) {
   1477           result = '(no scopes)';
   1478         } else {
   1479           result = 'Scopes #' + body.fromScope + ' to #' +
   1480                    (body.toScope - 1) + ' of ' + body.totalScopes + '\n';
   1481           for (i = 0; i < body.scopes.length; i++) {
   1482             if (i != 0) {
   1483               result += '\n';
   1484             }
   1485             result += formatScope_(body.scopes[i]);
   1486           }
   1487         }
   1488         details.text = result;
   1489         break;
   1490 
   1491       case 'scope':
   1492         result += formatScope_(body);
   1493         result += '\n';
   1494         var scope_object_value = response.lookup(body.object.ref);
   1495         result += formatObject_(scope_object_value, true);
   1496         details.text = result;
   1497         break;
   1498 
   1499       case 'evaluate':
   1500       case 'lookup':
   1501       case 'getobj':
   1502         if (last_cmd == 'p' || last_cmd == 'print') {
   1503           result = body.text;
   1504         } else {
   1505           var value;
   1506           if (lookup_handle) {
   1507             value = response.bodyValue(lookup_handle);
   1508           } else {
   1509             value = response.bodyValue();
   1510           }
   1511           if (value.isObject()) {
   1512             result += formatObject_(value, true);
   1513           } else {
   1514             result += 'type: ';
   1515             result += value.type();
   1516             if (!value.isUndefined() && !value.isNull()) {
   1517               result += ', ';
   1518               if (value.isString()) {
   1519                 result += '"';
   1520               }
   1521               result += value.value();
   1522               if (value.isString()) {
   1523                 result += '"';
   1524               }
   1525             }
   1526             result += '\n';
   1527           }
   1528         }
   1529         details.text = result;
   1530         break;
   1531 
   1532       case 'references':
   1533         var count = body.length;
   1534         result += 'found ' + count + ' objects';
   1535         result += '\n';
   1536         for (var i = 0; i < count; i++) {
   1537           var value = response.bodyValue(i);
   1538           result += formatObject_(value, false);
   1539           result += '\n';
   1540         }
   1541         details.text = result;
   1542         break;
   1543 
   1544       case 'source':
   1545         // Get the source from the response.
   1546         var source = body.source;
   1547         var from_line = body.fromLine + 1;
   1548         var lines = source.split('\n');
   1549         var maxdigits = 1 + Math.floor(log10(from_line + lines.length));
   1550         if (maxdigits < 3) {
   1551           maxdigits = 3;
   1552         }
   1553         var result = '';
   1554         for (var num = 0; num < lines.length; num++) {
   1555           // Check if there's an extra newline at the end.
   1556           if (num == (lines.length - 1) && lines[num].length == 0) {
   1557             break;
   1558           }
   1559 
   1560           var current_line = from_line + num;
   1561           var spacer = maxdigits - (1 + Math.floor(log10(current_line)));
   1562           if (current_line == Debug.State.currentSourceLine + 1) {
   1563             for (var i = 0; i < maxdigits; i++) {
   1564               result += '>';
   1565             }
   1566             result += '  ';
   1567           } else {
   1568             for (var i = 0; i < spacer; i++) {
   1569               result += ' ';
   1570             }
   1571             result += current_line + ': ';
   1572           }
   1573           result += lines[num];
   1574           result += '\n';
   1575         }
   1576         details.text = result;
   1577         break;
   1578 
   1579       case 'scripts':
   1580         var result = '';
   1581         for (i = 0; i < body.length; i++) {
   1582           if (i != 0) result += '\n';
   1583           if (body[i].id) {
   1584             result += body[i].id;
   1585           } else {
   1586             result += '[no id]';
   1587           }
   1588           result += ', ';
   1589           if (body[i].name) {
   1590             result += body[i].name;
   1591           } else {
   1592             if (body[i].compilationType == Debug.ScriptCompilationType.Eval
   1593                 && body[i].evalFromScript
   1594                 ) {
   1595               result += 'eval from ';
   1596               var script_value = response.lookup(body[i].evalFromScript.ref);
   1597               result += ' ' + script_value.field('name');
   1598               result += ':' + (body[i].evalFromLocation.line + 1);
   1599               result += ':' + body[i].evalFromLocation.column;
   1600             } else if (body[i].compilationType ==
   1601                        Debug.ScriptCompilationType.JSON) {
   1602               result += 'JSON ';
   1603             } else {  // body[i].compilation == Debug.ScriptCompilationType.Host
   1604               result += '[unnamed] ';
   1605             }
   1606           }
   1607           result += ' (lines: ';
   1608           result += body[i].lineCount;
   1609           result += ', length: ';
   1610           result += body[i].sourceLength;
   1611           if (body[i].type == Debug.ScriptType.Native) {
   1612             result += ', native';
   1613           } else if (body[i].type == Debug.ScriptType.Extension) {
   1614             result += ', extension';
   1615           }
   1616           result += '), [';
   1617           var sourceStart = body[i].sourceStart;
   1618           if (sourceStart.length > 40) {
   1619             sourceStart = sourceStart.substring(0, 37) + '...';
   1620           }
   1621           result += sourceStart;
   1622           result += ']';
   1623         }
   1624         if (body.length == 0) {
   1625           result = "no matching scripts found";
   1626         }
   1627         details.text = result;
   1628         break;
   1629 
   1630       case 'threads':
   1631         var result = 'Active V8 threads: ' + body.totalThreads + '\n';
   1632         body.threads.sort(function(a, b) { return a.id - b.id; });
   1633         for (i = 0; i < body.threads.length; i++) {
   1634           result += body.threads[i].current ? '*' : ' ';
   1635           result += ' ';
   1636           result += body.threads[i].id;
   1637           result += '\n';
   1638         }
   1639         details.text = result;
   1640         break;
   1641 
   1642       case 'continue':
   1643         details.text = "(running)";
   1644         break;
   1645 
   1646       case 'v8flags':
   1647         details.text = "flags set";
   1648         break;
   1649 
   1650       case 'gc':
   1651         details.text = "GC " + body.before + " => " + body.after;
   1652         if (body.after > (1024*1024)) {
   1653           details.text +=
   1654               " (" + roundNumber(body.before/(1024*1024), 1) + "M => " +
   1655                      roundNumber(body.after/(1024*1024), 1) + "M)";
   1656         } else if (body.after > 1024) {
   1657           details.text +=
   1658               " (" + roundNumber(body.before/1024, 1) + "K => " +
   1659                      roundNumber(body.after/1024, 1) + "K)";
   1660         }
   1661         break;
   1662 
   1663       default:
   1664         details.text =
   1665             'Response for unknown command \'' + response.command() + '\'' +
   1666             ' (' + response.raw_json() + ')';
   1667     }
   1668   } catch (e) {
   1669     details.text = 'Error: "' + e + '" formatting response';
   1670   }
   1671 
   1672   return details;
   1673 }
   1674 
   1675 
   1676 /**
   1677  * Protocol packages send from the debugger.
   1678  * @param {string} json - raw protocol packet as JSON string.
   1679  * @constructor
   1680  */
   1681 function ProtocolPackage(json) {
   1682   this.raw_json_ = json;
   1683   this.packet_ = JSON.parse(json);
   1684   this.refs_ = [];
   1685   if (this.packet_.refs) {
   1686     for (var i = 0; i < this.packet_.refs.length; i++) {
   1687       this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
   1688     }
   1689   }
   1690 }
   1691 
   1692 
   1693 /**
   1694  * Get the packet type.
   1695  * @return {String} the packet type
   1696  */
   1697 ProtocolPackage.prototype.type = function() {
   1698   return this.packet_.type;
   1699 };
   1700 
   1701 
   1702 /**
   1703  * Get the packet event.
   1704  * @return {Object} the packet event
   1705  */
   1706 ProtocolPackage.prototype.event = function() {
   1707   return this.packet_.event;
   1708 };
   1709 
   1710 
   1711 /**
   1712  * Get the packet request sequence.
   1713  * @return {number} the packet request sequence
   1714  */
   1715 ProtocolPackage.prototype.requestSeq = function() {
   1716   return this.packet_.request_seq;
   1717 };
   1718 
   1719 
   1720 /**
   1721  * Get the packet request sequence.
   1722  * @return {number} the packet request sequence
   1723  */
   1724 ProtocolPackage.prototype.running = function() {
   1725   return this.packet_.running ? true : false;
   1726 };
   1727 
   1728 
   1729 ProtocolPackage.prototype.success = function() {
   1730   return this.packet_.success ? true : false;
   1731 };
   1732 
   1733 
   1734 ProtocolPackage.prototype.message = function() {
   1735   return this.packet_.message;
   1736 };
   1737 
   1738 
   1739 ProtocolPackage.prototype.command = function() {
   1740   return this.packet_.command;
   1741 };
   1742 
   1743 
   1744 ProtocolPackage.prototype.body = function() {
   1745   return this.packet_.body;
   1746 };
   1747 
   1748 
   1749 ProtocolPackage.prototype.bodyValue = function(index) {
   1750   if (index != null) {
   1751     return new ProtocolValue(this.packet_.body[index], this);
   1752   } else {
   1753     return new ProtocolValue(this.packet_.body, this);
   1754   }
   1755 };
   1756 
   1757 
   1758 ProtocolPackage.prototype.body = function() {
   1759   return this.packet_.body;
   1760 };
   1761 
   1762 
   1763 ProtocolPackage.prototype.lookup = function(handle) {
   1764   var value = this.refs_[handle];
   1765   if (value) {
   1766     return new ProtocolValue(value, this);
   1767   } else {
   1768     return new ProtocolReference(handle);
   1769   }
   1770 };
   1771 
   1772 
   1773 ProtocolPackage.prototype.raw_json = function() {
   1774   return this.raw_json_;
   1775 };
   1776 
   1777 
   1778 function ProtocolValue(value, packet) {
   1779   this.value_ = value;
   1780   this.packet_ = packet;
   1781 }
   1782 
   1783 
   1784 /**
   1785  * Get the value type.
   1786  * @return {String} the value type
   1787  */
   1788 ProtocolValue.prototype.type = function() {
   1789   return this.value_.type;
   1790 };
   1791 
   1792 
   1793 /**
   1794  * Get a metadata field from a protocol value.
   1795  * @return {Object} the metadata field value
   1796  */
   1797 ProtocolValue.prototype.field = function(name) {
   1798   return this.value_[name];
   1799 };
   1800 
   1801 
   1802 /**
   1803  * Check is the value is a primitive value.
   1804  * @return {boolean} true if the value is primitive
   1805  */
   1806 ProtocolValue.prototype.isPrimitive = function() {
   1807   return this.isUndefined() || this.isNull() || this.isBoolean() ||
   1808          this.isNumber() || this.isString();
   1809 };
   1810 
   1811 
   1812 /**
   1813  * Get the object handle.
   1814  * @return {number} the value handle
   1815  */
   1816 ProtocolValue.prototype.handle = function() {
   1817   return this.value_.handle;
   1818 };
   1819 
   1820 
   1821 /**
   1822  * Check is the value is undefined.
   1823  * @return {boolean} true if the value is undefined
   1824  */
   1825 ProtocolValue.prototype.isUndefined = function() {
   1826   return this.value_.type == 'undefined';
   1827 };
   1828 
   1829 
   1830 /**
   1831  * Check is the value is null.
   1832  * @return {boolean} true if the value is null
   1833  */
   1834 ProtocolValue.prototype.isNull = function() {
   1835   return this.value_.type == 'null';
   1836 };
   1837 
   1838 
   1839 /**
   1840  * Check is the value is a boolean.
   1841  * @return {boolean} true if the value is a boolean
   1842  */
   1843 ProtocolValue.prototype.isBoolean = function() {
   1844   return this.value_.type == 'boolean';
   1845 };
   1846 
   1847 
   1848 /**
   1849  * Check is the value is a number.
   1850  * @return {boolean} true if the value is a number
   1851  */
   1852 ProtocolValue.prototype.isNumber = function() {
   1853   return this.value_.type == 'number';
   1854 };
   1855 
   1856 
   1857 /**
   1858  * Check is the value is a string.
   1859  * @return {boolean} true if the value is a string
   1860  */
   1861 ProtocolValue.prototype.isString = function() {
   1862   return this.value_.type == 'string';
   1863 };
   1864 
   1865 
   1866 /**
   1867  * Check is the value is an object.
   1868  * @return {boolean} true if the value is an object
   1869  */
   1870 ProtocolValue.prototype.isObject = function() {
   1871   return this.value_.type == 'object' || this.value_.type == 'function' ||
   1872          this.value_.type == 'error' || this.value_.type == 'regexp';
   1873 };
   1874 
   1875 
   1876 /**
   1877  * Get the constructor function
   1878  * @return {ProtocolValue} constructor function
   1879  */
   1880 ProtocolValue.prototype.constructorFunctionValue = function() {
   1881   var ctor = this.value_.constructorFunction;
   1882   return this.packet_.lookup(ctor.ref);
   1883 };
   1884 
   1885 
   1886 /**
   1887  * Get the __proto__ value
   1888  * @return {ProtocolValue} __proto__ value
   1889  */
   1890 ProtocolValue.prototype.protoObjectValue = function() {
   1891   var proto = this.value_.protoObject;
   1892   return this.packet_.lookup(proto.ref);
   1893 };
   1894 
   1895 
   1896 /**
   1897  * Get the number og properties.
   1898  * @return {number} the number of properties
   1899  */
   1900 ProtocolValue.prototype.propertyCount = function() {
   1901   return this.value_.properties ? this.value_.properties.length : 0;
   1902 };
   1903 
   1904 
   1905 /**
   1906  * Get the specified property name.
   1907  * @return {string} property name
   1908  */
   1909 ProtocolValue.prototype.propertyName = function(index) {
   1910   var property = this.value_.properties[index];
   1911   return property.name;
   1912 };
   1913 
   1914 
   1915 /**
   1916  * Return index for the property name.
   1917  * @param name The property name to look for
   1918  * @return {number} index for the property name
   1919  */
   1920 ProtocolValue.prototype.propertyIndex = function(name) {
   1921   for (var i = 0; i < this.propertyCount(); i++) {
   1922     if (this.value_.properties[i].name == name) {
   1923       return i;
   1924     }
   1925   }
   1926   return null;
   1927 };
   1928 
   1929 
   1930 /**
   1931  * Get the specified property value.
   1932  * @return {ProtocolValue} property value
   1933  */
   1934 ProtocolValue.prototype.propertyValue = function(index) {
   1935   var property = this.value_.properties[index];
   1936   return this.packet_.lookup(property.ref);
   1937 };
   1938 
   1939 
   1940 /**
   1941  * Check is the value is a string.
   1942  * @return {boolean} true if the value is a string
   1943  */
   1944 ProtocolValue.prototype.value = function() {
   1945   return this.value_.value;
   1946 };
   1947 
   1948 
   1949 ProtocolValue.prototype.valueString = function() {
   1950   return this.value_.text;
   1951 };
   1952 
   1953 
   1954 function ProtocolReference(handle) {
   1955   this.handle_ = handle;
   1956 }
   1957 
   1958 
   1959 ProtocolReference.prototype.handle = function() {
   1960   return this.handle_;
   1961 };
   1962 
   1963 
   1964 // A more universal stringify that supports more types than JSON.
   1965 // Used by the d8 shell to output results.
   1966 var stringifyDepthLimit = 4;  // To avoid crashing on cyclic objects
   1967 
   1968 function Stringify(x, depth) {
   1969   if (depth === undefined)
   1970     depth = stringifyDepthLimit;
   1971   else if (depth === 0)
   1972     return "*";
   1973   switch (typeof x) {
   1974     case "undefined":
   1975       return "undefined";
   1976     case "boolean":
   1977     case "number":
   1978     case "function":
   1979       return x.toString();
   1980     case "string":
   1981       return "\"" + x.toString() + "\"";
   1982     case "symbol":
   1983       return x.toString();
   1984     case "object":
   1985       if (IS_NULL(x)) return "null";
   1986       if (x.constructor && x.constructor.name === "Array") {
   1987         var elems = [];
   1988         for (var i = 0; i < x.length; ++i) {
   1989           elems.push(
   1990             {}.hasOwnProperty.call(x, i) ? Stringify(x[i], depth - 1) : "");
   1991         }
   1992         return "[" + elems.join(", ") + "]";
   1993       }
   1994       try {
   1995         var string = String(x);
   1996         if (string && string !== "[object Object]") return string;
   1997       } catch(e) {}
   1998       var props = [];
   1999       var names = Object.getOwnPropertyNames(x);
   2000       names = names.concat(Object.getOwnPropertySymbols(x));
   2001       for (var i in names) {
   2002         var name = names[i];
   2003         var desc = Object.getOwnPropertyDescriptor(x, name);
   2004         if (IS_UNDEFINED(desc)) continue;
   2005         if (IS_SYMBOL(name)) name = "[" + Stringify(name) + "]";
   2006         if ("value" in desc) {
   2007           props.push(name + ": " + Stringify(desc.value, depth - 1));
   2008         }
   2009         if (desc.get) {
   2010           var getter = Stringify(desc.get);
   2011           props.push("get " + name + getter.slice(getter.indexOf('(')));
   2012         }
   2013         if (desc.set) {
   2014           var setter = Stringify(desc.set);
   2015           props.push("set " + name + setter.slice(setter.indexOf('(')));
   2016         }
   2017       }
   2018       return "{" + props.join(", ") + "}";
   2019     default:
   2020       return "[crazy non-standard shit]";
   2021   }
   2022 }
   2023