The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / src / d8.js @ f230a1cf

History | View | Annotate | Download (64 KB)

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
"use strict";
29

    
30
String.prototype.startsWith = function (str) {
31
  if (str.length > this.length) {
32
    return false;
33
  }
34
  return this.substr(0, str.length) == str;
35
};
36

    
37
function log10(num) {
38
  return Math.log(num)/Math.log(10);
39
}
40

    
41
function ToInspectableObject(obj) {
42
  if (!obj && typeof obj === 'object') {
43
    return UNDEFINED;
44
  } else {
45
    return Object(obj);
46
  }
47
}
48

    
49
function GetCompletions(global, last, full) {
50
  var full_tokens = full.split();
51
  full = full_tokens.pop();
52
  var parts = full.split('.');
53
  parts.pop();
54
  var current = global;
55
  for (var i = 0; i < parts.length; i++) {
56
    var part = parts[i];
57
    var next = current[part];
58
    if (!next) {
59
      return [];
60
    }
61
    current = next;
62
  }
63
  var result = [];
64
  current = ToInspectableObject(current);
65
  while (typeof current !== 'undefined') {
66
    var mirror = new $debug.ObjectMirror(current);
67
    var properties = mirror.properties();
68
    for (var i = 0; i < properties.length; i++) {
69
      var name = properties[i].name();
70
      if (typeof name === 'string' && name.startsWith(last)) {
71
        result.push(name);
72
      }
73
    }
74
    current = ToInspectableObject(Object.getPrototypeOf(current));
75
  }
76
  return result;
77
}
78

    
79

    
80
// Global object holding debugger related constants and state.
81
var Debug = {};
82

    
83

    
84
// Debug events which can occour in the V8 JavaScript engine. These originate
85
// from the API include file v8-debug.h.
86
Debug.DebugEvent = { Break: 1,
87
                     Exception: 2,
88
                     NewFunction: 3,
89
                     BeforeCompile: 4,
90
                     AfterCompile: 5 };
91

    
92

    
93
// The different types of scripts matching enum ScriptType in objects.h.
94
Debug.ScriptType = { Native: 0,
95
                     Extension: 1,
96
                     Normal: 2 };
97

    
98

    
99
// The different types of script compilations matching enum
100
// Script::CompilationType in objects.h.
101
Debug.ScriptCompilationType = { Host: 0,
102
                                Eval: 1,
103
                                JSON: 2 };
104

    
105

    
106
// The different types of scopes matching constants runtime.cc.
107
Debug.ScopeType = { Global: 0,
108
                    Local: 1,
109
                    With: 2,
110
                    Closure: 3,
111
                    Catch: 4,
112
                    Block: 5 };
113

    
114

    
115
// Current debug state.
116
var kNoFrame = -1;
117
Debug.State = {
118
  currentFrame: kNoFrame,
119
  displaySourceStartLine: -1,
120
  displaySourceEndLine: -1,
121
  currentSourceLine: -1
122
};
123
var trace_compile = false;  // Tracing all compile events?
124
var trace_debug_json = false; // Tracing all debug json packets?
125
var last_cmd = '';
126
var repeat_cmd_line = '';
127
var is_running = true;
128
// Global variable used to store whether a handle was requested.
129
var lookup_handle = null;
130

    
131
// Copied from debug-delay.js.  This is needed below:
132
function ScriptTypeFlag(type) {
133
  return (1 << type);
134
}
135

    
136

    
137
// Process a debugger JSON message into a display text and a running status.
138
// This function returns an object with properties "text" and "running" holding
139
// this information.
140
function DebugMessageDetails(message) {
141
  if (trace_debug_json) {
142
    print("received: '" + message + "'");
143
  }
144
  // Convert the JSON string to an object.
145
  var response = new ProtocolPackage(message);
146
  is_running = response.running();
147

    
148
  if (response.type() == 'event') {
149
    return DebugEventDetails(response);
150
  } else {
151
    return DebugResponseDetails(response);
152
  }
153
}
154

    
155
function DebugEventDetails(response) {
156
  var details = {text:'', running:false};
157

    
158
  // Get the running state.
159
  details.running = response.running();
160

    
161
  var body = response.body();
162
  var result = '';
163
  switch (response.event()) {
164
    case 'break':
165
      if (body.breakpoints) {
166
        result += 'breakpoint';
167
        if (body.breakpoints.length > 1) {
168
          result += 's';
169
        }
170
        result += ' #';
171
        for (var i = 0; i < body.breakpoints.length; i++) {
172
          if (i > 0) {
173
            result += ', #';
174
          }
175
          result += body.breakpoints[i];
176
        }
177
      } else {
178
        result += 'break';
179
      }
180
      result += ' in ';
181
      result += body.invocationText;
182
      result += ', ';
183
      result += SourceInfo(body);
184
      result += '\n';
185
      result += SourceUnderline(body.sourceLineText, body.sourceColumn);
186
      Debug.State.currentSourceLine = body.sourceLine;
187
      Debug.State.displaySourceStartLine = -1;
188
      Debug.State.displaySourceEndLine = -1;
189
      Debug.State.currentFrame = 0;
190
      details.text = result;
191
      break;
192

    
193
    case 'exception':
194
      if (body.uncaught) {
195
        result += 'Uncaught: ';
196
      } else {
197
        result += 'Exception: ';
198
      }
199
      result += '"';
200
      result += body.exception.text;
201
      result += '"';
202
      if (body.sourceLine >= 0) {
203
        result += ', ';
204
        result += SourceInfo(body);
205
        result += '\n';
206
        result += SourceUnderline(body.sourceLineText, body.sourceColumn);
207
        Debug.State.currentSourceLine = body.sourceLine;
208
        Debug.State.displaySourceStartLine = -1;
209
        Debug.State.displaySourceEndLine = -1;
210
        Debug.State.currentFrame = 0;
211
      } else {
212
        result += ' (empty stack)';
213
        Debug.State.currentSourceLine = -1;
214
        Debug.State.displaySourceStartLine = -1;
215
        Debug.State.displaySourceEndLine = -1;
216
        Debug.State.currentFrame = kNoFrame;
217
      }
218
      details.text = result;
219
      break;
220

    
221
    case 'afterCompile':
222
      if (trace_compile) {
223
        result = 'Source ' + body.script.name + ' compiled:\n';
224
        var source = body.script.source;
225
        if (!(source[source.length - 1] == '\n')) {
226
          result += source;
227
        } else {
228
          result += source.substring(0, source.length - 1);
229
        }
230
      }
231
      details.text = result;
232
      break;
233

    
234
    case 'scriptCollected':
235
      details.text = result;
236
      break;
237

    
238
    default:
239
      details.text = 'Unknown debug event ' + response.event();
240
  }
241

    
242
  return details;
243
}
244

    
245

    
246
function SourceInfo(body) {
247
  var result = '';
248

    
249
  if (body.script) {
250
    if (body.script.name) {
251
      result += body.script.name;
252
    } else {
253
      result += '[unnamed]';
254
    }
255
  }
256
  result += ' line ';
257
  result += body.sourceLine + 1;
258
  result += ' column ';
259
  result += body.sourceColumn + 1;
260

    
261
  return result;
262
}
263

    
264

    
265
function SourceUnderline(source_text, position) {
266
  if (!source_text) {
267
    return;
268
  }
269

    
270
  // Create an underline with a caret pointing to the source position. If the
271
  // source contains a tab character the underline will have a tab character in
272
  // the same place otherwise the underline will have a space character.
273
  var underline = '';
274
  for (var i = 0; i < position; i++) {
275
    if (source_text[i] == '\t') {
276
      underline += '\t';
277
    } else {
278
      underline += ' ';
279
    }
280
  }
281
  underline += '^';
282

    
283
  // Return the source line text with the underline beneath.
284
  return source_text + '\n' + underline;
285
}
286

    
287

    
288
// Converts a text command to a JSON request.
289
function DebugCommandToJSONRequest(cmd_line) {
290
  var result = new DebugRequest(cmd_line).JSONRequest();
291
  if (trace_debug_json && result) {
292
    print("sending: '" + result + "'");
293
  }
294
  return result;
295
}
296

    
297

    
298
function DebugRequest(cmd_line) {
299
  // If the very first character is a { assume that a JSON request have been
300
  // entered as a command. Converting that to a JSON request is trivial.
301
  if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') {
302
    this.request_ = cmd_line;
303
    return;
304
  }
305

    
306
  // Check for a simple carriage return to repeat the last command:
307
  var is_repeating = false;
308
  if (cmd_line == '\n') {
309
    if (is_running) {
310
      cmd_line = 'break'; // Not in debugger mode, break with a frame request.
311
    } else {
312
      cmd_line = repeat_cmd_line; // use command to repeat.
313
      is_repeating = true;
314
    }
315
  }
316
  if (!is_running) { // Only save the command if in debugger mode.
317
    repeat_cmd_line = cmd_line;   // save last command.
318
  }
319

    
320
  // Trim string for leading and trailing whitespace.
321
  cmd_line = cmd_line.replace(/^\s+|\s+$/g, '');
322

    
323
  // Find the command.
324
  var pos = cmd_line.indexOf(' ');
325
  var cmd;
326
  var args;
