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