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 @ 40c0f755

History | View | Annotate | Download (38.1 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
// How crappy is it that I have to implement completely basic stuff
29
// like this myself?  Answer: very.
30
String.prototype.startsWith = function (str) {
31
  if (str.length > this.length)
32
    return false;
33
  return this.substr(0, str.length) == str;
34
}
35

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

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

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

    
76

    
77
// Global object holding debugger related constants and state.
78
const Debug = {};
79

    
80

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

    
89

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

    
95

    
96
// Current debug state.
97
const kNoFrame = -1;
98
Debug.State = {
99
  currentFrame: kNoFrame,
100
  currentSourceLine: -1
101
}
102
var trace_compile = false;  // Tracing all compile events?
103

    
104

    
105
// Process a debugger JSON message into a display text and a running status.
106
// This function returns an object with properties "text" and "running" holding
107
// this information.
108
function DebugMessageDetails(message) {
109
  // Convert the JSON string to an object.
110
  var response = new ProtocolPackage(message);
111

    
112
  if (response.type() == 'event') {
113
    return DebugEventDetails(response);
114
  } else {
115
    return DebugResponseDetails(response);
116
  }
117
}
118

    
119
function DebugEventDetails(response) {
120
  details = {text:'', running:false}
121

    
122
  // Get the running state.
123
  details.running = response.running();
124

    
125
  var body = response.body();
126
  var result = '';
127
  switch (response.event()) {
128
    case 'break':
129
      if (body.breakpoints) {
130
        result += 'breakpoint';
131
        if (body.breakpoints.length > 1) {
132
          result += 's';
133
        }
134
        result += ' #';
135
        for (var i = 0; i < body.breakpoints.length; i++) {
136
          if (i > 0) {
137
            result += ', #';
138
          }
139
          result += body.breakpoints[i];
140
        }
141
      } else {
142
        result += 'break';
143
      }
144
      result += ' in ';
145
      result += body.invocationText;
146
      result += ', ';
147
      result += SourceInfo(body);
148
      result += '\n';
149
      result += SourceUnderline(body.sourceLineText, body.sourceColumn);
150
      Debug.State.currentSourceLine = body.sourceLine;
151
      Debug.State.currentFrame = 0;
152
      details.text = result;
153
      break;
154
      
155
    case 'exception':
156
      if (body.uncaught) {
157
        result += 'Uncaught: ';
158
      } else {
159
        result += 'Exception: ';
160
      }
161
      result += '"';
162
      result += body.exception.text;
163
      result += '"';
164
      if (body.sourceLine >= 0) {
165
        result += ', ';
166
        result += SourceInfo(body);
167
        result += '\n';
168
        result += SourceUnderline(body.sourceLineText, body.sourceColumn);
169
        Debug.State.currentSourceLine = body.sourceLine;
170
        Debug.State.currentFrame = 0;
171
      } else {
172
        result += ' (empty stack)';
173
        Debug.State.currentSourceLine = -1;
174
        Debug.State.currentFrame = kNoFrame;
175
      }
176
      details.text = result;
177
      break;
178

    
179
    case 'afterCompile':
180
      if (trace_compile) {
181
        result = 'Source ' + body.script.name + ' compiled:\n'
182
        var source = body.script.source;
183
        if (!(source[source.length - 1] == '\n')) {
184
          result += source;
185
        } else {
186
          result += source.substring(0, source.length - 1);
187
        }
188
      }
189
      details.text = result;
190
      break;
191

    
192
    default:
193
      details.text = 'Unknown debug event ' + response.event();
194
  }
195

    
196
  return details;
197
};
198

    
199

    
200
function SourceInfo(body) {
201
  var result = '';
202
  
203
  if (body.script) {
204
    if (body.script.name) {
205
      result += body.script.name;
206
    } else {
207
      result += '[unnamed]';
208
    }
209
  }
210
  result += ' line ';
211
  result += body.sourceLine + 1;
212
  result += ' column ';
213
  result += body.sourceColumn + 1;
214
  
215
  return result;
216
}
217

    
218

    
219
function SourceUnderline(source_text, position) {
220
  if (!source_text) {
221
    return;
222
  }
223

    
224
  // Create an underline with a caret pointing to the source position. If the
225
  // source contains a tab character the underline will have a tab character in
226
  // the same place otherwise the underline will have a space character.
227
  var underline = '';
228
  for (var i = 0; i < position; i++) {
229
    if (source_text[i] == '\t') {
230
      underline += '\t';
231
    } else {
232
      underline += ' ';
233
    }
234
  }
235
  underline += '^';
236

    
237
  // Return the source line text with the underline beneath.
238
  return source_text + '\n' + underline;
239
};
240

    
241

    
242
// Converts a text command to a JSON request.
243
function DebugCommandToJSONRequest(cmd_line) {
244
  return new DebugRequest(cmd_line).JSONRequest();
245
};
246

    
247

    
248
function DebugRequest(cmd_line) {
249
  // If the very first character is a { assume that a JSON request have been
250
  // entered as a command. Converting that to a JSON request is trivial.
251
  if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') {
252
    this.request_ = cmd_line;
253
    return;
254
  }
255

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

    
259
  // Find the command.
260
  var pos = cmd_line.indexOf(' ');
261
  var cmd;
262
  var args;
263
  if (pos == -1) {
264
    cmd = cmd_line;
265
    args = '';
266
  } else {
267
    cmd = cmd_line.slice(0, pos);
268
    args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, '');
269
  }