327
  if (pos == -1) {
328
    cmd = cmd_line;
329
    args = '';
330
  } else {
331
    cmd = cmd_line.slice(0, pos);
332
    args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, '');
333
  }
334

    
335
  if ((cmd === undefined) || !cmd) {
336
    this.request_ = UNDEFINED;
337
    return;
338
  }
339

    
340
  last_cmd = cmd;
341

    
342
  // Switch on command.
343
  switch (cmd) {
344
    case 'continue':
345
    case 'c':
346
      this.request_ = this.continueCommandToJSONRequest_(args);
347
      break;
348

    
349
    case 'step':
350
    case 's':
351
      this.request_ = this.stepCommandToJSONRequest_(args, 'in');
352
      break;
353

    
354
    case 'stepi':
355
    case 'si':
356
      this.request_ = this.stepCommandToJSONRequest_(args, 'min');
357
      break;
358

    
359
    case 'next':
360
    case 'n':
361
      this.request_ = this.stepCommandToJSONRequest_(args, 'next');
362
      break;
363

    
364
    case 'finish':
365
    case 'fin':
366
      this.request_ = this.stepCommandToJSONRequest_(args, 'out');
367
      break;
368

    
369
    case 'backtrace':
370
    case 'bt':
371
      this.request_ = this.backtraceCommandToJSONRequest_(args);
372
      break;
373

    
374
    case 'frame':
375
    case 'f':
376
      this.request_ = this.frameCommandToJSONRequest_(args);
377
      break;
378

    
379
    case 'scopes':
380
      this.request_ = this.scopesCommandToJSONRequest_(args);
381
      break;
382

    
383
    case 'scope':
384
      this.request_ = this.scopeCommandToJSONRequest_(args);
385
      break;
386

    
387
    case 'disconnect':
388
    case 'exit':
389
    case 'quit':
390
      this.request_ = this.disconnectCommandToJSONRequest_(args);
391
      break;
392

    
393
    case 'up':
394
      this.request_ =
395
          this.frameCommandToJSONRequest_('' +
396
                                          (Debug.State.currentFrame + 1));
397
      break;
398

    
399
    case 'down':
400
    case 'do':
401
      this.request_ =
402
          this.frameCommandToJSONRequest_('' +
403
                                          (Debug.State.currentFrame - 1));
404
      break;
405

    
406
    case 'set':
407
    case 'print':
408
    case 'p':
409
      this.request_ = this.printCommandToJSONRequest_(args);
410
      break;
411

    
412
    case 'dir':
413
      this.request_ = this.dirCommandToJSONRequest_(args);
414
      break;
415

    
416
    case 'references':
417
      this.request_ = this.referencesCommandToJSONRequest_(args);
418
      break;
419

    
420
    case 'instances':
421
      this.request_ = this.instancesCommandToJSONRequest_(args);
422
      break;
423

    
424
    case 'list':
425
    case 'l':
426
      this.request_ = this.listCommandToJSONRequest_(args);
427
      break;
428
    case 'source':
429
      this.request_ = this.sourceCommandToJSONRequest_(args);
430
      break;
431

    
432
    case 'scripts':
433
    case 'script':
434
    case 'scr':
435
      this.request_ = this.scriptsCommandToJSONRequest_(args);
436
      break;
437

    
438
    case 'break':
439
    case 'b':
440
      this.request_ = this.breakCommandToJSONRequest_(args);
441
      break;
442

    
443
    case 'breakpoints':
444
    case 'bb':
445
      this.request_ = this.breakpointsCommandToJSONRequest_(args);
446
      break;
447

    
448
    case 'clear':
449
    case 'delete':
450
    case 'd':
451
      this.request_ = this.clearCommandToJSONRequest_(args);
452
      break;
453

    
454
    case 'threads':
455
      this.request_ = this.threadsCommandToJSONRequest_(args);
456
      break;
457

    
458
    case 'cond':
459
      this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond');
460
      break;
461

    
462
    case 'enable':
463
    case 'en':
464
      this.request_ =
465
          this.changeBreakpointCommandToJSONRequest_(args, 'enable');
466
      break;
467

    
468
    case 'disable':
469
    case 'dis':
470
      this.request_ =
471
          this.changeBreakpointCommandToJSONRequest_(args, 'disable');
472
      break;
473

    
474
    case 'ignore':
475
      this.request_ =
476
          this.changeBreakpointCommandToJSONRequest_(args, 'ignore');
477
      break;
478

    
479
    case 'info':
480
    case 'inf':
481
      this.request_ = this.infoCommandToJSONRequest_(args);
482
      break;
483

    
484
    case 'flags':
485
      this.request_ = this.v8FlagsToJSONRequest_(args);
486
      break;
487

    
488
    case 'gc':
489
      this.request_ = this.gcToJSONRequest_(args);
490
      break;
491

    
492
    case 'trace':
493
    case 'tr':
494
      // Return undefined to indicate command handled internally (no JSON).
495
      this.request_ = UNDEFINED;
496
      this.traceCommand_(args);
497
      break;
498

    
499
    case 'help':
500
    case '?':
501
      this.helpCommand_(args);
502
      // Return undefined to indicate command handled internally (no JSON).
503
      this.request_ = UNDEFINED;
504
      break;
505

    
506
    default:
507
      throw new Error('Unknown command "' + cmd + '"');
508
  }
509
}
510

    
511
DebugRequest.prototype.JSONRequest = function() {
512
  return this.request_;
513
};
514

    
515

    
516
function RequestPacket(command) {
517
  this.seq = 0;
518
  this.type = 'request';
519
  this.command = command;
520
}
521

    
522

    
523
RequestPacket.prototype.toJSONProtocol = function() {
524
  // Encode the protocol header.
525
  var json = '{';
526
  json += '"seq":' + this.seq;
527
  json += ',"type":"' + this.type + '"';
528
  if (this.command) {
529
    json += ',"command":' + StringToJSON_(this.command);
530
  }
531
  if (this.arguments) {
532
    json += ',"arguments":';
533
    // Encode the arguments part.
534
    if (this.arguments.toJSONProtocol) {
535
      json += this.arguments.toJSONProtocol();
536
    } else {
537
      json += SimpleObjectToJSON_(this.arguments);
538
    }
539
  }
540
  json += '}';
541
  return json;
542
};
543

    
544

    
545
DebugRequest.prototype.createRequest = function(command) {
546
  return new RequestPacket(command);
547
};
548

    
549

    
550
// Create a JSON request for the evaluation command.
551
DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
552
  lookup_handle = null;
553

    
554
  // Check if the expression is a handle id in the form #<handle>#.
555
  var handle_match = expression.match(/^#([0-9]*)#$/);
556
  if (handle_match) {
557
    // Remember the handle requested in a global variable.
558
    lookup_handle = parseInt(handle_match[1]);
559
    // Build a lookup request.
560
    var request = this.createRequest('lookup');
561
    request.arguments = {};
562
    request.arguments.handles = [ lookup_handle ];
563
    return request.toJSONProtocol();
564
  } else {
565
    // Build an evaluate request.
566
    var request = this.createRequest('evaluate');
567
    request.arguments = {};
568
    request.arguments.expression = expression;
569
    // Request a global evaluation if there is no current frame.
570
    if (Debug.State.currentFrame == kNoFrame) {
571
      request.arguments.global = true;
572
    }
573
    return request.toJSONProtocol();
574
  }
575
};
576

    
577

    
578
// Create a JSON request for the references/instances command.
579
DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
580
  // Build a references request.
581
  var handle_match = handle.match(/^#([0-9]*)#$/);
582
  if (handle_match) {
583
    var request = this.createRequest('references');
584
    request.arguments = {};
585
    request.arguments.type = type;
586
    request.arguments.handle = parseInt(handle_match[1]);
587
    return request.toJSONProtocol();
588
  } else {
589
    throw new Error('Invalid object id.');
590
  }
591
};
592

    
593

    
594
// Create a JSON request for the continue command.
595
DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
596
  var request = this.createRequest('continue');
597
  return request.toJSONProtocol();
