Home | History | Annotate | Download | only in src
      1 // Copyright 2008 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 String.prototype.startsWith = function (str) {
     29   if (str.length > this.length)
     30     return false;
     31   return this.substr(0, str.length) == str;
     32 }
     33 
     34 function log10(num) {
     35   return Math.log(num)/Math.log(10);
     36 }
     37 
     38 function ToInspectableObject(obj) {
     39   if (!obj && typeof obj === 'object') {
     40     return void 0;
     41   } else {
     42     return Object(obj);
     43   }
     44 }
     45 
     46 function GetCompletions(global, last, full) {
     47   var full_tokens = full.split();
     48   full = full_tokens.pop();
     49   var parts = full.split('.');
     50   parts.pop();
     51   var current = global;
     52   for (var i = 0; i < parts.length; i++) {
     53     var part = parts[i];
     54     var next = current[part];
     55     if (!next)
     56       return [];
     57     current = next;
     58   }
     59   var result = [];
     60   current = ToInspectableObject(current);
     61   while (typeof current !== 'undefined') {
     62     var mirror = new $debug.ObjectMirror(current);
     63     var properties = mirror.properties();
     64     for (var i = 0; i < properties.length; i++) {
     65       var name = properties[i].name();
     66       if (typeof name === 'string' && name.startsWith(last))
     67         result.push(name);
     68     }
     69     current = ToInspectableObject(current.__proto__);
     70   }
     71   return result;
     72 }
     73 
     74 
     75 // Global object holding debugger related constants and state.
     76 const Debug = {};
     77 
     78 
     79 // Debug events which can occour in the V8 JavaScript engine. These originate
     80 // from the API include file v8-debug.h.
     81 Debug.DebugEvent = { Break: 1,
     82                      Exception: 2,
     83                      NewFunction: 3,
     84                      BeforeCompile: 4,
     85                      AfterCompile: 5 };
     86 
     87 
     88 // The different types of scripts matching enum ScriptType in objects.h.
     89 Debug.ScriptType = { Native: 0,
     90                      Extension: 1,
     91                      Normal: 2 };
     92 
     93 
     94 // The different types of script compilations matching enum
     95 // Script::CompilationType in objects.h.
     96 Debug.ScriptCompilationType = { Host: 0,
     97                                 Eval: 1,
     98                                 JSON: 2 };
     99 
    100 
    101 // The different types of scopes matching constants runtime.cc.
    102 Debug.ScopeType = { Global: 0,
    103                     Local: 1,
    104                     With: 2,
    105                     Closure: 3,
    106                     Catch: 4 };
    107 
    108 
    109 // Current debug state.
    110 const kNoFrame = -1;
    111 Debug.State = {
    112   currentFrame: kNoFrame,
    113   currentSourceLine: -1
    114 }
    115 var trace_compile = false;  // Tracing all compile events?
    116 
    117 
    118 // Process a debugger JSON message into a display text and a running status.
    119 // This function returns an object with properties "text" and "running" holding
    120 // this information.
    121 function DebugMessageDetails(message) {
    122   // Convert the JSON string to an object.
    123   var response = new ProtocolPackage(message);
    124 
    125   if (response.type() == 'event') {
    126     return DebugEventDetails(response);
    127   } else {
    128     return DebugResponseDetails(response);
    129   }
    130 }
    131 
    132 function DebugEventDetails(response) {
    133   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.currentFrame = 0;
    165       details.text = result;
    166       break;
    167       
    168     case 'exception':
    169       if (body.uncaught) {
    170         result += 'Uncaught: ';
    171       } else {
    172         result += 'Exception: ';
    173       }
    174       result += '"';
    175       result += body.exception.text;
    176       result += '"';
    177       if (body.sourceLine >= 0) {
    178         result += ', ';
    179         result += SourceInfo(body);
    180         result += '\n';
    181         result += SourceUnderline(body.sourceLineText, body.sourceColumn);
    182         Debug.State.currentSourceLine = body.sourceLine;
    183         Debug.State.currentFrame = 0;
    184       } else {
    185         result += ' (empty stack)';
    186         Debug.State.currentSourceLine = -1;
    187         Debug.State.currentFrame = kNoFrame;
    188       }
    189       details.text = result;
    190       break;
    191 
    192     case 'afterCompile':
    193       if (trace_compile) {
    194         result = 'Source ' + body.script.name + ' compiled:\n'
    195         var source = body.script.source;
    196         if (!(source[source.length - 1] == '\n')) {
    197           result += source;
    198         } else {
    199           result += source.substring(0, source.length - 1);
    200         }
    201       }
    202       details.text = result;
    203       break;
    204 
    205     default:
    206       details.text = 'Unknown debug event ' + response.event();
    207   }
    208 
    209   return details;
    210 };
    211 
    212 
    213 function SourceInfo(body) {
    214   var result = '';
    215   
    216   if (body.script) {
    217     if (body.script.name) {
    218       result += body.script.name;
    219     } else {
    220       result += '[unnamed]';
    221     }
    222   }
    223   result += ' line ';
    224   result += body.sourceLine + 1;
    225   result += ' column ';
    226   result += body.sourceColumn + 1;
    227   
    228   return result;
    229 }
    230 
    231 
    232 function SourceUnderline(source_text, position) {
    233   if (!source_text) {
    234     return;
    235   }
    236 
    237   // Create an underline with a caret pointing to the source position. If the
    238   // source contains a tab character the underline will have a tab character in
    239   // the same place otherwise the underline will have a space character.
    240   var underline = '';
    241   for (var i = 0; i < position; i++) {
    242     if (source_text[i] == '\t') {
    243       underline += '\t';
    244     } else {
    245       underline += ' ';
    246     }
    247   }
    248   underline += '^';
    249 
    250   // Return the source line text with the underline beneath.
    251   return source_text + '\n' + underline;
    252 };
    253 
    254 
    255 // Converts a text command to a JSON request.
    256 function DebugCommandToJSONRequest(cmd_line) {
    257   return new DebugRequest(cmd_line).JSONRequest();
    258 };
    259 
    260 
    261 function DebugRequest(cmd_line) {
    262   // If the very first character is a { assume that a JSON request have been
    263   // entered as a command. Converting that to a JSON request is trivial.
    264   if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') {
    265     this.request_ = cmd_line;
    266     return;
    267   }
    268 
    269   // Trim string for leading and trailing whitespace.
    270   cmd_line = cmd_line.replace(/^\s+|\s+$/g, '');
    271 
    272   // Find the command.
    273   var pos = cmd_line.indexOf(' ');
    274   var cmd;
    275   var args;
    276   if (pos == -1) {
    277     cmd = cmd_line;
    278     args = '';
    279   } else {
    280     cmd = cmd_line.slice(0, pos);
    281     args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, '');
    282   }
    283 
    284   // Switch on command.
    285   switch (cmd) {
    286     case 'continue':
    287     case 'c':
    288       this.request_ = this.continueCommandToJSONRequest_(args);
    289       break;
    290 
    291     case 'step':
    292     case 's':
    293       this.request_ = this.stepCommandToJSONRequest_(args);
    294       break;
    295 
    296     case 'backtrace':
    297     case 'bt':
    298       this.request_ = this.backtraceCommandToJSONRequest_(args);
    299       break;
    300       
    301     case 'frame':
    302     case 'f':
    303       this.request_ = this.frameCommandToJSONRequest_(args);
    304       break;
    305       
    306     case 'scopes':
    307       this.request_ = this.scopesCommandToJSONRequest_(args);
    308       break;
    309       
    310     case 'scope':
    311       this.request_ = this.scopeCommandToJSONRequest_(args);
    312       break;
    313       
    314     case 'print':
    315     case 'p':
    316       this.request_ = this.printCommandToJSONRequest_(args);
    317       break;
    318 
    319     case 'dir':
    320       this.request_ = this.dirCommandToJSONRequest_(args);
    321       break;
    322 
    323     case 'references':
    324       this.request_ = this.referencesCommandToJSONRequest_(args);
    325       break;
    326 
    327     case 'instances':
    328       this.request_ = this.instancesCommandToJSONRequest_(args);
    329       break;
    330 
    331     case 'source':
    332       this.request_ = this.sourceCommandToJSONRequest_(args);
    333       break;
    334       
    335     case 'scripts':
    336       this.request_ = this.scriptsCommandToJSONRequest_(args);
    337       break;
    338       
    339     case 'break':
    340     case 'b':
    341       this.request_ = this.breakCommandToJSONRequest_(args);
    342       break;
    343       
    344     case 'clear':
    345       this.request_ = this.clearCommandToJSONRequest_(args);
    346       break;
    347 
    348     case 'threads':
    349       this.request_ = this.threadsCommandToJSONRequest_(args);
    350       break;
    351 
    352     case 'trace':
    353       // Return undefined to indicate command handled internally (no JSON).
    354       this.request_ = void 0;
    355       this.traceCommand_(args);
    356       break;
    357 
    358     case 'help':
    359     case '?':
    360       this.helpCommand_(args);
    361       // Return undefined to indicate command handled internally (no JSON).
    362       this.request_ = void 0;
    363       break;
    364 
    365     default:
    366       throw new Error('Unknown command "' + cmd + '"');
    367   }
    368   
    369   last_cmd = cmd;
    370 }
    371 
    372 DebugRequest.prototype.JSONRequest = function() {
    373   return this.request_;
    374 }
    375 
    376 
    377 function RequestPacket(command) {
    378   this.seq = 0;
    379   this.type = 'request';
    380   this.command = command;
    381 }
    382 
    383 
    384 RequestPacket.prototype.toJSONProtocol = function() {
    385   // Encode the protocol header.
    386   var json = '{';
    387   json += '"seq":' + this.seq;
    388   json += ',"type":"' + this.type + '"';
    389   if (this.command) {
    390     json += ',"command":' + StringToJSON_(this.command);
    391   }
    392   if (this.arguments) {
    393     json += ',"arguments":';
    394     // Encode the arguments part.
    395     if (this.arguments.toJSONProtocol) {
    396       json += this.arguments.toJSONProtocol()
    397     } else {
    398       json += SimpleObjectToJSON_(this.arguments);
    399     }
    400   }
    401   json += '}';
    402   return json;
    403 }
    404 
    405 
    406 DebugRequest.prototype.createRequest = function(command) {
    407   return new RequestPacket(command);
    408 };
    409 
    410 
    411 // Create a JSON request for the evaluation command.
    412 DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
    413   // Global varaible used to store whether a handle was requested.
    414   lookup_handle = null;
    415   // Check if the expression is a handle id in the form #<handle>#.
    416   var handle_match = expression.match(/^#([0-9]*)#$/);
    417   if (handle_match) {
    418     // Remember the handle requested in a global variable.
    419     lookup_handle = parseInt(handle_match[1]);
    420     // Build a lookup request.
    421     var request = this.createRequest('lookup');
    422     request.arguments = {};
    423     request.arguments.handles = [ lookup_handle ];
    424     return request.toJSONProtocol();
    425   } else {
    426     // Build an evaluate request.
    427     var request = this.createRequest('evaluate');
    428     request.arguments = {};
    429     request.arguments.expression = expression;
    430     // Request a global evaluation if there is no current frame.
    431     if (Debug.State.currentFrame == kNoFrame) {
    432       request.arguments.global = true;
    433     }
    434     return request.toJSONProtocol();
    435   }
    436 };
    437 
    438 
    439 // Create a JSON request for the references/instances command.
    440 DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
    441   // Build a references request.
    442   var handle_match = handle.match(/^#([0-9]*)#$/);
    443   if (handle_match) {
    444     var request = this.createRequest('references');
    445     request.arguments = {};
    446     request.arguments.type = type;
    447     request.arguments.handle = parseInt(handle_match[1]);
    448     return request.toJSONProtocol();
    449   } else {
    450     throw new Error('Invalid object id.');
    451   }
    452 };
    453 
    454 
    455 // Create a JSON request for the continue command.
    456 DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
    457   var request = this.createRequest('continue');
    458   return request.toJSONProtocol();
    459 };
    460 
    461 
    462 // Create a JSON request for the step command.
    463 DebugRequest.prototype.stepCommandToJSONRequest_ = function(args) {
    464   // Requesting a step is through the continue command with additional
    465   // arguments.
    466   var request = this.createRequest('continue');
    467   request.arguments = {};
    468 
    469   // Process arguments if any.
    470   if (args && args.length > 0) {
    471     args = args.split(/\s*[ ]+\s*/g);
    472 
    473     if (args.length > 2) {
    474       throw new Error('Invalid step arguments.');
    475     }
    476 
    477     if (args.length > 0) {
    478       // Get step count argument if any.
    479       if (args.length == 2) {
    480         var stepcount = parseInt(args[1]);
    481         if (isNaN(stepcount) || stepcount <= 0) {
    482           throw new Error('Invalid step count argument "' + args[0] + '".');
    483         }
    484         request.arguments.stepcount = stepcount;
    485       }
    486 
    487       // Get the step action.
    488       switch (args[0]) {
    489         case 'in':
    490         case 'i':
    491           request.arguments.stepaction = 'in';
    492           break;
    493           
    494         case 'min':
    495         case 'm':
    496           request.arguments.stepaction = 'min';
    497           break;
    498           
    499         case 'next':
    500         case 'n':
    501           request.arguments.stepaction = 'next';
    502           break;
    503           
    504         case 'out':
    505         case 'o':
    506           request.arguments.stepaction = 'out';
    507           break;
    508           
    509         default:
    510           throw new Error('Invalid step argument "' + args[0] + '".');
    511       }
    512     }
    513   } else {
    514     // Default is step next.
    515     request.arguments.stepaction = 'next';
    516   }
    517 
    518   return request.toJSONProtocol();
    519 };
    520 
    521 
    522 // Create a JSON request for the backtrace command.
    523 DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) {
    524   // Build a backtrace request from the text command.
    525   var request = this.createRequest('backtrace');
    526   
    527   // Default is to show top 10 frames.
    528   request.arguments = {};
    529   request.arguments.fromFrame = 0;
    530   request.arguments.toFrame = 10;
    531 
    532   args = args.split(/\s*[ ]+\s*/g);
    533   if (args.length == 1 && args[0].length > 0) {
    534     var frameCount = parseInt(args[0]);
    535     if (frameCount > 0) {
    536       // Show top frames.
    537       request.arguments.fromFrame = 0;
    538       request.arguments.toFrame = frameCount;
    539     } else {
    540       // Show bottom frames.
    541       request.arguments.fromFrame = 0;
    542       request.arguments.toFrame = -frameCount;
    543       request.arguments.bottom = true;
    544     }
    545   } else if (args.length == 2) {
    546     var fromFrame = parseInt(args[0]);
    547     var toFrame = parseInt(args[1]);
    548     if (isNaN(fromFrame) || fromFrame < 0) {
    549       throw new Error('Invalid start frame argument "' + args[0] + '".');
    550     }
    551     if (isNaN(toFrame) || toFrame < 0) {
    552       throw new Error('Invalid end frame argument "' + args[1] + '".');
    553     }
    554     if (fromFrame > toFrame) {
    555       throw new Error('Invalid arguments start frame cannot be larger ' +
    556                       'than end frame.');
    557     }
    558     // Show frame range.
    559     request.arguments.fromFrame = fromFrame;
    560     request.arguments.toFrame = toFrame + 1;
    561   } else if (args.length > 2) {
    562     throw new Error('Invalid backtrace arguments.');
    563   }
    564 
    565   return request.toJSONProtocol();
    566 };
    567 
    568 
    569 // Create a JSON request for the frame command.
    570 DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) {
    571   // Build a frame request from the text command.
    572   var request = this.createRequest('frame');
    573   args = args.split(/\s*[ ]+\s*/g);
    574   if (args.length > 0 && args[0].length > 0) {
    575     request.arguments = {};
    576     request.arguments.number = args[0];
    577   }
    578   return request.toJSONProtocol();
    579 };
    580 
    581 
    582 // Create a JSON request for the scopes command.
    583 DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) {
    584   // Build a scopes request from the text command.
    585   var request = this.createRequest('scopes');
    586   return request.toJSONProtocol();
    587 };
    588 
    589 
    590 // Create a JSON request for the scope command.
    591 DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) {
    592   // Build a scope request from the text command.
    593   var request = this.createRequest('scope');
    594   args = args.split(/\s*[ ]+\s*/g);
    595   if (args.length > 0 && args[0].length > 0) {
    596     request.arguments = {};
    597     request.arguments.number = args[0];
    598   }
    599   return request.toJSONProtocol();
    600 };
    601 
    602 
    603 // Create a JSON request for the print command.
    604 DebugRequest.prototype.printCommandToJSONRequest_ = function(args) {
    605   // Build an evaluate request from the text command.
    606   if (args.length == 0) {
    607     throw new Error('Missing expression.');
    608   }
    609   return this.makeEvaluateJSONRequest_(args);
    610 };
    611 
    612 
    613 // Create a JSON request for the dir command.
    614 DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
    615   // Build an evaluate request from the text command.
    616   if (args.length == 0) {
    617     throw new Error('Missing expression.');
    618   }
    619   return this.makeEvaluateJSONRequest_(args);
    620 };
    621 
    622 
    623 // Create a JSON request for the references command.
    624 DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
    625   // Build an evaluate request from the text command.
    626   if (args.length == 0) {
    627     throw new Error('Missing object id.');
    628   }
    629   
    630   return this.makeReferencesJSONRequest_(args, 'referencedBy');
    631 };
    632 
    633 
    634 // Create a JSON request for the instances command.
    635 DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
    636   // Build an evaluate request from the text command.
    637   if (args.length == 0) {
    638     throw new Error('Missing object id.');
    639   }
    640   
    641   // Build a references request.
    642   return this.makeReferencesJSONRequest_(args, 'constructedBy');
    643 };
    644 
    645 
    646 // Create a JSON request for the source command.
    647 DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
    648   // Build a evaluate request from the text command.
    649   var request = this.createRequest('source');
    650 
    651   // Default is ten lines starting five lines before the current location.
    652   var from = Debug.State.currentSourceLine - 5;
    653   var lines = 10;
    654 
    655   // Parse the arguments.
    656   args = args.split(/\s*[ ]+\s*/g);
    657   if (args.length > 1 && args[0].length > 0 && args[1].length > 0) {
    658     from = parseInt(args[0]) - 1;
    659     lines = parseInt(args[1]);
    660   } else if (args.length > 0 && args[0].length > 0) {
    661     from = parseInt(args[0]) - 1;
    662   }
    663 
    664   if (from < 0) from = 0;
    665   if (lines < 0) lines = 10;
    666 
    667   // Request source arround current source location.
    668   request.arguments = {};
    669   request.arguments.fromLine = from;
    670   request.arguments.toLine = from + lines;
    671 
    672   return request.toJSONProtocol();
    673 };
    674 
    675 
    676 // Create a JSON request for the scripts command.
    677 DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) {
    678   // Build a evaluate request from the text command.
    679   var request = this.createRequest('scripts');
    680 
    681   // Process arguments if any.
    682   if (args && args.length > 0) {
    683     args = args.split(/\s*[ ]+\s*/g);
    684 
    685     if (args.length > 1) {
    686       throw new Error('Invalid scripts arguments.');
    687     }
    688 
    689     request.arguments = {};
    690     switch (args[0]) {
    691       case 'natives':
    692         request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native);
    693         break;
    694         
    695       case 'extensions':
    696         request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension);
    697         break;
    698         
    699       case 'all':
    700         request.arguments.types =
    701             ScriptTypeFlag(Debug.ScriptType.Normal) |
    702             ScriptTypeFlag(Debug.ScriptType.Native) |
    703             ScriptTypeFlag(Debug.ScriptType.Extension);
    704         break;
    705         
    706       default:
    707         throw new Error('Invalid argument "' + args[0] + '".');
    708     }
    709   }
    710 
    711   return request.toJSONProtocol();
    712 };
    713 
    714 
    715 // Create a JSON request for the break command.
    716 DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) {
    717   // Build a evaluate request from the text command.
    718   var request = this.createRequest('setbreakpoint');
    719 
    720   // Process arguments if any.
    721   if (args && args.length > 0) {
    722     var target = args;
    723     var type = 'function';
    724     var line;
    725     var column;
    726     var condition;
    727     var pos;
    728 
    729     // Check for breakpoint condition.
    730     pos = args.indexOf(' ');
    731     if (pos > 0) {
    732       target = args.substring(0, pos);
    733       condition = args.substring(pos + 1, args.length);
    734     }
    735 
    736     // Check for script breakpoint (name:line[:column]). If no ':' in break
    737     // specification it is considered a function break point.
    738     pos = target.indexOf(':');
    739     if (pos > 0) {
    740       type = 'script';
    741       var tmp = target.substring(pos + 1, target.length);
    742       target = target.substring(0, pos);
    743       
    744       // Check for both line and column.
    745       pos = tmp.indexOf(':');
    746       if (pos > 0) {
    747         column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1;
    748         line = parseInt(tmp.substring(0, pos)) - 1;
    749       } else {
    750         line = parseInt(tmp) - 1;
    751       }
    752     } else if (target[0] == '#' && target[target.length - 1] == '#') {
    753       type = 'handle';
    754       target = target.substring(1, target.length - 1);
    755     } else {
    756       type = 'function';
    757     }
    758   
    759     request.arguments = {};
    760     request.arguments.type = type;
    761     request.arguments.target = target;
    762     request.arguments.line = line;
    763     request.arguments.column = column;
    764     request.arguments.condition = condition;
    765   } else {
    766     throw new Error('Invalid break arguments.');
    767   }
    768 
    769   return request.toJSONProtocol();
    770 };
    771 
    772 
    773 // Create a JSON request for the clear command.
    774 DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) {
    775   // Build a evaluate request from the text command.
    776   var request = this.createRequest('clearbreakpoint');
    777 
    778   // Process arguments if any.
    779   if (args && args.length > 0) {
    780     request.arguments = {};
    781     request.arguments.breakpoint = parseInt(args);
    782   } else {
    783     throw new Error('Invalid break arguments.');
    784   }
    785 
    786   return request.toJSONProtocol();
    787 };
    788 
    789 
    790 // Create a JSON request for the threads command.
    791 DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
    792   // Build a threads request from the text command.
    793   var request = this.createRequest('threads');
    794   return request.toJSONProtocol();
    795 };
    796 
    797 
    798 // Handle the trace command.
    799 DebugRequest.prototype.traceCommand_ = function(args) {
    800   // Process arguments.
    801   if (args && args.length > 0) {
    802     if (args == 'compile') {
    803       trace_compile = !trace_compile;
    804       print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off'));
    805     } else {
    806       throw new Error('Invalid trace arguments.');
    807     }
    808   } else {
    809     throw new Error('Invalid trace arguments.');
    810   }
    811 }
    812 
    813 // Handle the help command.
    814 DebugRequest.prototype.helpCommand_ = function(args) {
    815   // Help os quite simple.
    816   if (args && args.length > 0) {
    817     print('warning: arguments to \'help\' are ignored');
    818   }
    819 
    820   print('break location [condition]');
    821   print('  break on named function: location is a function name');
    822   print('  break on function: location is #<id>#');
    823   print('  break on script position: location is name:line[:column]');
    824   print('clear <breakpoint #>');
    825   print('backtrace [n] | [-n] | [from to]');
    826   print('frame <frame #>');
    827   print('scopes');
    828   print('scope <scope #>');
    829   print('step [in | next | out| min [step count]]');
    830   print('print <expression>');
    831   print('dir <expression>');
    832   print('source [from line [num lines]]');
    833   print('scripts');
    834   print('continue');
    835   print('trace compile');
    836   print('help');
    837 }
    838 
    839 
    840 function formatHandleReference_(value) {
    841   if (value.handle() >= 0) {
    842     return '#' + value.handle() + '#';
    843   } else {
    844     return '#Transient#';
    845   }
    846 }
    847 
    848 
    849 function formatObject_(value, include_properties) {
    850   var result = '';
    851   result += formatHandleReference_(value);
    852   result += ', type: object'
    853   result += ', constructor ';
    854   var ctor = value.constructorFunctionValue();
    855   result += formatHandleReference_(ctor);
    856   result += ', __proto__ ';
    857   var proto = value.protoObjectValue();
    858   result += formatHandleReference_(proto);
    859   result += ', ';
    860   result += value.propertyCount();
    861   result +=  ' properties.';
    862   if (include_properties) {
    863     result +=  '\n';
    864     for (var i = 0; i < value.propertyCount(); i++) {
    865       result += '  ';
    866       result += value.propertyName(i);
    867       result += ': ';
    868       var property_value = value.propertyValue(i);
    869       if (property_value instanceof ProtocolReference) {
    870         result += '<no type>';
    871       } else {
    872         if (property_value && property_value.type()) {
    873           result += property_value.type();
    874         } else {
    875           result += '<no type>';
    876         }
    877       }
    878       result += ' ';
    879       result += formatHandleReference_(property_value);
    880       result += '\n';
    881     }
    882   }
    883   return result;
    884 }
    885 
    886 
    887 function formatScope_(scope) {
    888   var result = '';
    889   var index = scope.index;
    890   result += '#' + (index <= 9 ? '0' : '') + index;
    891   result += ' ';
    892   switch (scope.type) {
    893     case Debug.ScopeType.Global:
    894       result += 'Global, ';
    895       result += '#' + scope.object.ref + '#';
    896       break;
    897     case Debug.ScopeType.Local:
    898       result += 'Local';
    899       break;
    900     case Debug.ScopeType.With:
    901       result += 'With, ';
    902       result += '#' + scope.object.ref + '#';
    903       break;
    904     case Debug.ScopeType.Catch:
    905       result += 'Catch, ';
    906       result += '#' + scope.object.ref + '#';
    907       break;
    908     case Debug.ScopeType.Closure:
    909       result += 'Closure';
    910       break;
    911     default:
    912       result += 'UNKNOWN';
    913   }
    914   return result;
    915 }
    916 
    917 
    918 // Convert a JSON response to text for display in a text based debugger.
    919 function DebugResponseDetails(response) {
    920   details = {text:'', running:false}
    921 
    922   try {
    923     if (!response.success()) {
    924       details.text = response.message();
    925       return details;
    926     }
    927 
    928     // Get the running state.
    929     details.running = response.running();
    930 
    931     var body = response.body();
    932     var result = '';
    933     switch (response.command()) {
    934       case 'setbreakpoint':
    935         result = 'set breakpoint #';
    936         result += body.breakpoint;
    937         details.text = result;
    938         break;
    939         
    940       case 'clearbreakpoint':
    941         result = 'cleared breakpoint #';
    942         result += body.breakpoint;
    943         details.text = result;
    944         break;
    945         
    946       case 'backtrace':
    947         if (body.totalFrames == 0) {
    948           result = '(empty stack)';
    949         } else {
    950           var result = 'Frames #' + body.fromFrame + ' to #' +
    951               (body.toFrame - 1) + ' of ' + body.totalFrames + '\n';
    952           for (i = 0; i < body.frames.length; i++) {
    953             if (i != 0) result += '\n';
    954             result += body.frames[i].text;
    955           }
    956         }
    957         details.text = result;
    958         break;
    959         
    960       case 'frame':
    961         details.text = SourceUnderline(body.sourceLineText,
    962                                        body.column);
    963         Debug.State.currentSourceLine = body.line;
    964         Debug.State.currentFrame = body.index;
    965         break;
    966         
    967       case 'scopes':
    968         if (body.totalScopes == 0) {
    969           result = '(no scopes)';
    970         } else {
    971           result = 'Scopes #' + body.fromScope + ' to #' +
    972                    (body.toScope - 1) + ' of ' + body.totalScopes + '\n';
    973           for (i = 0; i < body.scopes.length; i++) {
    974             if (i != 0) {
    975               result += '\n';
    976             }
    977             result += formatScope_(body.scopes[i]);
    978           }
    979         }
    980         details.text = result;
    981         break;
    982 
    983       case 'scope':
    984         result += formatScope_(body);
    985         result += '\n';
    986         var scope_object_value = response.lookup(body.object.ref);
    987         result += formatObject_(scope_object_value, true);
    988         details.text = result;
    989         break;
    990       
    991       case 'evaluate':
    992       case 'lookup':
    993         if (last_cmd == 'p' || last_cmd == 'print') {
    994           result = body.text;
    995         } else {
    996           var value;
    997           if (lookup_handle) {
    998             value = response.bodyValue(lookup_handle);
    999           } else {
   1000             value = response.bodyValue();
   1001           }
   1002           if (value.isObject()) {
   1003             result += formatObject_(value, true);
   1004           } else {
   1005             result += 'type: ';
   1006             result += value.type();
   1007             if (!value.isUndefined() && !value.isNull()) {
   1008               result += ', ';
   1009               if (value.isString()) {
   1010                 result += '"';
   1011               }
   1012               result += value.value();
   1013               if (value.isString()) {
   1014                 result += '"';
   1015               }
   1016             }
   1017             result += '\n';
   1018           }
   1019         }
   1020         details.text = result;
   1021         break;
   1022 
   1023       case 'references':
   1024         var count = body.length;
   1025         result += 'found ' + count + ' objects';
   1026         result += '\n';
   1027         for (var i = 0; i < count; i++) {
   1028           var value = response.bodyValue(i);
   1029           result += formatObject_(value, false);
   1030           result += '\n';
   1031         }
   1032         details.text = result;
   1033         break;
   1034         
   1035       case 'source':
   1036         // Get the source from the response.
   1037         var source = body.source;
   1038         var from_line = body.fromLine + 1;
   1039         var lines = source.split('\n');
   1040         var maxdigits = 1 + Math.floor(log10(from_line + lines.length));
   1041         if (maxdigits < 3) {
   1042           maxdigits = 3;
   1043         }
   1044         var result = '';
   1045         for (var num = 0; num < lines.length; num++) {
   1046           // Check if there's an extra newline at the end.
   1047           if (num == (lines.length - 1) && lines[num].length == 0) {
   1048             break;
   1049           }
   1050 
   1051           var current_line = from_line + num;
   1052           spacer = maxdigits - (1 + Math.floor(log10(current_line)));
   1053           if (current_line == Debug.State.currentSourceLine + 1) {
   1054             for (var i = 0; i < maxdigits; i++) {
   1055               result += '>';
   1056             }
   1057             result += '  ';
   1058           } else {
   1059             for (var i = 0; i < spacer; i++) {
   1060               result += ' ';
   1061             }
   1062             result += current_line + ': ';
   1063           }
   1064           result += lines[num];
   1065           result += '\n';
   1066         }
   1067         details.text = result;
   1068         break;
   1069         
   1070       case 'scripts':
   1071         var result = '';
   1072         for (i = 0; i < body.length; i++) {
   1073           if (i != 0) result += '\n';
   1074           if (body[i].id) {
   1075             result += body[i].id;
   1076           } else {
   1077             result += '[no id]';
   1078           }
   1079           result += ', ';
   1080           if (body[i].name) {
   1081             result += body[i].name;
   1082           } else {
   1083             if (body[i].compilationType == Debug.ScriptCompilationType.Eval) {
   1084               result += 'eval from ';
   1085               var script_value = response.lookup(body[i].evalFromScript.ref);
   1086               result += ' ' + script_value.field('name');
   1087               result += ':' + (body[i].evalFromLocation.line + 1);
   1088               result += ':' + body[i].evalFromLocation.column;
   1089             } else if (body[i].compilationType ==
   1090                        Debug.ScriptCompilationType.JSON) {
   1091               result += 'JSON ';
   1092             } else {  // body[i].compilation == Debug.ScriptCompilationType.Host
   1093               result += '[unnamed] ';
   1094             }
   1095           }
   1096           result += ' (lines: ';
   1097           result += body[i].lineCount;
   1098           result += ', length: ';
   1099           result += body[i].sourceLength;
   1100           if (body[i].type == Debug.ScriptType.Native) {
   1101             result += ', native';
   1102           } else if (body[i].type == Debug.ScriptType.Extension) {
   1103             result += ', extension';
   1104           }
   1105           result += '), [';
   1106           var sourceStart = body[i].sourceStart;
   1107           if (sourceStart.length > 40) {
   1108             sourceStart = sourceStart.substring(0, 37) + '...';
   1109           }
   1110           result += sourceStart;
   1111           result += ']';
   1112         }
   1113         details.text = result;
   1114         break;
   1115 
   1116       case 'threads':
   1117         var result = 'Active V8 threads: ' + body.totalThreads + '\n';
   1118         body.threads.sort(function(a, b) { return a.id - b.id; });
   1119         for (i = 0; i < body.threads.length; i++) {
   1120           result += body.threads[i].current ? '*' : ' ';
   1121           result += ' ';
   1122           result += body.threads[i].id;
   1123           result += '\n';
   1124         }
   1125         details.text = result;
   1126         break;
   1127 
   1128       case 'continue':
   1129         details.text = "(running)";
   1130         break;
   1131         
   1132       default:
   1133         details.text =
   1134             'Response for unknown command \'' + response.command + '\'' +
   1135             ' (' + json_response + ')';
   1136     }
   1137   } catch (e) {
   1138     details.text = 'Error: "' + e + '" formatting response';
   1139   }
   1140   
   1141   return details;
   1142 };
   1143 
   1144 
   1145 /**
   1146  * Protocol packages send from the debugger.
   1147  * @param {string} json - raw protocol packet as JSON string.
   1148  * @constructor
   1149  */
   1150 function ProtocolPackage(json) {
   1151   this.packet_ = JSON.parse(json);
   1152   this.refs_ = [];
   1153   if (this.packet_.refs) {
   1154     for (var i = 0; i < this.packet_.refs.length; i++) {
   1155       this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
   1156     }
   1157   }
   1158 }
   1159 
   1160 
   1161 /**
   1162  * Get the packet type.
   1163  * @return {String} the packet type
   1164  */
   1165 ProtocolPackage.prototype.type = function() {
   1166   return this.packet_.type;
   1167 }
   1168 
   1169 
   1170 /**
   1171  * Get the packet event.
   1172  * @return {Object} the packet event
   1173  */
   1174 ProtocolPackage.prototype.event = function() {
   1175   return this.packet_.event;
   1176 }
   1177 
   1178 
   1179 /**
   1180  * Get the packet request sequence.
   1181  * @return {number} the packet request sequence
   1182  */
   1183 ProtocolPackage.prototype.requestSeq = function() {
   1184   return this.packet_.request_seq;
   1185 }
   1186 
   1187 
   1188 /**
   1189  * Get the packet request sequence.
   1190  * @return {number} the packet request sequence
   1191  */
   1192 ProtocolPackage.prototype.running = function() {
   1193   return this.packet_.running ? true : false;
   1194 }
   1195 
   1196 
   1197 ProtocolPackage.prototype.success = function() {
   1198   return this.packet_.success ? true : false;
   1199 }
   1200 
   1201 
   1202 ProtocolPackage.prototype.message = function() {
   1203   return this.packet_.message;
   1204 }
   1205 
   1206 
   1207 ProtocolPackage.prototype.command = function() {
   1208   return this.packet_.command;
   1209 }
   1210 
   1211 
   1212 ProtocolPackage.prototype.body = function() {
   1213   return this.packet_.body;
   1214 }
   1215 
   1216 
   1217 ProtocolPackage.prototype.bodyValue = function(index) {
   1218   if (index != null) {
   1219     return new ProtocolValue(this.packet_.body[index], this);
   1220   } else {
   1221     return new ProtocolValue(this.packet_.body, this);
   1222   }
   1223 }
   1224 
   1225 
   1226 ProtocolPackage.prototype.body = function() {
   1227   return this.packet_.body;
   1228 }
   1229 
   1230 
   1231 ProtocolPackage.prototype.lookup = function(handle) {
   1232   var value = this.refs_[handle];
   1233   if (value) {
   1234     return new ProtocolValue(value, this);
   1235   } else {
   1236     return new ProtocolReference(handle);
   1237   }
   1238 }
   1239 
   1240 
   1241 function ProtocolValue(value, packet) {
   1242   this.value_ = value;
   1243   this.packet_ = packet;
   1244 }
   1245 
   1246 
   1247 /**
   1248  * Get the value type.
   1249  * @return {String} the value type
   1250  */
   1251 ProtocolValue.prototype.type = function() {
   1252   return this.value_.type;
   1253 }
   1254 
   1255 
   1256 /**
   1257  * Get a metadata field from a protocol value. 
   1258  * @return {Object} the metadata field value
   1259  */
   1260 ProtocolValue.prototype.field = function(name) {
   1261   return this.value_[name];
   1262 }
   1263 
   1264 
   1265 /**
   1266  * Check is the value is a primitive value.
   1267  * @return {boolean} true if the value is primitive
   1268  */
   1269 ProtocolValue.prototype.isPrimitive = function() {
   1270   return this.isUndefined() || this.isNull() || this.isBoolean() ||
   1271          this.isNumber() || this.isString();
   1272 }
   1273 
   1274 
   1275 /**
   1276  * Get the object handle.
   1277  * @return {number} the value handle
   1278  */
   1279 ProtocolValue.prototype.handle = function() {
   1280   return this.value_.handle;
   1281 }
   1282 
   1283 
   1284 /**
   1285  * Check is the value is undefined.
   1286  * @return {boolean} true if the value is undefined
   1287  */
   1288 ProtocolValue.prototype.isUndefined = function() {
   1289   return this.value_.type == 'undefined';
   1290 }
   1291 
   1292 
   1293 /**
   1294  * Check is the value is null.
   1295  * @return {boolean} true if the value is null
   1296  */
   1297 ProtocolValue.prototype.isNull = function() {
   1298   return this.value_.type == 'null';
   1299 }
   1300 
   1301 
   1302 /**
   1303  * Check is the value is a boolean.
   1304  * @return {boolean} true if the value is a boolean
   1305  */
   1306 ProtocolValue.prototype.isBoolean = function() {
   1307   return this.value_.type == 'boolean';
   1308 }
   1309 
   1310 
   1311 /**
   1312  * Check is the value is a number.
   1313  * @return {boolean} true if the value is a number
   1314  */
   1315 ProtocolValue.prototype.isNumber = function() {
   1316   return this.value_.type == 'number';
   1317 }
   1318 
   1319 
   1320 /**
   1321  * Check is the value is a string.
   1322  * @return {boolean} true if the value is a string
   1323  */
   1324 ProtocolValue.prototype.isString = function() {
   1325   return this.value_.type == 'string';
   1326 }
   1327 
   1328 
   1329 /**
   1330  * Check is the value is an object.
   1331  * @return {boolean} true if the value is an object
   1332  */
   1333 ProtocolValue.prototype.isObject = function() {
   1334   return this.value_.type == 'object' || this.value_.type == 'function' ||
   1335          this.value_.type == 'error' || this.value_.type == 'regexp';
   1336 }
   1337 
   1338 
   1339 /**
   1340  * Get the constructor function
   1341  * @return {ProtocolValue} constructor function
   1342  */
   1343 ProtocolValue.prototype.constructorFunctionValue = function() {
   1344   var ctor = this.value_.constructorFunction;
   1345   return this.packet_.lookup(ctor.ref);
   1346 }
   1347 
   1348 
   1349 /**
   1350  * Get the __proto__ value
   1351  * @return {ProtocolValue} __proto__ value
   1352  */
   1353 ProtocolValue.prototype.protoObjectValue = function() {
   1354   var proto = this.value_.protoObject;
   1355   return this.packet_.lookup(proto.ref);
   1356 }
   1357 
   1358 
   1359 /**
   1360  * Get the number og properties.
   1361  * @return {number} the number of properties
   1362  */
   1363 ProtocolValue.prototype.propertyCount = function() {
   1364   return this.value_.properties ? this.value_.properties.length : 0;
   1365 }
   1366 
   1367 
   1368 /**
   1369  * Get the specified property name.
   1370  * @return {string} property name
   1371  */
   1372 ProtocolValue.prototype.propertyName = function(index) {
   1373   var property = this.value_.properties[index];
   1374   return property.name;
   1375 }
   1376 
   1377 
   1378 /**
   1379  * Return index for the property name.
   1380  * @param name The property name to look for
   1381  * @return {number} index for the property name
   1382  */
   1383 ProtocolValue.prototype.propertyIndex = function(name) {
   1384   for (var i = 0; i < this.propertyCount(); i++) {
   1385     if (this.value_.properties[i].name == name) {
   1386       return i;
   1387     }
   1388   }
   1389   return null;
   1390 }
   1391 
   1392 
   1393 /**
   1394  * Get the specified property value.
   1395  * @return {ProtocolValue} property value
   1396  */
   1397 ProtocolValue.prototype.propertyValue = function(index) {
   1398   var property = this.value_.properties[index];
   1399   return this.packet_.lookup(property.ref);
   1400 }
   1401 
   1402 
   1403 /**
   1404  * Check is the value is a string.
   1405  * @return {boolean} true if the value is a string
   1406  */
   1407 ProtocolValue.prototype.value = function() {
   1408   return this.value_.value;
   1409 }
   1410 
   1411 
   1412 function ProtocolReference(handle) {
   1413   this.handle_ = handle;
   1414 }
   1415 
   1416 
   1417 ProtocolReference.prototype.handle = function() {
   1418   return this.handle_;
   1419 }
   1420 
   1421 
   1422 function MakeJSONPair_(name, value) {
   1423   return '"' + name + '":' + value;
   1424 }
   1425 
   1426 
   1427 function ArrayToJSONObject_(content) {
   1428   return '{' + content.join(',') + '}';
   1429 }
   1430 
   1431 
   1432 function ArrayToJSONArray_(content) {
   1433   return '[' + content.join(',') + ']';
   1434 }
   1435 
   1436 
   1437 function BooleanToJSON_(value) {
   1438   return String(value); 
   1439 }
   1440 
   1441 
   1442 function NumberToJSON_(value) {
   1443   return String(value); 
   1444 }
   1445 
   1446 
   1447 // Mapping of some control characters to avoid the \uXXXX syntax for most
   1448 // commonly used control cahracters.
   1449 const ctrlCharMap_ = {
   1450   '\b': '\\b',
   1451   '\t': '\\t',
   1452   '\n': '\\n',
   1453   '\f': '\\f',
   1454   '\r': '\\r',
   1455   '"' : '\\"',
   1456   '\\': '\\\\'
   1457 };
   1458 
   1459 
   1460 // Regular expression testing for ", \ and control characters (0x00 - 0x1F).
   1461 const ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]');
   1462 
   1463 
   1464 // Regular expression matching ", \ and control characters (0x00 - 0x1F)
   1465 // globally.
   1466 const ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g');
   1467 
   1468 
   1469 /**
   1470  * Convert a String to its JSON representation (see http://www.json.org/). To
   1471  * avoid depending on the String object this method calls the functions in
   1472  * string.js directly and not through the value.
   1473  * @param {String} value The String value to format as JSON
   1474  * @return {string} JSON formatted String value
   1475  */
   1476 function StringToJSON_(value) {
   1477   // Check for" , \ and control characters (0x00 - 0x1F). No need to call
   1478   // RegExpTest as ctrlchar is constructed using RegExp.
   1479   if (ctrlCharTest_.test(value)) {
   1480     // Replace ", \ and control characters (0x00 - 0x1F).
   1481     return '"' +
   1482       value.replace(ctrlCharMatch_, function (char) {
   1483         // Use charmap if possible.
   1484         var mapped = ctrlCharMap_[char];
   1485         if (mapped) return mapped;
   1486         mapped = char.charCodeAt();
   1487         // Convert control character to unicode escape sequence.
   1488         return '\\u00' +
   1489           '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) +
   1490           '0' // TODO %NumberToRadixString(mapped % 16, 16);
   1491       })
   1492     + '"';
   1493   }
   1494 
   1495   // Simple string with no special characters.
   1496   return '"' + value + '"';
   1497 }
   1498 
   1499 
   1500 /**
   1501  * Convert a Date to ISO 8601 format. To avoid depending on the Date object
   1502  * this method calls the functions in date.js directly and not through the
   1503  * value.
   1504  * @param {Date} value The Date value to format as JSON
   1505  * @return {string} JSON formatted Date value
   1506  */
   1507 function DateToISO8601_(value) {
   1508   function f(n) {
   1509     return n < 10 ? '0' + n : n;
   1510   }
   1511   function g(n) {
   1512     return n < 10 ? '00' + n : n < 100 ? '0' + n : n;
   1513   }
   1514   return builtins.GetUTCFullYearFrom(value)         + '-' +
   1515           f(builtins.GetUTCMonthFrom(value) + 1)    + '-' +
   1516           f(builtins.GetUTCDateFrom(value))         + 'T' +
   1517           f(builtins.GetUTCHoursFrom(value))        + ':' +
   1518           f(builtins.GetUTCMinutesFrom(value))      + ':' +
   1519           f(builtins.GetUTCSecondsFrom(value))      + '.' +
   1520           g(builtins.GetUTCMillisecondsFrom(value)) + 'Z';
   1521 }
   1522 
   1523 
   1524 /**
   1525  * Convert a Date to ISO 8601 format. To avoid depending on the Date object
   1526  * this method calls the functions in date.js directly and not through the
   1527  * value.
   1528  * @param {Date} value The Date value to format as JSON
   1529  * @return {string} JSON formatted Date value
   1530  */
   1531 function DateToJSON_(value) {
   1532   return '"' + DateToISO8601_(value) + '"';
   1533 }
   1534 
   1535 
   1536 /**
   1537  * Convert an Object to its JSON representation (see http://www.json.org/).
   1538  * This implementation simply runs through all string property names and adds
   1539  * each property to the JSON representation for some predefined types. For type
   1540  * "object" the function calls itself recursively unless the object has the
   1541  * function property "toJSONProtocol" in which case that is used. This is not
   1542  * a general implementation but sufficient for the debugger. Note that circular
   1543  * structures will cause infinite recursion.
   1544  * @param {Object} object The object to format as JSON
   1545  * @return {string} JSON formatted object value
   1546  */
   1547 function SimpleObjectToJSON_(object) {
   1548   var content = [];
   1549   for (var key in object) {
   1550     // Only consider string keys.
   1551     if (typeof key == 'string') {
   1552       var property_value = object[key];
   1553 
   1554       // Format the value based on its type.
   1555       var property_value_json;
   1556       switch (typeof property_value) {
   1557         case 'object':
   1558           if (typeof property_value.toJSONProtocol == 'function') {
   1559             property_value_json = property_value.toJSONProtocol(true)
   1560           } else if (property_value.constructor.name == 'Array'){
   1561             property_value_json = SimpleArrayToJSON_(property_value);
   1562           } else {
   1563             property_value_json = SimpleObjectToJSON_(property_value);
   1564           }
   1565           break;
   1566 
   1567         case 'boolean':
   1568           property_value_json = BooleanToJSON_(property_value);
   1569           break;
   1570 
   1571         case 'number':
   1572           property_value_json = NumberToJSON_(property_value);
   1573           break;
   1574 
   1575         case 'string':
   1576           property_value_json = StringToJSON_(property_value);
   1577           break;
   1578 
   1579         default:
   1580           property_value_json = null;
   1581       }
   1582 
   1583       // Add the property if relevant.
   1584       if (property_value_json) {
   1585         content.push(StringToJSON_(key) + ':' + property_value_json);
   1586       }
   1587     }
   1588   }
   1589 
   1590   // Make JSON object representation.
   1591   return '{' + content.join(',') + '}';
   1592 }
   1593 
   1594 
   1595 /**
   1596  * Convert an array to its JSON representation. This is a VERY simple
   1597  * implementation just to support what is needed for the debugger.
   1598  * @param {Array} arrya The array to format as JSON
   1599  * @return {string} JSON formatted array value
   1600  */
   1601 function SimpleArrayToJSON_(array) {
   1602   // Make JSON array representation.
   1603   var json = '[';
   1604   for (var i = 0; i < array.length; i++) {
   1605     if (i != 0) {
   1606       json += ',';
   1607     }
   1608     var elem = array[i];
   1609     if (elem.toJSONProtocol) {
   1610       json += elem.toJSONProtocol(true)
   1611     } else if (typeof(elem) === 'object')  {
   1612       json += SimpleObjectToJSON_(elem);
   1613     } else if (typeof(elem) === 'boolean')  {
   1614       json += BooleanToJSON_(elem);
   1615     } else if (typeof(elem) === 'number')  {
   1616       json += NumberToJSON_(elem);
   1617     } else if (typeof(elem) === 'string')  {
   1618       json += StringToJSON_(elem);
   1619     } else {
   1620       json += elem;
   1621     }
   1622   }
   1623   json += ']';
   1624   return json;
   1625 }
   1626