270

    
271
  // Switch on command.
272
  switch (cmd) {
273
    case 'continue':
274
    case 'c':
275
      this.request_ = this.continueCommandToJSONRequest_(args);
276
      break;
277

    
278
    case 'step':
279
    case 's':
280
      this.request_ = this.stepCommandToJSONRequest_(args);
281
      break;
282

    
283
    case 'backtrace':
284
    case 'bt':
285
      this.request_ = this.backtraceCommandToJSONRequest_(args);
286
      break;
287
      
288
    case 'frame':
289
    case 'f':
290
      this.request_ = this.frameCommandToJSONRequest_(args);
291
      break;
292
      
293
    case 'print':
294
    case 'p':
295
      this.request_ = this.printCommandToJSONRequest_(args);
296
      break;
297

    
298
    case 'dir':
299
      this.request_ = this.dirCommandToJSONRequest_(args);
300
      break;
301

    
302
    case 'references':
303
      this.request_ = this.referencesCommandToJSONRequest_(args);
304
      break;
305

    
306
    case 'instances':
307
      this.request_ = this.instancesCommandToJSONRequest_(args);
308
      break;
309

    
310
    case 'source':
311
      this.request_ = this.sourceCommandToJSONRequest_(args);
312
      break;
313
      
314
    case 'scripts':
315
      this.request_ = this.scriptsCommandToJSONRequest_(args);
316
      break;
317
      
318
    case 'break':
319
    case 'b':
320
      this.request_ = this.breakCommandToJSONRequest_(args);
321
      break;
322
      
323
    case 'clear':
324
      this.request_ = this.clearCommandToJSONRequest_(args);
325
      break;
326

    
327
    case 'threads':
328
      this.request_ = this.threadsCommandToJSONRequest_(args);
329
      break;
330

    
331
    case 'trace':
332
      // Return undefined to indicate command handled internally (no JSON).
333
      this.request_ = void 0;
334
      this.traceCommand_(args);
335
      break;
336

    
337
    case 'help':
338
    case '?':
339
      this.helpCommand_(args);
340
      // Return undefined to indicate command handled internally (no JSON).
341
      this.request_ = void 0;
342
      break;
343

    
344
    default:
345
      throw new Error('Unknown command "' + cmd + '"');
346
  }
347
  
348
  last_cmd = cmd;
349
}
350

    
351
DebugRequest.prototype.JSONRequest = function() {
352
  return this.request_;
353
}
354

    
355

    
356
function RequestPacket(command) {
357
  this.seq = 0;
358
  this.type = 'request';
359
  this.command = command;
360
}
361

    
362

    
363
RequestPacket.prototype.toJSONProtocol = function() {
364
  // Encode the protocol header.
365
  var json = '{';
366
  json += '"seq":' + this.seq;
367
  json += ',"type":"' + this.type + '"';
368
  if (this.command) {
369
    json += ',"command":' + StringToJSON_(this.command);
370
  }
371
  if (this.arguments) {
372
    json += ',"arguments":';
373
    // Encode the arguments part.
374
    if (this.arguments.toJSONProtocol) {
375
      json += this.arguments.toJSONProtocol()
376
    } else {
377
      json += SimpleObjectToJSON_(this.arguments);
378
    }
379
  }
380
  json += '}';
381
  return json;
382
}
383

    
384

    
385
DebugRequest.prototype.createRequest = function(command) {
386
  return new RequestPacket(command);
387
};
388

    
389

    
390
// Create a JSON request for the evaluation command.
391
DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
392
  // Check if the expression is a handle id in the form #<handle>#.
393
  var handle_match = expression.match(/^#([0-9]*)#$/);
394
  if (handle_match) {
395
    // Build a lookup request.
396
    var request = this.createRequest('lookup');
397
    request.arguments = {};
398
    request.arguments.handle = parseInt(handle_match[1]);
399
    return request.toJSONProtocol();
400
  } else {
401
    // Build an evaluate request.
402
    var request = this.createRequest('evaluate');
403
    request.arguments = {};
404
    request.arguments.expression = expression;
405
    // Request a global evaluation if there is no current frame.
406
    if (Debug.State.currentFrame == kNoFrame) {
407
      request.arguments.global = true;
408
    }
409
    return request.toJSONProtocol();
410
  }
411
};
412

    
413

    
414
// Create a JSON request for the references/instances command.
415
DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
416
  // Build a references request.
417
  var handle_match = handle.match(/^#([0-9]*)#$/);
418
  if (handle_match) {
419
    var request = this.createRequest('references');
420
    request.arguments = {};
421
    request.arguments.type = type;
422
    request.arguments.handle = parseInt(handle_match[1]);
423
    return request.toJSONProtocol();
424
  } else {
425
    throw new Error('Invalid object id.');
426
  }
427
};
428

    
429

    
430
// Create a JSON request for the continue command.
431
DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
432
  var request = this.createRequest('continue');
433
  return request.toJSONProtocol();