598
};
599

    
600

    
601
// Create a JSON request for the step command.
602
DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) {
603
  // Requesting a step is through the continue command with additional
604
  // arguments.
605
  var request = this.createRequest('continue');
606
  request.arguments = {};
607

    
608
  // Process arguments if any.
609

    
610
  // Only process args if the command is 'step' which is indicated by type being
611
  // set to 'in'.  For all other commands, ignore the args.
612
  if (args && args.length > 0) {
613
    args = args.split(/\s+/g);
614

    
615
    if (args.length > 2) {
616
      throw new Error('Invalid step arguments.');
617
    }
618

    
619
    if (args.length > 0) {
620
      // Check if we have a gdb stype step command.  If so, the 1st arg would
621
      // be the step count.  If it's not a number, then assume that we're
622
      // parsing for the legacy v8 step command.
623
      var stepcount = Number(args[0]);
624
      if (stepcount == Number.NaN) {
625
        // No step count at arg 1.  Process as legacy d8 step command:
626
        if (args.length == 2) {
627
          var stepcount = parseInt(args[1]);
628
          if (isNaN(stepcount) || stepcount <= 0) {
629
            throw new Error('Invalid step count argument "' + args[0] + '".');
630
          }
631
          request.arguments.stepcount = stepcount;
632
        }
633

    
634
        // Get the step action.
635
        switch (args[0]) {
636
          case 'in':
637
          case 'i':
638
            request.arguments.stepaction = 'in';
639
            break;
640

    
641
          case 'min':
642
          case 'm':
643
            request.arguments.stepaction = 'min';
644
            break;
645

    
646
          case 'next':
647
          case 'n':
648
            request.arguments.stepaction = 'next';
649
            break;
650

    
651
          case 'out':
652
          case 'o':
653
            request.arguments.stepaction = 'out';
654
            break;
655

    
656
          default:
657
            throw new Error('Invalid step argument "' + args[0] + '".');
658
        }
659

    
660
      } else {
661
        // gdb style step commands:
662
        request.arguments.stepaction = type;
663
        request.arguments.stepcount = stepcount;
664
      }
665
    }
666
  } else {
667
    // Default is step of the specified type.
668
    request.arguments.stepaction = type;
669
  }
670

    
671
  return request.toJSONProtocol();
672
};
673

    
674

    
675
// Create a JSON request for the backtrace command.
676
DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) {
677
  // Build a backtrace request from the text command.
678
  var request = this.createRequest('backtrace');
679

    
680
  // Default is to show top 10 frames.
681
  request.arguments = {};
682
  request.arguments.fromFrame = 0;
683
  request.arguments.toFrame = 10;
684

    
685
  args = args.split(/\s*[ ]+\s*/g);
686
  if (args.length == 1 && args[0].length > 0) {
687
    var frameCount = parseInt(args[0]);
688
    if (frameCount > 0) {
689
      // Show top frames.
690
      request.arguments.fromFrame = 0;
691
      request.arguments.toFrame = frameCount;
692
    } else {
693
      // Show bottom frames.
694
      request.arguments.fromFrame = 0;
695
      request.arguments.toFrame = -frameCount;
696
      request.arguments.bottom = true;
697
    }
698
  } else if (args.length == 2) {
699
    var fromFrame = parseInt(args[0]);
700
    var toFrame = parseInt(args[1]);
701
    if (isNaN(fromFrame) || fromFrame < 0) {
702
      throw new Error('Invalid start frame argument "' + args[0] + '".');
703
    }
704
    if (isNaN(toFrame) || toFrame < 0) {
705
      throw new Error('Invalid end frame argument "' + args[1] + '".');
706
    }
707
    if (fromFrame > toFrame) {
708
      throw new Error('Invalid arguments start frame cannot be larger ' +
709
                      'than end frame.');
710
    }
711
    // Show frame range.
712
    request.arguments.fromFrame = fromFrame;
713
    request.arguments.toFrame = toFrame + 1;
714
  } else if (args.length > 2) {
715
    throw new Error('Invalid backtrace arguments.');
716
  }
717

    
718
  return request.toJSONProtocol();
719
};
720

    
721

    
722
// Create a JSON request for the frame command.
723
DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) {
724
  // Build a frame request from the text command.
725
  var request = this.createRequest('frame');
726
  args = args.split(/\s*[ ]+\s*/g);
727
  if (args.length > 0 && args[0].length > 0) {
728
    request.arguments = {};
729
    request.arguments.number = args[0];
730
  }
731
  return request.toJSONProtocol();
732
};
733

    
734

    
735
// Create a JSON request for the scopes command.
736
DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) {
737
  // Build a scopes request from the text command.
738
  var request = this.createRequest('scopes');
739
  return request.toJSONProtocol();
740
};
741

    
742

    
743
// Create a JSON request for the scope command.
744
DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) {
745
  // Build a scope request from the text command.
746
  var request = this.createRequest('scope');
747
  args = args.split(/\s*[ ]+\s*/g);
748
  if (args.length > 0 && args[0].length > 0) {
749
    request.arguments = {};
750
    request.arguments.number = args[0];
751
  }
752
  return request.toJSONProtocol();
753
};
754

    
755

    
756
// Create a JSON request for the print command.
757
DebugRequest.prototype.printCommandToJSONRequest_ = function(args) {
758
  // Build an evaluate request from the text command.
759
  if (args.length == 0) {
760
    throw new Error('Missing expression.');
761
  }
762
  return this.makeEvaluateJSONRequest_(args);
763
};
764

    
765

    
766
// Create a JSON request for the dir command.
767
DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
768
  // Build an evaluate request from the text command.
769
  if (args.length == 0) {
770
    throw new Error('Missing expression.');
771
  }
772
  return this.makeEvaluateJSONRequest_(args);
773
};
774

    
775

    
776
// Create a JSON request for the references command.
777
DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
778
  // Build an evaluate request from the text command.
779
  if (args.length == 0) {
780
    throw new Error('Missing object id.');
781
  }
782

    
783
  return this.makeReferencesJSONRequest_(args, 'referencedBy');
784
};
785

    
786

    
787
// Create a JSON request for the instances command.
788
DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
789
  // Build an evaluate request from the text command.
790
  if (args.length == 0) {
791
    throw new Error('Missing object id.');
792
  }
793

    
794
  // Build a references request.
795
  return this.makeReferencesJSONRequest_(args, 'constructedBy');
796
};
797

    
798

    
799
// Create a JSON request for the list command.
800
DebugRequest.prototype.listCommandToJSONRequest_ = function(args) {
801

    
802
  // Default is ten lines starting five lines before the current location.
803
  if (Debug.State.displaySourceEndLine == -1) {
804
    // If we list forwards, we will start listing after the last source end
805
    // line.  Set it to start from 5 lines before the current location.
806
    Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5;
807
    // If we list backwards, we will start listing backwards from the last
808
    // source start line.  Set it to start from 1 lines before the current
809
    // location.
810
    Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1;
811
  }
812

    
813
  var from = Debug.State.displaySourceEndLine + 1;
814
  var lines = 10;
815

    
816
  // Parse the arguments.
817
  args = args.split(/\s*,\s*/g);
818
  if (args == '') {
819
  } else if ((args.length == 1) && (args[0] == '-')) {
820
    from = Debug.State.displaySourceStartLine - lines;
821
  } else if (args.length == 2) {
822
    from = parseInt(args[0]);
823
    lines = parseInt(args[1]) - from + 1; // inclusive of the ending line.
824
  } else {
825
    throw new Error('Invalid list arguments.');
826
  }
827
  Debug.State.displaySourceStartLine = from;
828
  Debug.State.displaySourceEndLine = from + lines - 1;
829
  var sourceArgs = '' + from + ' ' + lines;
830
  return this.sourceCommandToJSONRequest_(sourceArgs);
831
};
832

    
833

    
834
// Create a JSON request for the source command.
835
DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
836
  // Build a evaluate request from the text command.
837
  var request = this.createRequest('source');
838

    
839
  // Default is ten lines starting five lines before the current location.
840
  var from = Debug.State.currentSourceLine - 5;
841
  var lines = 10;
842

    
843
  // Parse the arguments.
844
  args = args.split(/\s*[ ]+\s*/g);
845
  if (args.length > 1 && args[0].length > 0 && args[1].length > 0) {
846
    from = parseInt(args[0]) - 1;
847
    lines = parseInt(args[1]);
848
  } else if (args.length > 0 && args[0].length > 0) {
849
    from = parseInt(args[0]) - 1;
850
  }
851

    
852
  if (from < 0) from = 0;
853
  if (lines < 0) lines = 10;
854

    
855
  // Request source arround current source location.
856
  request.arguments = {};
857
  request.arguments.fromLine = from;
858
  request.arguments.toLine = from + lines;
859

    
860
  return request.toJSONProtocol();
861
};
862

    
863

    
864
// Create a JSON request for the scripts command.
865
DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) {
866
  // Build a evaluate request from the text command.
867
  var request = this.createRequest('scripts');
868

    
869
  // Process arguments if any.
870
  if (args && args.length > 0) {
871
    args = args.split(/\s*[ ]+\s*/g);
872

    
873
    if (args.length > 1) {
874
      throw new Error('Invalid scripts arguments.');
875
    }
876

    
877
    request.arguments = {};
878
    switch (args[0]) {
879
      case 'natives':
880
        request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native);
881
        break;
882

    
883
      case 'extensions':
884
        request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension);
885
        break;
886

    
887
      case 'all':
888
        request.arguments.types =
889
            ScriptTypeFlag(Debug.ScriptType.Normal) |
890
            ScriptTypeFlag(Debug.ScriptType.Native) |
891
            ScriptTypeFlag(Debug.ScriptType.Extension);
892
        break;
893

    
894
      default:
895
        // If the arg is not one of the know one aboves, then it must be a
896
        // filter used for filtering the results:
897
        request.arguments.filter = args[0];
898
        break;
899
    }
900
  }
901

    
902
  return request.toJSONProtocol();
