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