434
};
435

    
436

    
437
// Create a JSON request for the step command.
438
DebugRequest.prototype.stepCommandToJSONRequest_ = function(args) {
439
  // Requesting a step is through the continue command with additional
440
  // arguments.
441
  var request = this.createRequest('continue');
442
  request.arguments = {};
443

    
444
  // Process arguments if any.
445
  if (args && args.length > 0) {
446
    args = args.split(/\s*[ ]+\s*/g);
447

    
448
    if (args.length > 2) {
449
      throw new Error('Invalid step arguments.');
450
    }
451

    
452
    if (args.length > 0) {
453
      // Get step count argument if any.
454
      if (args.length == 2) {
455
        var stepcount = parseInt(args[1]);
456
        if (isNaN(stepcount) || stepcount <= 0) {
457
          throw new Error('Invalid step count argument "' + args[0] + '".');
458
        }
459
        request.arguments.stepcount = stepcount;
460
      }
461

    
462
      // Get the step action.
463
      switch (args[0]) {
464
        case 'in':
465
        case 'i':
466
          request.arguments.stepaction = 'in';
467
          break;
468
          
469
        case 'min':
470
        case 'm':
471
          request.arguments.stepaction = 'min';
472
          break;
473
          
474
        case 'next':
475
        case 'n':
476
          request.arguments.stepaction = 'next';
477
          break;
478
          
479
        case 'out':
480
        case 'o':
481
          request.arguments.stepaction = 'out';
482
          break;
483
          
484
        default:
485
          throw new Error('Invalid step argument "' + args[0] + '".');
486
      }
487
    }
488
  } else {
489
    // Default is step next.
490
    request.arguments.stepaction = 'next';
491
  }
492

    
493
  return request.toJSONProtocol();
494
};
495

    
496

    
497
// Create a JSON request for the backtrace command.
498
DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) {
499
  // Build a backtrace request from the text command.
500
  var request = this.createRequest('backtrace');
501
  args = args.split(/\s*[ ]+\s*/g);
502
  if (args.length == 2) {
503
    request.arguments = {};
504
    var fromFrame = parseInt(args[0]);
505
    var toFrame = parseInt(args[1]);
506
    if (isNaN(fromFrame) || fromFrame < 0) {
507
      throw new Error('Invalid start frame argument "' + args[0] + '".');
508
    }
509
    if (isNaN(toFrame) || toFrame < 0) {
510
      throw new Error('Invalid end frame argument "' + args[1] + '".');
511
    }
512
    if (fromFrame > toFrame) {
513
      throw new Error('Invalid arguments start frame cannot be larger ' +
514
                      'than end frame.');
515
    }
516
    request.arguments.fromFrame = fromFrame;
517
    request.arguments.toFrame = toFrame + 1;
518
  }
519
  return request.toJSONProtocol();
520
};
521

    
522

    
523
// Create a JSON request for the frame command.
524
DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) {
525
  // Build a frame request from the text command.
526
  var request = this.createRequest('frame');
527
  args = args.split(/\s*[ ]+\s*/g);
528
  if (args.length > 0 && args[0].length > 0) {
529
    request.arguments = {};
530
    request.arguments.number = args[0];
531
  }
532
  return request.toJSONProtocol();
533
};
534

    
535

    
536
// Create a JSON request for the print command.
537
DebugRequest.prototype.printCommandToJSONRequest_ = function(args) {
538
  // Build an evaluate request from the text command.
539
  if (args.length == 0) {
540
    throw new Error('Missing expression.');
541
  }
542
  return this.makeEvaluateJSONRequest_(args);
543
};
544

    
545

    
546
// Create a JSON request for the dir command.
547
DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
548
  // Build an evaluate request from the text command.
549
  if (args.length == 0) {
550
    throw new Error('Missing expression.');
551
  }
552
  return this.makeEvaluateJSONRequest_(args);
553
};
554

    
555

    
556
// Create a JSON request for the references command.
557
DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
558
  // Build an evaluate request from the text command.
559
  if (args.length == 0) {
560
    throw new Error('Missing object id.');
561
  }
562
  
563
  return this.makeReferencesJSONRequest_(args, 'referencedBy');
564
};
565

    
566

    
567
// Create a JSON request for the instances command.
568
DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
569
  // Build an evaluate request from the text command.
570
  if (args.length == 0) {
571
    throw new Error('Missing object id.');
572
  }
573
  
574
  // Build a references request.
575
  return this.makeReferencesJSONRequest_(args, 'constructedBy');
576
};
577

    
578

    
579
// Create a JSON request for the source command.
580
DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
581
  // Build a evaluate request from the text command.
582
  var request = this.createRequest('source');
583

    
584
  // Default is ten lines starting five lines before the current location.
585
  var from = Debug.State.currentSourceLine - 5;
586
  var lines = 10;
587

    
588
  // Parse the arguments.
589
  args = args.split(/\s*[ ]+\s*/g);
590
  if (args.length > 1 && args[0].length > 0 && args[1].length > 0) {
591
    from = parseInt(args[0]) - 1;
592
    lines = parseInt(args[1]);
593
  } else if (args.length > 0 && args[0].length > 0) {
594
    from = parseInt(args[0]) - 1;
595
  }