903
};
904

    
905

    
906
// Create a JSON request for the break command.
907
DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) {
908
  // Build a evaluate request from the text command.
909
  // Process arguments if any.
910
  if (args && args.length > 0) {
911
    var target = args;
912
    var type = 'function';
913
    var line;
914
    var column;
915
    var condition;
916
    var pos;
917

    
918
    var request = this.createRequest('setbreakpoint');
919

    
920
    // Break the args into target spec and condition if appropriate.
921

    
922
    // Check for breakpoint condition.
923
    pos = args.indexOf(' ');
924
    if (pos > 0) {
925
      target = args.substring(0, pos);
926
      condition = args.substring(pos + 1, args.length);
927
    }
928

    
929
    // Check for script breakpoint (name:line[:column]). If no ':' in break
930
    // specification it is considered a function break point.
931
    pos = target.indexOf(':');
932
    if (pos > 0) {
933
      var tmp = target.substring(pos + 1, target.length);
934
      target = target.substring(0, pos);
935
      if (target[0] == '/' && target[target.length - 1] == '/') {
936
        type = 'scriptRegExp';
937
        target = target.substring(1, target.length - 1);
938
      } else {
939
        type = 'script';
940
      }
941

    
942
      // Check for both line and column.
943
      pos = tmp.indexOf(':');
944
      if (pos > 0) {
945
        column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1;
946
        line = parseInt(tmp.substring(0, pos)) - 1;
947
      } else {
948
        line = parseInt(tmp) - 1;
949
      }
950
    } else if (target[0] == '#' && target[target.length - 1] == '#') {
951
      type = 'handle';
952
      target = target.substring(1, target.length - 1);
953
    } else {
954
      type = 'function';
955
    }
956

    
957
    request.arguments = {};
958
    request.arguments.type = type;
959
    request.arguments.target = target;
960
    request.arguments.line = line;
961
    request.arguments.column = column;
962
    request.arguments.condition = condition;
963
  } else {
964
    var request = this.createRequest('suspend');
965
  }
966

    
967
  return request.toJSONProtocol();
968
};
969

    
970

    
971
DebugRequest.prototype.breakpointsCommandToJSONRequest_ = function(args) {
972
  if (args && args.length > 0) {
973
    throw new Error('Unexpected arguments.');
974
  }
975
  var request = this.createRequest('listbreakpoints');
976
  return request.toJSONProtocol();
977
};
978

    
979

    
980
// Create a JSON request for the clear command.
981
DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) {
982
  // Build a evaluate request from the text command.
983
  var request = this.createRequest('clearbreakpoint');
984

    
985
  // Process arguments if any.
986
  if (args && args.length > 0) {
987
    request.arguments = {};
988
    request.arguments.breakpoint = parseInt(args);
989
  } else {
990
    throw new Error('Invalid break arguments.');
991
  }
992

    
993
  return request.toJSONProtocol();
994
};
995

    
996

    
997
// Create a JSON request for the change breakpoint command.
998
DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ =
999
    function(args, command) {
1000

    
1001
  var request;
1002

    
1003
  // Check for exception breaks first:
1004
  //   en[able] exc[eptions] [all|unc[aught]]
1005
  //   en[able] [all|unc[aught]] exc[eptions]
1006
  //   dis[able] exc[eptions] [all|unc[aught]]
1007
  //   dis[able] [all|unc[aught]] exc[eptions]
1008
  if ((command == 'enable' || command == 'disable') &&
1009
      args && args.length > 1) {
1010
    var nextPos = args.indexOf(' ');
1011
    var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args;
1012
    var excType = null;
1013

    
1014
    // Check for:
1015
    //   en[able] exc[eptions] [all|unc[aught]]
1016
    //   dis[able] exc[eptions] [all|unc[aught]]
1017
    if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
1018

    
1019
      var arg2 = (nextPos > 0) ?
1020
          args.substring(nextPos + 1, args.length) : 'all';
1021
      if (!arg2) {
1022
        arg2 = 'all'; // if unspecified, set for all.
1023
      } else if (arg2 == 'unc') { // check for short cut.
1024
        arg2 = 'uncaught';
1025
      }
1026
      excType = arg2;
1027

    
1028
    // Check for:
1029
    //   en[able] [all|unc[aught]] exc[eptions]
1030
    //   dis[able] [all|unc[aught]] exc[eptions]
1031
    } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') {
1032

    
1033
      var arg2 = (nextPos > 0) ?
1034
          args.substring(nextPos + 1, args.length) : null;
1035
      if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
1036
        excType = arg1;
1037
        if (excType == 'unc') {
1038
          excType = 'uncaught';
1039
        }
1040
      }
1041
    }
1042

    
1043
    // If we matched one of the command formats, then excType will be non-null:
1044
    if (excType) {
1045
      // Build a evaluate request from the text command.
1046
      request = this.createRequest('setexceptionbreak');
1047

    
1048
      request.arguments = {};
1049
      request.arguments.type = excType;
1050
      request.arguments.enabled = (command == 'enable');
1051

    
1052
      return request.toJSONProtocol();
1053
    }
1054
  }
1055

    
1056
  // Build a evaluate request from the text command.
1057
  request = this.createRequest('changebreakpoint');
1058

    
1059
  // Process arguments if any.
1060
  if (args && args.length > 0) {
1061
    request.arguments = {};
1062
    var pos = args.indexOf(' ');
1063
    var breakpointArg = args;
1064
    var otherArgs;
1065
    if (pos > 0) {
1066
      breakpointArg = args.substring(0, pos);
1067
      otherArgs = args.substring(pos + 1, args.length);
1068
    }
1069

    
1070
    request.arguments.breakpoint = parseInt(breakpointArg);
1071

    
1072
    switch(command) {
1073
      case 'cond':
1074
        request.arguments.condition = otherArgs ? otherArgs : null;
1075
        break;
1076
      case 'enable':
1077
        request.arguments.enabled = true;
1078
        break;
1079
      case 'disable':
1080
        request.arguments.enabled = false;
1081
        break;
1082
      case 'ignore':
1083
        request.arguments.ignoreCount = parseInt(otherArgs);
1084
        break;
1085
      default:
1086
        throw new Error('Invalid arguments.');
1087
    }
1088
  } else {
1089
    throw new Error('Invalid arguments.');
1090
  }
1091

    
1092
  return request.toJSONProtocol();
1093
};
1094

    
1095

    
1096
// Create a JSON request for the disconnect command.
1097
DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) {
1098
  var request;
1099
  request = this.createRequest('disconnect');
1100
  return request.toJSONProtocol();
1101
};
1102

    
1103

    
1104
// Create a JSON request for the info command.
1105
DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) {
1106
  var request;
1107
  if (args && (args == 'break' || args == 'br')) {
1108
    // Build a evaluate request from the text command.
1109
    request = this.createRequest('listbreakpoints');
1110
    last_cmd = 'info break';
1111
  } else if (args && (args == 'locals' || args == 'lo')) {
1112
    // Build a evaluate request from the text command.
1113
    request = this.createRequest('frame');
1114
    last_cmd = 'info locals';
1115
  } else if (args && (args == 'args' || args == 'ar')) {
1116
    // Build a evaluate request from the text command.
1117
    request = this.createRequest('frame');
1118
    last_cmd = 'info args';
1119
  } else {
1120
    throw new Error('Invalid info arguments.');
1121
  }
1122

    
1123
  return request.toJSONProtocol();
1124
};
1125

    
1126

    
1127
DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) {
1128
  var request;
1129
  request = this.createRequest('v8flags');
1130
  request.arguments = {};
1131
  request.arguments.flags = args;
1132
  return request.toJSONProtocol();
1133
};
1134

    
1135

    
1136
DebugRequest.prototype.gcToJSONRequest_ = function(args) {
1137
  var request;
1138
  if (!args) {
1139
    args = 'all';
1140
  }
1141
  var args = args.split(/\s+/g);
1142
  var cmd = args[0];
1143

    
1144
  switch(cmd) {
1145
    case 'all':
1146
    case 'quick':
1147
    case 'full':
1148
    case 'young':
1149
    case 'old':
1150
    case 'compact':
1151
    case 'sweep':
1152
    case 'scavenge': {
1153
      if (cmd == 'young') { cmd = 'quick'; }
1154
      else if (cmd == 'old') { cmd = 'full'; }
1155

    
1156
      request = this.createRequest('gc');
1157
      request.arguments = {};
1158
      request.arguments.type = cmd;
1159
      break;
1160
    }
1161
      // Else fall thru to the default case below to report the error.
1162
    default:
1163
      throw new Error('Missing arguments after ' + cmd + '.');
1164
  }
1165
  return request.toJSONProtocol();
1166
};
1167

    
1168

    
1169
// Create a JSON request for the threads command.
1170
DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
1171
  // Build a threads request from the text command.
1172
  var request = this.createRequest('threads');
1173
  return request.toJSONProtocol();
1174
};
1175

    
1176

    
1177
// Handle the trace command.
1178
DebugRequest.prototype.traceCommand_ = function(args) {
1179
  // Process arguments.
1180
  if (args && args.length > 0) {
1181
    if (args == 'compile') {
1182
      trace_compile = !trace_compile;
1183
      print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off'));
1184
    } else if (args === 'debug json' || args === 'json' || args === 'packets') {
1185
      trace_debug_json = !trace_debug_json;
1186
      print('Tracing of debug json packets ' +
1187
            (trace_debug_json ? 'on' : 'off'));
1188
    } else {
1189
      throw new Error('Invalid trace arguments.');
1190
    }
1191
  } else {
1192
    throw new Error('Invalid trace arguments.');
1193
  }
1194
};
1195

    
1196
// Handle the help command.
1197
DebugRequest.prototype.helpCommand_ = function(args) {
1198
  // Help os quite simple.
1199
  if (args && args.length > 0) {
1200
    print('warning: arguments to \'help\' are ignored');
1201
  }
1202

    
1203
  print('Note: <> denotes symbollic values to be replaced with real values.');
1204
  print('Note: [] denotes optional parts of commands, or optional options / arguments.');
1205
  print('      e.g. d[elete] - you get the same command if you type d or delete.');
1206
  print('');
1207
  print('[break] - break as soon as possible');
1208
  print('b[reak] location [condition]');
1209
  print('        - break on named function: location is a function name');
1210
  print('        - break on function: location is #<id>#');
1211
  print('        - break on script position: location is name:line[:column]');
1212
  print('');
1213
  print('clear <breakpoint #>       - deletes the specified user defined breakpoint');
1214
  print('d[elete]  <breakpoint #>   - deletes the specified user defined breakpoint');
1215
  print('dis[able] <breakpoint #>   - disables the specified user defined breakpoint');
1216
  print('dis[able] exc[eptions] [[all] | unc[aught]]');
1217
  print('                           - disables breaking on exceptions');
1218
  print('en[able]  <breakpoint #>   - enables the specified user defined breakpoint');
1219
  print('en[able]  exc[eptions] [[all] | unc[aught]]');
1220
  print('                           - enables breaking on exceptions');
1221
  print('');
1222
  print('b[ack]t[race] [n] | [-n] | [from to]');
1223
  print('                           - prints the stack back trace');
1224
  print('f[rame]                    - prints info about the current frame context');
1225
  print('f[rame] <frame #>          - set context to specified frame #');
1226
  print('scopes');
1227
  print('scope <scope #>');
1228
  print('');
1229
  print('up                         - set context to caller of current frame');
1230
  print('do[wn]                     - set context to callee of current frame');
1231
  print('inf[o] br[eak]             - prints info about breakpoints in use');
1232
  print('inf[o] ar[gs]              - prints info about arguments of the current function');
1233
  print('inf[o] lo[cals]            - prints info about locals in the current function');
1234
  print('');
1235
  print('step [in | next | out| min [step count]]');
1236
  print('c[ontinue]                 - continue executing after a breakpoint');
1237
  print('s[tep]   [<N>]             - step into the next N callees (default N is 1)');
1238
  print('s[tep]i  [<N>]             - step into the next N callees (default N is 1)');
1239
  print('n[ext]   [<N>]             - step over the next N callees (default N is 1)');
1240
  print('fin[ish] [<N>]             - step out of N frames (default N is 1)');
1241
  print('');
1242
  print('p[rint] <expression>       - prints the result of the specified expression');
1243
  print('dir <expression>           - prints the object structure of the result');
1244
  print('set <var> = <expression>   - executes the specified statement');
1245
  print('');
1246
  print('l[ist]                     - list the source code around for the current pc');
1247
  print('l[ist] [- | <start>,<end>] - list the specified range of source code');
1248
  print('source [from line [num lines]]');
1249
  print('scr[ipts] [native|extensions|all]');
1250
  print('scr[ipts] [<filter text>]  - list scripts with the specified text in its description');
1251
  print('');
1252
  print('gc                         - runs the garbage collector');
1253
  print('');
1254
  print('trace compile');
1255
  // hidden command: trace debug json - toggles tracing of debug json packets
1256
  print('');
1257
  print('disconnect|exit|quit       - disconnects and quits the debugger');
1258
  print('help                       - prints this help information');
1259
};
1260

    
1261

    
1262
function formatHandleReference_(value) {
1263
  if (value.handle() >= 0) {
1264
    return '#' + value.handle() + '#';
1265
  } else {
1266
    return '#Transient#';
1267
  }
1268
}
1269

    
1270

    
1271
function formatObject_(value, include_properties) {
1272
  var result = '';
1273
  result += formatHandleReference_(value);
1274
  result += ', type: object';
1275
  result += ', constructor ';
1276
  var ctor = value.constructorFunctionValue();
1277
  result += formatHandleReference_(ctor);
1278
  result += ', __proto__ ';
1279
  var proto = value.protoObjectValue();
1280
  result += formatHandleReference_(proto);
1281
  result += ', ';
1282
  result += value.propertyCount();
1283
  result +=  ' properties.';
1284
  if (include_properties) {
1285
    result +=  '\n';
1286
    for (var i = 0; i < value.propertyCount(); i++) {
1287
      result += '  ';
1288
      result += value.propertyName(i);
1289
      result += ': ';
1290
      var property_value = value.propertyValue(i);
1291
      if (property_value instanceof ProtocolReference) {
1292
        result += '<no type>';
1293
      } else {
1294
        if (property_value && property_value.type()) {
1295
          result += property_value.type();
1296
        } else {
1297
          result += '<no type>';
1298
        }
1299
      }
1300
      result += ' ';
1301
      result += formatHandleReference_(property_value);
1302
      result += '\n';
1303
    }
1304
  }
1305
  return result;
1306
}
1307

    
1308

    
1309
function formatScope_(scope) {
1310
  var result = '';
1311
  var index = scope.index;
1312
  result += '#' + (index <= 9 ? '0' : '') + index;
1313
  result += ' ';
1314
  switch (scope.type) {
1315
    case Debug.ScopeType.Global:
1316
      result += 'Global, ';
1317
      result += '#' + scope.object.ref + '#';
1318
      break;
1319
    case Debug.ScopeType.Local:
1320
      result += 'Local';
1321
      break;
1322
    case Debug.ScopeType.With:
1323
      result += 'With, ';
1324
      result += '#' + scope.object.ref + '#';
1325
      break;
1326
    case Debug.ScopeType.Catch:
1327
      result += 'Catch, ';
1328
      result += '#' + scope.object.ref + '#';
1329
      break;
1330
    case Debug.ScopeType.Closure:
1331
      result += 'Closure';
1332
      break;
1333
    default:
1334
      result += 'UNKNOWN';
1335
  }
1336
  return result;
1337
}
1338

    
1339

    
1340
function refObjectToString_(protocolPackage, handle) {
1341
  var value = protocolPackage.lookup(handle);
1342
  var result = '';
1343
  if (value.isString()) {
1344
    result = '"' + value.value() + '"';
1345
  } else if (value.isPrimitive()) {
1346
    result = value.valueString();
1347
  } else if (value.isObject()) {
1348
    result += formatObject_(value, true);
1349
  }
1350
  return result;
1351
}
1352

    
1353

    
1354
// Rounds number 'num' to 'length' decimal places.
1355
function roundNumber(num, length) {
1356
  var factor = Math.pow(10, length);
1357
  return Math.round(num * factor) / factor;
1358
}
1359

    
1360

    
1361
// Convert a JSON response to text for display in a text based debugger.
1362
function DebugResponseDetails(response) {
1363
  var details = { text: '', running: false };
1364

    
1365
  try {
1366
    if (!response.success()) {
1367
      details.text = response.message();
1368
      return details;
1369
    }
1370

    
1371
    // Get the running state.
1372
    details.running = response.running();
1373

    
1374
    var body = response.body();
1375
    var result = '';
1376
    switch (response.command()) {
1377
      case 'suspend':
1378
        details.text = 'stopped';
1379
        break;
1380

    
1381
      case 'setbreakpoint':
1382
        result = 'set breakpoint #';
1383
        result += body.breakpoint;
1384
        details.text = result;
1385
        break;
1386

    
1387
      case 'clearbreakpoint':
1388
        result = 'cleared breakpoint #';
1389
        result += body.breakpoint;
1390
        details.text = result;
1391
        break;
1392

    
1393
      case 'changebreakpoint':
1394
        result = 'successfully changed breakpoint';
1395
        details.text = result;
1396
        break;
1397

    
1398
      case 'listbreakpoints':
1399
        result = 'breakpoints: (' + body.breakpoints.length + ')';
1400
        for (var i = 0; i < body.breakpoints.length; i++) {
1401
          var breakpoint = body.breakpoints[i];
1402
          result += '\n id=' + breakpoint.number;
1403
          result += ' type=' + breakpoint.type;
1404
          if (breakpoint.script_id) {
1405
              result += ' script_id=' + breakpoint.script_id;
1406
          }
1407
          if (breakpoint.script_name) {
1408
              result += ' script_name=' + breakpoint.script_name;
1409
          }
1410
          if (breakpoint.script_regexp) {
1411
              result += ' script_regexp=' + breakpoint.script_regexp;
1412
          }
1413
          result += ' line=' + (breakpoint.line + 1);
1414
          if (breakpoint.column != null) {
1415
            result += ' column=' + (breakpoint.column + 1);
1416
          }
1417
          if (breakpoint.groupId) {
1418
            result += ' groupId=' + breakpoint.groupId;
1419
          }
1420
          if (breakpoint.ignoreCount) {
1421
              result += ' ignoreCount=' + breakpoint.ignoreCount;
1422
          }
1423
          if (breakpoint.active === false) {
1424
            result += ' inactive';
1425
          }
1426
          if (breakpoint.condition) {
1427
            result += ' condition=' + breakpoint.condition;
1428
          }
1429
          result += ' hit_count=' + breakpoint.hit_count;
1430
        }
1431
        if (body.breakpoints.length === 0) {
1432
          result = "No user defined breakpoints\n";
1433
        } else {
1434
          result += '\n';
1435
        }
1436
        if (body.breakOnExceptions) {
1437
          result += '* breaking on ALL exceptions is enabled\n';
1438
        } else if (body.breakOnUncaughtExceptions) {
1439
          result += '* breaking on UNCAUGHT exceptions is enabled\n';
1440
        } else {
1441
          result += '* all exception breakpoints are disabled\n';
1442
        }
1443
        details.text = result;
1444
        break;
1445

    
1446
      case 'setexceptionbreak':
1447
        result = 'Break on ' + body.type + ' exceptions: ';
1448
        result += body.enabled ? 'enabled' : 'disabled';
1449
        details.text = result;
1450
        break;
1451

    
1452
      case 'backtrace':
1453
        if (body.totalFrames == 0) {
1454
          result = '(empty stack)';
1455
        } else {
1456
          var result = 'Frames #' + body.fromFrame + ' to #' +
1457
              (body.toFrame - 1) + ' of ' + body.totalFrames + '\n';
1458
          for (i = 0; i < body.frames.length; i++) {
1459
            if (i != 0) result += '\n';
1460
            result += body.frames[i].text;
1461
          }
1462
        }
1463
        details.text = result;
1464
        break;
1465

    
1466
      case 'frame':
1467
        if (last_cmd === 'info locals') {
1468
          var locals = body.locals;
1469
          if (locals.length === 0) {
1470
            result = 'No locals';
1471
          } else {
1472
            for (var i = 0; i < locals.length; i++) {
1473
              var local = locals[i];
1474
              result += local.name + ' = ';
1475
              result += refObjectToString_(response, local.value.ref);
1476
              result += '\n';
1477
            }
1478
          }
1479
        } else if (last_cmd === 'info args') {
1480
          var args = body.arguments;
1481
          if (args.length === 0) {
1482
            result = 'No arguments';
1483
          } else {
1484
            for (var i = 0; i < args.length; i++) {
1485
              var arg = args[i];
1486
              result += arg.name + ' = ';
1487
              result += refObjectToString_(response, arg.value.ref);
1488
              result += '\n';
1489
            }
1490
          }
1491
        } else {
1492
          result = SourceUnderline(body.sourceLineText,
1493
                                   body.column);
1494
          Debug.State.currentSourceLine = body.line;
1495
          Debug.State.currentFrame = body.index;
1496
          Debug.State.displaySourceStartLine = -1;
1497
          Debug.State.displaySourceEndLine = -1;
1498
        }
1499
        details.text = result;
1500
        break;
1501

    
1502
      case 'scopes':
1503
        if (body.totalScopes == 0) {
1504
          result = '(no scopes)';
1505
        } else {
1506
          result = 'Scopes #' + body.fromScope + ' to #' +
1507
                   (body.toScope - 1) + ' of ' + body.totalScopes + '\n';
1508
          for (i = 0; i < body.scopes.length; i++) {
1509
            if (i != 0) {
1510
              result += '\n';
1511
            }
1512
            result += formatScope_(body.scopes[i]);
1513
          }
1514
        }
1515
        details.text = result;
1516
        break;
1517

    
1518
      case 'scope':
1519
        result += formatScope_(body);
1520
        result += '\n';
1521
        var scope_object_value = response.lookup(body.object.ref);
1522
        result += formatObject_(scope_object_value, true);
1523
        details.text = result;
1524
        break;
1525

    
1526
      case 'evaluate':
1527
      case 'lookup':
1528
      case 'getobj':
1529
        if (last_cmd == 'p' || last_cmd == 'print') {
1530
          result = body.text;
1531
        } else {
1532
          var value;
1533
          if (lookup_handle) {
1534
            value = response.bodyValue(lookup_handle);
1535
          } else {
1536
            value = response.bodyValue();
1537
          }
1538
          if (value.isObject()) {
1539
            result += formatObject_(value, true);
1540
          } else {
1541
            result += 'type: ';
1542
            result += value.type();
1543
            if (!value.isUndefined() && !value.isNull()) {
1544
              result += ', ';
1545
              if (value.isString()) {
1546
                result += '"';
1547
              }
1548
              result += value.value();
1549
              if (value.isString()) {
1550
                result += '"';
1551
              }
1552
            }
1553
            result += '\n';
1554
          }
1555
        }
1556
        details.text = result;
1557
        break;
1558

    
1559
      case 'references':
1560
        var count = body.length;
1561
        result += 'found ' + count + ' objects';
1562
        result += '\n';
1563
        for (var i = 0; i < count; i++) {
1564
          var value = response.bodyValue(i);
1565
          result += formatObject_(value, false);
1566
          result += '\n';
1567
        }
1568
        details.text = result;
1569
        break;
1570

    
1571
      case 'source':
1572
        // Get the source from the response.
1573
        var source = body.source;
1574
        var from_line = body.fromLine + 1;
1575
        var lines = source.split('\n');
1576
        var maxdigits = 1 + Math.floor(log10(from_line + lines.length));
1577
        if (maxdigits < 3) {
1578
          maxdigits = 3;
1579
        }
1580
        var result = '';
1581
        for (var num = 0; num < lines.length; num++) {
1582
          // Check if there's an extra newline at the end.
1583
          if (num == (lines.length - 1) && lines[num].length == 0) {
1584
            break;
1585
          }
1586

    
1587
          var current_line = from_line + num;
1588
          var spacer = maxdigits - (1 + Math.floor(log10(current_line)));
1589
          if (current_line == Debug.State.currentSourceLine + 1) {
1590
            for (var i = 0; i < maxdigits; i++) {
1591
              result += '>';
1592
            }
1593
            result += '  ';
1594
          } else {
1595
            for (var i = 0; i < spacer; i++) {
1596
              result += ' ';
1597
            }
1598
            result += current_line + ': ';
1599
          }
1600
          result += lines[num];
1601
          result += '\n';
1602
        }
1603
        details.text = result;
1604
        break;
1605

    
1606
      case 'scripts':
1607
        var result = '';
1608
        for (i = 0; i < body.length; i++) {
1609
          if (i != 0) result += '\n';
1610
          if (body[i].id) {
1611
            result += body[i].id;
1612
          } else {
1613
            result += '[no id]';
1614
          }
1615
          result += ', ';
1616
          if (body[i].name) {
1617
            result += body[i].name;
1618
          } else {
1619
            if (body[i].compilationType == Debug.ScriptCompilationType.Eval
1620
                && body[i].evalFromScript
1621
                ) {
1622
              result += 'eval from ';
1623
              var script_value = response.lookup(body[i].evalFromScript.ref);
1624
              result += ' ' + script_value.field('name');
1625
              result += ':' + (body[i].evalFromLocation.line + 1);
1626
              result += ':' + body[i].evalFromLocation.column;
1627
            } else if (body[i].compilationType ==
1628
                       Debug.ScriptCompilationType.JSON) {
1629
              result += 'JSON ';
1630
            } else {  // body[i].compilation == Debug.ScriptCompilationType.Host
1631
              result += '[unnamed] ';
1632
            }
1633
          }
1634
          result += ' (lines: ';
1635
          result += body[i].lineCount;
1636
          result += ', length: ';
1637
          result += body[i].sourceLength;
1638
          if (body[i].type == Debug.ScriptType.Native) {
1639
            result += ', native';
1640
          } else if (body[i].type == Debug.ScriptType.Extension) {
1641
            result += ', extension';
1642
          }
1643
          result += '), [';
1644
          var sourceStart = body[i].sourceStart;
1645
          if (sourceStart.length > 40) {
1646
            sourceStart = sourceStart.substring(0, 37) + '...';
1647
          }
1648
          result += sourceStart;
1649
          result += ']';
1650
        }
1651
        if (body.length == 0) {
1652
          result = "no matching scripts found";
1653
        }
1654
        details.text = result;
1655
        break;
1656

    
1657
      case 'threads':
1658
        var result = 'Active V8 threads: ' + body.totalThreads + '\n';
1659
        body.threads.sort(function(a, b) { return a.id - b.id; });
1660
        for (i = 0; i < body.threads.length; i++) {
1661
          result += body.threads[i].current ? '*' : ' ';
1662
          result += ' ';
1663
          result += body.threads[i].id;
1664
          result += '\n';
1665
        }
1666
        details.text = result;
1667
        break;
1668

    
1669
      case 'continue':
1670
        details.text = "(running)";
1671
        break;
1672

    
1673
      case 'v8flags':
1674
        details.text = "flags set";
1675
        break;
1676

    
1677
      case 'gc':
1678
        details.text = "GC " + body.before + " => " + body.after;
1679
        if (body.after > (1024*1024)) {
1680
          details.text +=
1681
              " (" + roundNumber(body.before/(1024*1024), 1) + "M => " +
1682
                     roundNumber(body.after/(1024*1024), 1) + "M)";
1683
        } else if (body.after > 1024) {
1684
          details.text +=
1685
              " (" + roundNumber(body.before/1024, 1) + "K => " +
1686
                     roundNumber(body.after/1024, 1) + "K)";
1687
        }
1688
        break;
1689

    
1690
      default:
1691
        details.text =
1692
            'Response for unknown command \'' + response.command() + '\'' +
1693
            ' (' + response.raw_json() + ')';
1694
    }
1695
  } catch (e) {
1696
    details.text = 'Error: "' + e + '" formatting response';
1697
  }
1698

    
1699
  return details;
1700
}
1701

    
1702

    
1703
/**
1704
 * Protocol packages send from the debugger.
1705
 * @param {string} json - raw protocol packet as JSON string.
1706
 * @constructor
1707
 */