596

    
597
  if (from < 0) from = 0;
598
  if (lines < 0) lines = 10;
599

    
600
  // Request source arround current source location.
601
  request.arguments = {};
602
  request.arguments.fromLine = from;
603
  request.arguments.toLine = from + lines;
604

    
605
  return request.toJSONProtocol();
606
};
607

    
608

    
609
// Create a JSON request for the scripts command.
610
DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) {
611
  // Build a evaluate request from the text command.
612
  var request = this.createRequest('scripts');
613

    
614
  // Process arguments if any.
615
  if (args && args.length > 0) {
616
    args = args.split(/\s*[ ]+\s*/g);
617

    
618
    if (args.length > 1) {
619
      throw new Error('Invalid scripts arguments.');
620
    }
621

    
622
    request.arguments = {};
623
    switch (args[0]) {
624
      case 'natives':
625
        request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native);
626
        break;
627
        
628
      case 'extensions':
629
        request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension);
630
        break;
631
        
632
      case 'all':
633
        request.arguments.types =
634
            ScriptTypeFlag(Debug.ScriptType.Normal) |
635
            ScriptTypeFlag(Debug.ScriptType.Native) |
636
            ScriptTypeFlag(Debug.ScriptType.Extension);
637
        break;
638
        
639
      default:
640
        throw new Error('Invalid argument "' + args[0] + '".');
641
    }
642
  }
643

    
644
  return request.toJSONProtocol();
645
};
646

    
647

    
648
// Create a JSON request for the break command.
649
DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) {
650
  // Build a evaluate request from the text command.
651
  var request = this.createRequest('setbreakpoint');
652

    
653
  // Process arguments if any.
654
  if (args && args.length > 0) {
655
    var target = args;
656
    var condition;
657

    
658
    var pos = args.indexOf(' ');
659
    if (pos > 0) {
660
      target = args.substring(0, pos);
661
      condition = args.substring(pos + 1, args.length);
662
    }
663

    
664
    request.arguments = {};
665
    request.arguments.type = 'function';
666
    request.arguments.target = target;
667
    request.arguments.condition = condition;
668
  } else {
669
    throw new Error('Invalid break arguments.');
670
  }
671

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

    
675

    
676
// Create a JSON request for the clear command.
677
DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) {
678
  // Build a evaluate request from the text command.
679
  var request = this.createRequest('clearbreakpoint');
680

    
681
  // Process arguments if any.
682
  if (args && args.length > 0) {
683
    request.arguments = {};
684
    request.arguments.breakpoint = parseInt(args);
685
  } else {
686
    throw new Error('Invalid break arguments.');
687
  }
688

    
689
  return request.toJSONProtocol();
690
};
691

    
692

    
693
// Create a JSON request for the threads command.
694
DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
695
  // Build a threads request from the text command.
696
  var request = this.createRequest('threads');
697
  return request.toJSONProtocol();