1708
function ProtocolPackage(json) {
1709
  this.raw_json_ = json;
1710
  this.packet_ = JSON.parse(json);
1711
  this.refs_ = [];
1712
  if (this.packet_.refs) {
1713
    for (var i = 0; i < this.packet_.refs.length; i++) {
1714
      this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
1715
    }
1716
  }
1717
}
1718

    
1719

    
1720
/**
1721
 * Get the packet type.
1722
 * @return {String} the packet type
1723
 */
1724
ProtocolPackage.prototype.type = function() {
1725
  return this.packet_.type;
1726
};
1727

    
1728

    
1729
/**
1730
 * Get the packet event.
1731
 * @return {Object} the packet event
1732
 */
1733
ProtocolPackage.prototype.event = function() {
1734
  return this.packet_.event;
1735
};
1736

    
1737

    
1738
/**
1739
 * Get the packet request sequence.
1740
 * @return {number} the packet request sequence
1741
 */
1742
ProtocolPackage.prototype.requestSeq = function() {
1743
  return this.packet_.request_seq;
1744
};
1745

    
1746

    
1747
/**
1748
 * Get the packet request sequence.
1749
 * @return {number} the packet request sequence
1750
 */
1751
ProtocolPackage.prototype.running = function() {
1752
  return this.packet_.running ? true : false;
1753
};
1754

    
1755

    
1756
ProtocolPackage.prototype.success = function() {
1757
  return this.packet_.success ? true : false;
1758
};
1759

    
1760

    
1761
ProtocolPackage.prototype.message = function() {
1762
  return this.packet_.message;
1763
};
1764

    
1765

    
1766
ProtocolPackage.prototype.command = function() {
1767
  return this.packet_.command;
1768
};
1769

    
1770

    
1771
ProtocolPackage.prototype.body = function() {
1772
  return this.packet_.body;
1773
};
1774

    
1775

    
1776
ProtocolPackage.prototype.bodyValue = function(index) {
1777
  if (index != null) {
1778
    return new ProtocolValue(this.packet_.body[index], this);
1779
  } else {
1780
    return new ProtocolValue(this.packet_.body, this);
1781
  }
1782
};
1783

    
1784

    
1785
ProtocolPackage.prototype.body = function() {
1786
  return this.packet_.body;
1787
};
1788

    
1789

    
1790
ProtocolPackage.prototype.lookup = function(handle) {
1791
  var value = this.refs_[handle];
1792
  if (value) {
1793
    return new ProtocolValue(value, this);
1794
  } else {
1795
    return new ProtocolReference(handle);
1796
  }
1797
};
1798

    
1799

    
1800
ProtocolPackage.prototype.raw_json = function() {
1801
  return this.raw_json_;
1802
};
1803

    
1804

    
1805
function ProtocolValue(value, packet) {
1806
  this.value_ = value;
1807
  this.packet_ = packet;
1808
}
1809

    
1810

    
1811
/**
1812
 * Get the value type.
1813
 * @return {String} the value type
1814
 */
1815
ProtocolValue.prototype.type = function() {
1816
  return this.value_.type;
1817
};
1818

    
1819

    
1820
/**
1821
 * Get a metadata field from a protocol value.
1822
 * @return {Object} the metadata field value
1823
 */
1824
ProtocolValue.prototype.field = function(name) {
1825
  return this.value_[name];
1826
};
1827

    
1828

    
1829
/**
1830
 * Check is the value is a primitive value.
1831
 * @return {boolean} true if the value is primitive
1832
 */
1833
ProtocolValue.prototype.isPrimitive = function() {
1834
  return this.isUndefined() || this.isNull() || this.isBoolean() ||
1835
         this.isNumber() || this.isString();
1836
};
1837

    
1838

    
1839
/**
1840
 * Get the object handle.
1841
 * @return {number} the value handle
1842
 */
1843
ProtocolValue.prototype.handle = function() {
1844
  return this.value_.handle;
1845
};
1846

    
1847

    
1848
/**
1849
 * Check is the value is undefined.
1850
 * @return {boolean} true if the value is undefined
1851
 */
1852
ProtocolValue.prototype.isUndefined = function() {
1853
  return this.value_.type == 'undefined';
1854
};
1855

    
1856

    
1857
/**
1858
 * Check is the value is null.
1859
 * @return {boolean} true if the value is null
1860
 */
1861
ProtocolValue.prototype.isNull = function() {
1862
  return this.value_.type == 'null';
1863
};
1864

    
1865

    
1866
/**
1867
 * Check is the value is a boolean.
1868
 * @return {boolean} true if the value is a boolean
1869
 */
1870
ProtocolValue.prototype.isBoolean = function() {
1871
  return this.value_.type == 'boolean';
1872
};
1873

    
1874

    
1875
/**
1876
 * Check is the value is a number.
1877
 * @return {boolean} true if the value is a number
1878
 */
1879
ProtocolValue.prototype.isNumber = function() {
1880
  return this.value_.type == 'number';
1881
};
1882

    
1883

    
1884
/**
1885
 * Check is the value is a string.
1886
 * @return {boolean} true if the value is a string
1887
 */
1888
ProtocolValue.prototype.isString = function() {
1889
  return this.value_.type == 'string';
1890
};
1891

    
1892

    
1893
/**
1894
 * Check is the value is an object.
1895
 * @return {boolean} true if the value is an object
1896
 */
1897
ProtocolValue.prototype.isObject = function() {
1898
  return this.value_.type == 'object' || this.value_.type == 'function' ||
1899
         this.value_.type == 'error' || this.value_.type == 'regexp';
1900
};
1901

    
1902

    
1903
/**
1904
 * Get the constructor function
1905
 * @return {ProtocolValue} constructor function
1906
 */
1907
ProtocolValue.prototype.constructorFunctionValue = function() {
1908
  var ctor = this.value_.constructorFunction;
1909
  return this.packet_.lookup(ctor.ref);
1910
};
1911

    
1912

    
1913
/**
1914
 * Get the __proto__ value
1915
 * @return {ProtocolValue} __proto__ value
1916
 */
1917
ProtocolValue.prototype.protoObjectValue = function() {
1918
  var proto = this.value_.protoObject;
1919
  return this.packet_.lookup(proto.ref);
1920
};
1921

    
1922

    
1923
/**
1924
 * Get the number og properties.
1925
 * @return {number} the number of properties
1926
 */
1927
ProtocolValue.prototype.propertyCount = function() {
1928
  return this.value_.properties ? this.value_.properties.length : 0;
1929
};
1930

    
1931

    
1932
/**
1933
 * Get the specified property name.
1934
 * @return {string} property name
1935
 */
1936
ProtocolValue.prototype.propertyName = function(index) {
1937
  var property = this.value_.properties[index];
1938
  return property.name;
1939
};
1940

    
1941

    
1942
/**
1943
 * Return index for the property name.
1944
 * @param name The property name to look for
1945
 * @return {number} index for the property name
1946
 */
1947
ProtocolValue.prototype.propertyIndex = function(name) {
1948
  for (var i = 0; i < this.propertyCount(); i++) {
1949
    if (this.value_.properties[i].name == name) {
1950
      return i;
1951
    }
1952
  }
1953
  return null;
1954
};
1955

    
1956

    
1957
/**
1958
 * Get the specified property value.
1959
 * @return {ProtocolValue} property value
1960
 */
1961
ProtocolValue.prototype.propertyValue = function(index) {
1962
  var property = this.value_.properties[index];
1963
  return this.packet_.lookup(property.ref);
1964
};
1965

    
1966

    
1967
/**
1968
 * Check is the value is a string.
1969
 * @return {boolean} true if the value is a string
1970
 */