698
};
699

    
700

    
701
// Handle the trace command.
702
DebugRequest.prototype.traceCommand_ = function(args) {
703
  // Process arguments.
704
  if (args && args.length > 0) {
705
    if (args == 'compile') {
706
      trace_compile = !trace_compile;
707
      print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off'));
708
    } else {
709
      throw new Error('Invalid trace arguments.');
710
    }
711
  } else {
712
    throw new Error('Invalid trace arguments.');
713
  }
714
}
715

    
716
// Handle the help command.
717
DebugRequest.prototype.helpCommand_ = function(args) {
718
  // Help os quite simple.
719
  if (args && args.length > 0) {
720
    print('warning: arguments to \'help\' are ignored');
721
  }
722

    
723
  print('break location [condition]');
724
  print('clear <breakpoint #>');
725
  print('backtrace [from frame #] [to frame #]]');
726
  print('frame <frame #>');
727
  print('step [in | next | out| min [step count]]');
728
  print('print <expression>');
729
  print('source [from line [num lines]]');
730
  print('scripts');
731
  print('continue');
732
  print('trace compile');
733
  print('help');
734
}
735

    
736

    
737
function formatHandleReference_(value) {
738
  return '#' + value.handle() + '#';
739
}
740

    
741

    
742
function formatObject_(value, include_properties) {
743
  var result = '';
744
  result += formatHandleReference_(value);
745
  result += ', type: object'
746
  result += ', constructor ';
747
  var ctor = value.constructorFunctionValue();
748
  result += formatHandleReference_(ctor);
749
  result += ', __proto__ ';
750
  var proto = value.protoObjectValue();
751
  result += formatHandleReference_(proto);
752
  result += ', ';
753
  result += value.propertyCount();
754
  result +=  ' properties.';
755
  if (include_properties) {
756
    result +=  '\n';
757
    for (var i = 0; i < value.propertyCount(); i++) {
758
      result += '  ';
759
      result += value.propertyName(i);
760
      result += ': ';
761
      var property_value = value.propertyValue(i);
762
      if (property_value && property_value.type()) {
763
        result += property_value.type();
764
      } else {
765
        result += '<no type>';
766
      }
767
      result += ' ';
768
      result += formatHandleReference_(property_value);
769
      result += '\n';
770
    }
771
  }
772
  return result;
773
}
774

    
775

    
776
// Convert a JSON response to text for display in a text based debugger.
777
function DebugResponseDetails(response) {
778
  details = {text:'', running:false}
779

    
780
  try {
781
    if (!response.success()) {
782
      details.text = response.message();
783
      return details;
784
    }
785

    
786
    // Get the running state.
787
    details.running = response.running();
788

    
789
    var body = response.body();
790
    var result = '';
791
    switch (response.command()) {
792
      case 'setbreakpoint':
793
        result = 'set breakpoint #';
794
        result += body.breakpoint;
795
        details.text = result;
796
        break;
797
        
798
      case 'clearbreakpoint':
799
        result = 'cleared breakpoint #';
800
        result += body.breakpoint;
801
        details.text = result;
802
        break;
803
        
804
      case 'backtrace':
805
        if (body.totalFrames == 0) {
806
          result = '(empty stack)';
807
        } else {
808
          var result = 'Frames #' + body.fromFrame + ' to #' +
809
              (body.toFrame - 1) + ' of ' + body.totalFrames + '\n';
810
          for (i = 0; i < body.frames.length; i++) {
811
            if (i != 0) result += '\n';
812
            result += body.frames[i].text;
813
          }
814
        }
815
        details.text = result;
816
        break;
817
        
818
      case 'frame':
819
        details.text = SourceUnderline(body.sourceLineText,
820
                                       body.column);
821
        Debug.State.currentSourceLine = body.line;
822
        Debug.State.currentFrame = body.index;
823
        break;
824
        
825
      case 'evaluate':
826
      case 'lookup':
827
        if (last_cmd == 'p' || last_cmd == 'print') {
828
          result = body.text;
829
        } else {
830
          var value = response.bodyValue();
831
          if (value.isObject()) {
832
            result += formatObject_(value, true);
833
          } else {
834
            result += 'type: ';
835
            result += value.type();
836
            if (!value.isUndefined() && !value.isNull()) {
837
              result += ', ';
838
              if (value.isString()) {
839
                result += '"';
840
              }
841
              result += value.value();
842
              if (value.isString()) {
843
                result += '"';
844
              }
845
            }
846
            result += '\n';
847
          }
848
        }
849
        details.text = result;
850
        break;
851

    
852
      case 'references':
853
        var count = body.length;
854
        result += 'found ' + count + ' objects';
855
        result += '\n';
856
        for (var i = 0; i < count; i++) {
857
          var value = response.bodyValue(i);
858
          result += formatObject_(value, false);
859
          result += '\n';
860
        }
861
        details.text = result;
862
        break;
863
        
864
      case 'source':
865
        // Get the source from the response.
866
        var source = body.source;
867
        var from_line = body.fromLine + 1;
868
        var lines = source.split('\n');
869
        var maxdigits = 1 + Math.floor(log10(from_line + lines.length));
870
        if (maxdigits < 3) {
871
          maxdigits = 3;
872
        }
873
        var result = '';
874
        for (var num = 0; num < lines.length; num++) {
875
          // Check if there's an extra newline at the end.
876
          if (num == (lines.length - 1) && lines[num].length == 0) {
877
            break;
878
          }
879

    
880
          var current_line = from_line + num;
881
          spacer = maxdigits - (1 + Math.floor(log10(current_line)));
882
          if (current_line == Debug.State.currentSourceLine + 1) {
883
            for (var i = 0; i < maxdigits; i++) {
884
              result += '>';
885
            }
886
            result += '  ';
887
          } else {
888
            for (var i = 0; i < spacer; i++) {
889
              result += ' ';
890
            }
891
            result += current_line + ': ';
892
          }
893
          result += lines[num];
894
          result += '\n';
895
        }
896
        details.text = result;
897
        break;
898
        
899
      case 'scripts':
900
        var result = '';
901
        for (i = 0; i < body.length; i++) {
902
          if (i != 0) result += '\n';
903
          if (body[i].id) {
904
            result += body[i].id;
905
          } else {
906
            result += '[no id]';
907
          }
908
          result += ', ';
909
          if (body[i].name) {
910
            result += body[i].name;
911
          } else {
912
            result += '[unnamed] ';
913
          }
914
          result += ' (lines: ';
915
          result += body[i].lineCount;
916
          result += ', length: ';
917
          result += body[i].sourceLength;
918
          if (body[i].type == Debug.ScriptType.Native) {
919
            result += ', native';
920
          } else if (body[i].type == Debug.ScriptType.Extension) {
921
            result += ', extension';
922
          }
923
          result += '), [';
924
          var sourceStart = body[i].sourceStart;
925
          if (sourceStart.length > 40) {
926
            sourceStart = sourceStart.substring(0, 37) + '...';
927
          }
928
          result += sourceStart;
929
          result += ']';
930
        }
931
        details.text = result;
932
        break;
933

    
934
      case 'threads':
935
        var result = 'Active V8 threads: ' + body.totalThreads + '\n';
936
        body.threads.sort(function(a, b) { return a.id - b.id; });
937
        for (i = 0; i < body.threads.length; i++) {
938
          result += body.threads[i].current ? '*' : ' ';
939
          result += ' ';
940
          result += body.threads[i].id;
941
          result += '\n';
942
        }
943
        details.text = result;
944
        break;
945

    
946
      case 'continue':
947
        details.text = "(running)";
948
        break;
949
        
950
      default:
951
        details.text =
952
            'Response for unknown command \'' + response.command + '\'' +
953
            ' (' + json_response + ')';
954
    }
955
  } catch (e) {
956
    details.text = 'Error: "' + e + '" formatting response';
957
  }
958
  
959
  return details;
960
};
961

    
962

    
963
/**
964
 * Protocol packages send from the debugger.
965
 * @param {string} json - raw protocol packet as JSON string.
966
 * @constructor
967
 */
968
function ProtocolPackage(json) {
969
  this.packet_ = eval('(' + json + ')');
970
  this.refs_ = [];
971
  if (this.packet_.refs) {
972
    for (var i = 0; i < this.packet_.refs.length; i++) {
973
      this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
974
    }
975
  }
976
}
977

    
978

    
979
/**
980
 * Get the packet type.
981
 * @return {String} the packet type
982
 */
983
ProtocolPackage.prototype.type = function() {
984
  return this.packet_.type;
985
}
986

    
987

    
988
/**
989
 * Get the packet event.
990
 * @return {Object} the packet event
991
 */
992
ProtocolPackage.prototype.event = function() {
993
  return this.packet_.event;
994
}
995

    
996

    
997
/**
998
 * Get the packet request sequence.
999
 * @return {number} the packet request sequence
1000
 */
1001
ProtocolPackage.prototype.requestSeq = function() {
1002
  return this.packet_.request_seq;
1003
}
1004

    
1005

    
1006
/**
1007
 * Get the packet request sequence.
1008
 * @return {number} the packet request sequence
1009
 */
1010
ProtocolPackage.prototype.running = function() {
1011
  return this.packet_.running ? true : false;
1012
}
1013

    
1014

    
1015
ProtocolPackage.prototype.success = function() {
1016
  return this.packet_.success ? true : false;
1017
}
1018

    
1019

    
1020
ProtocolPackage.prototype.message = function() {
1021
  return this.packet_.message;
1022
}
1023

    
1024

    
1025
ProtocolPackage.prototype.command = function() {
1026
  return this.packet_.command;
1027
}
1028

    
1029

    
1030
ProtocolPackage.prototype.body = function() {
1031
  return this.packet_.body;
1032
}
1033

    
1034

    
1035
ProtocolPackage.prototype.bodyValue = function(index) {
1036
  if (index) {
1037
    return new ProtocolValue(this.packet_.body[index], this);
1038
  } else {
1039
    return new ProtocolValue(this.packet_.body, this);
1040
  }
1041
}
1042

    
1043

    
1044
ProtocolPackage.prototype.body = function() {
1045
  return this.packet_.body;
1046
}
1047

    
1048

    
1049
ProtocolPackage.prototype.lookup = function(handle) {
1050
  var value = this.refs_[handle];
1051
  if (value) {
1052
    return new ProtocolValue(value, this);
1053
  } else {
1054
    return new ProtocolReference(handle);
1055
  }
1056
}
1057

    
1058

    
1059
function ProtocolValue(value, packet) {
1060
  this.value_ = value;
1061
  this.packet_ = packet;
1062
}
1063

    
1064

    
1065
/**
1066
 * Get the value type.
1067
 * @return {String} the value type
1068
 */
1069
ProtocolValue.prototype.type = function() {
1070
  return this.value_.type;
1071
}
1072

    
1073

    
1074
/**
1075
 * Check is the value is a primitive value.
1076
 * @return {boolean} true if the value is primitive
1077
 */
1078
ProtocolValue.prototype.isPrimitive = function() {
1079
  return this.isUndefined() || this.isNull() || this.isBoolean() ||
1080
         this.isNumber() || this.isString();
1081
}
1082

    
1083

    
1084
/**
1085
 * Get the object handle.
1086
 * @return {number} the value handle
1087
 */
1088
ProtocolValue.prototype.handle = function() {
1089
  return this.value_.handle;
1090
}
1091

    
1092

    
1093
/**
1094
 * Check is the value is undefined.
1095
 * @return {boolean} true if the value is undefined
1096
 */
1097
ProtocolValue.prototype.isUndefined = function() {
1098
  return this.value_.type == 'undefined';
1099
}
1100

    
1101

    
1102
/**
1103
 * Check is the value is null.
1104
 * @return {boolean} true if the value is null
1105
 */
1106
ProtocolValue.prototype.isNull = function() {
1107
  return this.value_.type == 'null';
1108
}
1109

    
1110

    
1111
/**
1112
 * Check is the value is a boolean.
1113
 * @return {boolean} true if the value is a boolean
1114
 */
1115
ProtocolValue.prototype.isBoolean = function() {
1116
  return this.value_.type == 'boolean';
1117
}
1118

    
1119

    
1120
/**
1121
 * Check is the value is a number.
1122
 * @return {boolean} true if the value is a number
1123
 */
1124
ProtocolValue.prototype.isNumber = function() {
1125
  return this.value_.type == 'number';
1126
}
1127

    
1128

    
1129
/**
1130
 * Check is the value is a string.
1131
 * @return {boolean} true if the value is a string
1132
 */