1971
ProtocolValue.prototype.value = function() {
1972
  return this.value_.value;
1973
};
1974

    
1975

    
1976
ProtocolValue.prototype.valueString = function() {
1977
  return this.value_.text;
1978
};
1979

    
1980

    
1981
function ProtocolReference(handle) {
1982
  this.handle_ = handle;
1983
}
1984

    
1985

    
1986
ProtocolReference.prototype.handle = function() {
1987
  return this.handle_;
1988
};
1989

    
1990

    
1991
function MakeJSONPair_(name, value) {
1992
  return '"' + name + '":' + value;
1993
}
1994

    
1995

    
1996
function ArrayToJSONObject_(content) {
1997
  return '{' + content.join(',') + '}';
1998
}
1999

    
2000

    
2001
function ArrayToJSONArray_(content) {
2002
  return '[' + content.join(',') + ']';
2003
}
2004

    
2005

    
2006
function BooleanToJSON_(value) {
2007
  return String(value);
2008
}
2009

    
2010

    
2011
function NumberToJSON_(value) {
2012
  return String(value);
2013
}
2014

    
2015

    
2016
// Mapping of some control characters to avoid the \uXXXX syntax for most
2017
// commonly used control cahracters.
2018
var ctrlCharMap_ = {
2019
  '\b': '\\b',
2020
  '\t': '\\t',
2021
  '\n': '\\n',
2022
  '\f': '\\f',
2023
  '\r': '\\r',
2024
  '"' : '\\"',
2025
  '\\': '\\\\'
2026
};
2027

    
2028

    
2029
// Regular expression testing for ", \ and control characters (0x00 - 0x1F).
2030
var ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]');
2031

    
2032

    
2033
// Regular expression matching ", \ and control characters (0x00 - 0x1F)
2034
// globally.
2035
var ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g');
2036

    
2037

    
2038
/**
2039
 * Convert a String to its JSON representation (see http://www.json.org/). To
2040
 * avoid depending on the String object this method calls the functions in
2041
 * string.js directly and not through the value.
2042
 * @param {String} value The String value to format as JSON
2043
 * @return {string} JSON formatted String value
2044
 */
2045
function StringToJSON_(value) {
2046
  // Check for" , \ and control characters (0x00 - 0x1F). No need to call
2047
  // RegExpTest as ctrlchar is constructed using RegExp.
2048
  if (ctrlCharTest_.test(value)) {
2049
    // Replace ", \ and control characters (0x00 - 0x1F).
2050
    return '"' +
2051
      value.replace(ctrlCharMatch_, function (char) {
2052
        // Use charmap if possible.
2053
        var mapped = ctrlCharMap_[char];
2054
        if (mapped) return mapped;
2055
        mapped = char.charCodeAt();
2056
        // Convert control character to unicode escape sequence.
2057
        return '\\u00' +
2058
          '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) +
2059
          '0'; // TODO %NumberToRadixString(mapped % 16, 16)
2060
      })
2061
    + '"';
2062
  }
2063

    
2064
  // Simple string with no special characters.
2065
  return '"' + value + '"';
2066
}
2067

    
2068

    
2069
/**
2070
 * Convert a Date to ISO 8601 format. To avoid depending on the Date object
2071
 * this method calls the functions in date.js directly and not through the
2072
 * value.
2073
 * @param {Date} value The Date value to format as JSON
2074
 * @return {string} JSON formatted Date value
2075
 */
2076
function DateToISO8601_(value) {
2077
  var f = function(n) {
2078
    return n < 10 ? '0' + n : n;
2079
  };
2080
  var g = function(n) {
2081
    return n < 10 ? '00' + n : n < 100 ? '0' + n : n;
2082
  };
2083
  return builtins.GetUTCFullYearFrom(value)         + '-' +
2084
          f(builtins.GetUTCMonthFrom(value) + 1)    + '-' +
2085
          f(builtins.GetUTCDateFrom(value))         + 'T' +
2086
          f(builtins.GetUTCHoursFrom(value))        + ':' +
2087
          f(builtins.GetUTCMinutesFrom(value))      + ':' +
2088
          f(builtins.GetUTCSecondsFrom(value))      + '.' +
2089
          g(builtins.GetUTCMillisecondsFrom(value)) + 'Z';
2090
}
2091

    
2092

    
2093
/**
2094
 * Convert a Date to ISO 8601 format. To avoid depending on the Date object
2095
 * this method calls the functions in date.js directly and not through the
2096
 * value.
2097
 * @param {Date} value The Date value to format as JSON
2098
 * @return {string} JSON formatted Date value
2099
 */
2100
function DateToJSON_(value) {
2101
  return '"' + DateToISO8601_(value) + '"';
2102
}
2103

    
2104

    
2105
/**
2106
 * Convert an Object to its JSON representation (see http://www.json.org/).
2107
 * This implementation simply runs through all string property names and adds
2108
 * each property to the JSON representation for some predefined types. For type
2109
 * "object" the function calls itself recursively unless the object has the
2110
 * function property "toJSONProtocol" in which case that is used. This is not
2111
 * a general implementation but sufficient for the debugger. Note that circular
2112
 * structures will cause infinite recursion.
2113
 * @param {Object} object The object to format as JSON
2114
 * @return {string} JSON formatted object value
2115
 */
2116
function SimpleObjectToJSON_(object) {
2117
  var content = [];
2118
  for (var key in object) {
2119
    // Only consider string keys.
2120
    if (typeof key == 'string') {
2121
      var property_value = object[key];
2122

    
2123
      // Format the value based on its type.
2124
      var property_value_json;
2125
      switch (typeof property_value) {
2126
        case 'object':
2127
          if (IS_NULL(property_value)) {
2128
            property_value_json = 'null';
2129
          } else if (typeof property_value.toJSONProtocol == 'function') {
2130
            property_value_json = property_value.toJSONProtocol(true);
2131
          } else if (property_value.constructor.name == 'Array'){
2132
            property_value_json = SimpleArrayToJSON_(property_value);
2133
          } else {
2134
            property_value_json = SimpleObjectToJSON_(property_value);
2135
          }
2136
          break;
2137

    
2138
        case 'boolean':
2139
          property_value_json = BooleanToJSON_(property_value);
2140
          break;
2141

    
2142
        case 'number':
2143
          property_value_json = NumberToJSON_(property_value);
2144
          break;
2145

    
2146
        case 'string':
2147
          property_value_json = StringToJSON_(property_value);
2148
          break;
2149

    
2150
        default:
2151
          property_value_json = null;
2152
      }
2153

    
2154
      // Add the property if relevant.
2155
      if (property_value_json) {
2156
        content.push(StringToJSON_(key) + ':' + property_value_json);
2157
      }
2158
    }
2159
  }
2160

    
2161
  // Make JSON object representation.
2162
  return '{' + content.join(',') + '}';
2163
}
2164

    
2165

    
2166
/**
2167
 * Convert an array to its JSON representation. This is a VERY simple
2168
 * implementation just to support what is needed for the debugger.
2169
 * @param {Array} arrya The array to format as JSON
2170
 * @return {string} JSON formatted array value
2171
 */
2172
function SimpleArrayToJSON_(array) {
2173
  // Make JSON array representation.
2174
  var json = '[';
2175
  for (var i = 0; i < array.length; i++) {
2176
    if (i != 0) {
2177
      json += ',';
2178
    }
2179
    var elem = array[i];
2180
    if (elem.toJSONProtocol) {
2181
      json += elem.toJSONProtocol(true);
2182
    } else if (typeof(elem) === 'object')  {
2183
      json += SimpleObjectToJSON_(elem);
2184
    } else if (typeof(elem) === 'boolean')  {
2185
      json += BooleanToJSON_(elem);
2186
    } else if (typeof(elem) === 'number')  {
2187
      json += NumberToJSON_(elem);
2188
    } else if (typeof(elem) === 'string')  {
2189
      json += StringToJSON_(elem);
2190
    } else {
2191
      json += elem;
2192
    }
2193
  }
2194
  json += ']';
2195
  return json;
2196
}
2197

    
2198

    
2199
// A more universal stringify that supports more types than JSON.
2200
// Used by the d8 shell to output results.
2201
var stringifyDepthLimit = 4;  // To avoid crashing on cyclic objects
2202

    
2203
function Stringify(x, depth) {
2204
  if (depth === undefined)
2205
    depth = stringifyDepthLimit;
2206
  else if (depth === 0)
2207
    return "*";
2208
  switch (typeof x) {
2209
    case "undefined":
2210
      return "undefined";
2211
    case "boolean":
2212
    case "number":
2213
    case "function":
2214
      return x.toString();
2215
    case "string":
2216
      return "\"" + x.toString() + "\"";
2217
    case "symbol":
2218
      return "Symbol(" + (x.name ? Stringify(x.name, depth) : "") + ")"
2219
    case "object":
2220
      if (IS_NULL(x)) return "null";
2221
      if (x.constructor && x.constructor.name === "Array") {
2222
        var elems = [];
2223
        for (var i = 0; i < x.length; ++i) {
2224
          elems.push(
2225
            {}.hasOwnProperty.call(x, i) ? Stringify(x[i], depth - 1) : "");
2226
        }
2227
        return "[" + elems.join(", ") + "]";
2228
      }
2229
      try {
2230
        var string = String(x);
2231
        if (string && string !== "[object Object]") return string;
2232
      } catch(e) {}
2233
      var props = [];
2234
      for (var name in x) {
2235
        var desc = Object.getOwnPropertyDescriptor(x, name);
2236
        if (IS_UNDEFINED(desc)) continue;
2237
        if ("value" in desc) {
2238
          props.push(name + ": " + Stringify(desc.value, depth - 1));
2239
        }
2240
        if ("get" in desc) {
2241
          var getter = desc.get.toString();
2242
          props.push("get " + name + getter.slice(getter.indexOf('(')));
2243
        }
2244
        if ("set" in desc) {
2245
          var setter = desc.set.toString();
2246
          props.push("set " + name + setter.slice(setter.indexOf('(')));
2247
        }
2248
      }
2249
      return "{" + props.join(", ") + "}";
2250
    default:
2251
      return "[crazy non-standard shit]";
2252
  }
2253
}