1133
ProtocolValue.prototype.isString = function() {
1134
  return this.value_.type == 'string';
1135
}
1136

    
1137

    
1138
/**
1139
 * Check is the value is an object.
1140
 * @return {boolean} true if the value is an object
1141
 */
1142
ProtocolValue.prototype.isObject = function() {
1143
  return this.value_.type == 'object' || this.value_.type == 'function' ||
1144
         this.value_.type == 'error' || this.value_.type == 'regexp';
1145
}
1146

    
1147

    
1148
/**
1149
 * Get the constructor function
1150
 * @return {ProtocolValue} constructor function
1151
 */
1152
ProtocolValue.prototype.constructorFunctionValue = function() {
1153
  var ctor = this.value_.constructorFunction;
1154
  return this.packet_.lookup(ctor.ref);
1155
}
1156

    
1157

    
1158
/**
1159
 * Get the __proto__ value
1160
 * @return {ProtocolValue} __proto__ value
1161
 */
1162
ProtocolValue.prototype.protoObjectValue = function() {
1163
  var proto = this.value_.protoObject;
1164
  return this.packet_.lookup(proto.ref);
1165
}
1166

    
1167

    
1168
/**
1169
 * Get the number og properties.
1170
 * @return {number} the number of properties
1171
 */
1172
ProtocolValue.prototype.propertyCount = function() {
1173
  return this.value_.properties ? this.value_.properties.length : 0;
1174
}
1175

    
1176

    
1177
/**
1178
 * Get the specified property name.
1179
 * @return {string} property name
1180
 */
1181
ProtocolValue.prototype.propertyName = function(index) {
1182
  var property = this.value_.properties[index];
1183
  return property.name;
1184
}
1185

    
1186

    
1187
/**
1188
 * Return index for the property name.
1189
 * @param name The property name to look for
1190
 * @return {number} index for the property name
1191
 */
1192
ProtocolValue.prototype.propertyIndex = function(name) {
1193
  for (var i = 0; i < this.propertyCount(); i++) {
1194
    if (this.value_.properties[i].name == name) {
1195
      return i;
1196
    }
1197
  }
1198
  return null;
1199
}
1200

    
1201

    
1202
/**
1203
 * Get the specified property value.
1204
 * @return {ProtocolValue} property value
1205
 */
1206
ProtocolValue.prototype.propertyValue = function(index) {
1207
  var property = this.value_.properties[index];
1208
  return this.packet_.lookup(property.ref);
1209
}
1210

    
1211

    
1212
/**
1213
 * Check is the value is a string.
1214
 * @return {boolean} true if the value is a string
1215
 */
1216
ProtocolValue.prototype.value = function() {
1217
  return this.value_.value;
1218
}
1219

    
1220

    
1221
function ProtocolReference(handle) {
1222
  this.handle_ = handle;
1223
}
1224

    
1225

    
1226
ProtocolReference.prototype.handle = function() {
1227
  return this.handle_;
1228
}
1229

    
1230

    
1231
function MakeJSONPair_(name, value) {
1232
  return '"' + name + '":' + value;
1233
}
1234

    
1235

    
1236
function ArrayToJSONObject_(content) {
1237
  return '{' + content.join(',') + '}';
1238
}
1239

    
1240

    
1241
function ArrayToJSONArray_(content) {
1242
  return '[' + content.join(',') + ']';
1243
}
1244

    
1245

    
1246
function BooleanToJSON_(value) {
1247
  return String(value); 
1248
}
1249

    
1250

    
1251
function NumberToJSON_(value) {
1252
  return String(value); 
1253
}
1254

    
1255

    
1256
// Mapping of some control characters to avoid the \uXXXX syntax for most
1257
// commonly used control cahracters.
1258
const ctrlCharMap_ = {
1259
  '\b': '\\b',
1260
  '\t': '\\t',
1261
  '\n': '\\n',
1262
  '\f': '\\f',
1263
  '\r': '\\r',
1264
  '"' : '\\"',
1265
  '\\': '\\\\'
1266
};
1267

    
1268

    
1269
// Regular expression testing for ", \ and control characters (0x00 - 0x1F).
1270
const ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]');
1271

    
1272

    
1273
// Regular expression matching ", \ and control characters (0x00 - 0x1F)
1274
// globally.
1275
const ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g');
1276

    
1277

    
1278
/**
1279
 * Convert a String to its JSON representation (see http://www.json.org/). To
1280
 * avoid depending on the String object this method calls the functions in
1281
 * string.js directly and not through the value.
1282
 * @param {String} value The String value to format as JSON
1283
 * @return {string} JSON formatted String value
1284
 */
1285
function StringToJSON_(value) {
1286
  // Check for" , \ and control characters (0x00 - 0x1F). No need to call
1287
  // RegExpTest as ctrlchar is constructed using RegExp.
1288
  if (ctrlCharTest_.test(value)) {
1289
    // Replace ", \ and control characters (0x00 - 0x1F).
1290
    return '"' +
1291
      value.replace(ctrlCharMatch_, function (char) {
1292
        // Use charmap if possible.
1293
        var mapped = ctrlCharMap_[char];
1294
        if (mapped) return mapped;
1295
        mapped = char.charCodeAt();
1296
        // Convert control character to unicode escape sequence.
1297
        return '\\u00' +
1298
          '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) +
1299
          '0' // TODO %NumberToRadixString(mapped % 16, 16);
1300
      })
1301
    + '"';
1302
  }
1303

    
1304
  // Simple string with no special characters.
1305
  return '"' + value + '"';
1306
}
1307

    
1308

    
1309
/**
1310
 * Convert a Date to ISO 8601 format. To avoid depending on the Date object
1311
 * this method calls the functions in date.js directly and not through the
1312
 * value.
1313
 * @param {Date} value The Date value to format as JSON
1314
 * @return {string} JSON formatted Date value
1315
 */
1316
function DateToISO8601_(value) {
1317
  function f(n) {
1318
    return n < 10 ? '0' + n : n;
1319
  }
1320
  function g(n) {
1321
    return n < 10 ? '00' + n : n < 100 ? '0' + n : n;
1322
  }
1323
  return builtins.GetUTCFullYearFrom(value)         + '-' +
1324
          f(builtins.GetUTCMonthFrom(value) + 1)    + '-' +
1325
          f(builtins.GetUTCDateFrom(value))         + 'T' +
1326
          f(builtins.GetUTCHoursFrom(value))        + ':' +
1327
          f(builtins.GetUTCMinutesFrom(value))      + ':' +
1328
          f(builtins.GetUTCSecondsFrom(value))      + '.' +
1329
          g(builtins.GetUTCMillisecondsFrom(value)) + 'Z';
1330
}
1331

    
1332

    
1333
/**
1334
 * Convert a Date to ISO 8601 format. To avoid depending on the Date object
1335
 * this method calls the functions in date.js directly and not through the
1336
 * value.
1337
 * @param {Date} value The Date value to format as JSON
1338
 * @return {string} JSON formatted Date value
1339
 */
1340
function DateToJSON_(value) {
1341
  return '"' + DateToISO8601_(value) + '"';
1342
}
1343

    
1344

    
1345
/**
1346
 * Convert an Object to its JSON representation (see http://www.json.org/).
1347
 * This implementation simply runs through all string property names and adds
1348
 * each property to the JSON representation for some predefined types. For type
1349
 * "object" the function calls itself recursively unless the object has the
1350
 * function property "toJSONProtocol" in which case that is used. This is not
1351
 * a general implementation but sufficient for the debugger. Note that circular
1352
 * structures will cause infinite recursion.
1353
 * @param {Object} object The object to format as JSON
1354
 * @return {string} JSON formatted object value
1355
 */
1356
function SimpleObjectToJSON_(object) {
1357
  var content = [];
1358
  for (var key in object) {
1359
    // Only consider string keys.
1360
    if (typeof key == 'string') {
1361
      var property_value = object[key];
1362

    
1363
      // Format the value based on its type.
1364
      var property_value_json;
1365
      switch (typeof property_value) {
1366
        case 'object':
1367
          if (typeof property_value.toJSONProtocol == 'function') {
1368
            property_value_json = property_value.toJSONProtocol(true)
1369
          } else if (property_value.constructor.name == 'Array'){
1370
            property_value_json = SimpleArrayToJSON_(property_value);
1371
          } else {
1372
            property_value_json = SimpleObjectToJSON_(property_value);
1373
          }
1374
          break;
1375

    
1376
        case 'boolean':
1377
          property_value_json = BooleanToJSON_(property_value);
1378
          break;
1379

    
1380
        case 'number':
1381
          property_value_json = NumberToJSON_(property_value);
1382
          break;
1383

    
1384
        case 'string':
1385
          property_value_json = StringToJSON_(property_value);
1386
          break;
1387

    
1388
        default:
1389
          property_value_json = null;
1390
      }
1391

    
1392
      // Add the property if relevant.
1393
      if (property_value_json) {
1394
        content.push(StringToJSON_(key) + ':' + property_value_json);
1395
      }
1396
    }
1397
  }
1398

    
1399
  // Make JSON object representation.
1400
  return '{' + content.join(',') + '}';
1401
}
1402

    
1403

    
1404
/**
1405
 * Convert an array to its JSON representation. This is a VERY simple
1406
 * implementation just to support what is needed for the debugger.
1407
 * @param {Array} arrya The array to format as JSON
1408
 * @return {string} JSON formatted array value
1409
 */
1410
function SimpleArrayToJSON_(array) {
1411
  // Make JSON array representation.
1412
  var json = '[';
1413
  for (var i = 0; i < array.length; i++) {
1414
    if (i != 0) {
1415
      json += ',';
1416
    }
1417
    var elem = array[i];
1418
    if (elem.toJSONProtocol) {
1419
      json += elem.toJSONProtocol(true)
1420
    } else if (typeof(elem) === 'object')  {
1421
      json += SimpleObjectToJSON_(elem);
1422
    } else if (typeof(elem) === 'boolean')  {
1423
      json += BooleanToJSON_(elem);
1424
    } else if (typeof(elem) === 'number')  {
1425
      json += NumberToJSON_(elem);
1426
    } else if (typeof(elem) === 'string')  {
1427
      json += StringToJSON_(elem);
1428
    } else {
1429
      json += elem;
1430
    }
1431
  }
1432
  json += ']';
1433
  return json;
1434
}