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 / debug-debugger.js @ f230a1cf

History | View | Annotate | Download (77.3 KB)

1
// Copyright 2012 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
// Default number of frames to include in the response to backtrace request.
29
var kDefaultBacktraceLength = 10;
30

    
31
var Debug = {};
32

    
33
// Regular expression to skip "crud" at the beginning of a source line which is
34
// not really code. Currently the regular expression matches whitespace and
35
// comments.
36
var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
37

    
38
// Debug events which can occour in the V8 JavaScript engine. These originate
39
// from the API include file debug.h.
40
Debug.DebugEvent = { Break: 1,
41
                     Exception: 2,
42
                     NewFunction: 3,
43
                     BeforeCompile: 4,
44
                     AfterCompile: 5,
45
                     ScriptCollected: 6 };
46

    
47
// Types of exceptions that can be broken upon.
48
Debug.ExceptionBreak = { Caught : 0,
49
                         Uncaught: 1 };
50

    
51
// The different types of steps.
52
Debug.StepAction = { StepOut: 0,
53
                     StepNext: 1,
54
                     StepIn: 2,
55
                     StepMin: 3,
56
                     StepInMin: 4 };
57

    
58
// The different types of scripts matching enum ScriptType in objects.h.
59
Debug.ScriptType = { Native: 0,
60
                     Extension: 1,
61
                     Normal: 2 };
62

    
63
// The different types of script compilations matching enum
64
// Script::CompilationType in objects.h.
65
Debug.ScriptCompilationType = { Host: 0,
66
                                Eval: 1,
67
                                JSON: 2 };
68

    
69
// The different script break point types.
70
Debug.ScriptBreakPointType = { ScriptId: 0,
71
                               ScriptName: 1,
72
                               ScriptRegExp: 2 };
73

    
74
// The different types of breakpoint position alignments.
75
// Must match BreakPositionAlignment in debug.h.
76
Debug.BreakPositionAlignment = {
77
  Statement: 0,
78
  BreakPosition: 1
79
};
80

    
81
function ScriptTypeFlag(type) {
82
  return (1 << type);
83
}
84

    
85
// Globals.
86
var next_response_seq = 0;
87
var next_break_point_number = 1;
88
var break_points = [];
89
var script_break_points = [];
90
var debugger_flags = {
91
  breakPointsActive: {
92
    value: true,
93
    getValue: function() { return this.value; },
94
    setValue: function(value) {
95
      this.value = !!value;
96
      %SetDisableBreak(!this.value);
97
    }
98
  },
99
  breakOnCaughtException: {
100
    getValue: function() { return Debug.isBreakOnException(); },
101
    setValue: function(value) {
102
      if (value) {
103
        Debug.setBreakOnException();
104
      } else {
105
        Debug.clearBreakOnException();
106
      }
107
    }
108
  },
109
  breakOnUncaughtException: {
110
    getValue: function() { return Debug.isBreakOnUncaughtException(); },
111
    setValue: function(value) {
112
      if (value) {
113
        Debug.setBreakOnUncaughtException();
114
      } else {
115
        Debug.clearBreakOnUncaughtException();
116
      }
117
    }
118
  },
119
};
120

    
121

    
122
// Create a new break point object and add it to the list of break points.
123
function MakeBreakPoint(source_position, opt_script_break_point) {
124
  var break_point = new BreakPoint(source_position, opt_script_break_point);
125
  break_points.push(break_point);
126
  return break_point;
127
}
128

    
129

    
130
// Object representing a break point.
131
// NOTE: This object does not have a reference to the function having break
132
// point as this would cause function not to be garbage collected when it is
133
// not used any more. We do not want break points to keep functions alive.
134
function BreakPoint(source_position, opt_script_break_point) {
135
  this.source_position_ = source_position;
136
  if (opt_script_break_point) {
137
    this.script_break_point_ = opt_script_break_point;
138
  } else {
139
    this.number_ = next_break_point_number++;
140
  }
141
  this.hit_count_ = 0;
142
  this.active_ = true;
143
  this.condition_ = null;
144
  this.ignoreCount_ = 0;
145
}
146

    
147

    
148
BreakPoint.prototype.number = function() {
149
  return this.number_;
150
};
151

    
152

    
153
BreakPoint.prototype.func = function() {
154
  return this.func_;
155
};
156

    
157

    
158
BreakPoint.prototype.source_position = function() {
159
  return this.source_position_;
160
};
161

    
162

    
163
BreakPoint.prototype.hit_count = function() {
164
  return this.hit_count_;
165
};
166

    
167

    
168
BreakPoint.prototype.active = function() {
169
  if (this.script_break_point()) {
170
    return this.script_break_point().active();
171
  }
172
  return this.active_;
173
};
174

    
175

    
176
BreakPoint.prototype.condition = function() {
177
  if (this.script_break_point() && this.script_break_point().condition()) {
178
    return this.script_break_point().condition();
179
  }
180
  return this.condition_;
181
};
182

    
183

    
184
BreakPoint.prototype.ignoreCount = function() {
185
  return this.ignoreCount_;
186
};
187

    
188

    
189
BreakPoint.prototype.script_break_point = function() {
190
  return this.script_break_point_;
191
};
192

    
193

    
194
BreakPoint.prototype.enable = function() {
195
  this.active_ = true;
196
};
197

    
198

    
199
BreakPoint.prototype.disable = function() {
200
  this.active_ = false;
201
};
202

    
203

    
204
BreakPoint.prototype.setCondition = function(condition) {
205
  this.condition_ = condition;
206
};
207

    
208

    
209
BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
210
  this.ignoreCount_ = ignoreCount;
211
};
212

    
213

    
214
BreakPoint.prototype.isTriggered = function(exec_state) {
215
  // Break point not active - not triggered.
216
  if (!this.active()) return false;
217

    
218
  // Check for conditional break point.
219
  if (this.condition()) {
220
    // If break point has condition try to evaluate it in the top frame.
221
    try {
222
      var mirror = exec_state.frame(0).evaluate(this.condition());
223
      // If no sensible mirror or non true value break point not triggered.
224
      if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) {
225
        return false;
226
      }
227
    } catch (e) {
228
      // Exception evaluating condition counts as not triggered.
229
      return false;
230
    }
231
  }
232

    
233
  // Update the hit count.
234
  this.hit_count_++;
235
  if (this.script_break_point_) {
236
    this.script_break_point_.hit_count_++;
237
  }
238

    
239
  // If the break point has an ignore count it is not triggered.
240
  if (this.ignoreCount_ > 0) {
241
    this.ignoreCount_--;
242
    return false;
243
  }
244

    
245
  // Break point triggered.
246
  return true;
247
};
248

    
249

    
250
// Function called from the runtime when a break point is hit. Returns true if
251
// the break point is triggered and supposed to break execution.
252
function IsBreakPointTriggered(break_id, break_point) {
253
  return break_point.isTriggered(MakeExecutionState(break_id));
254
}
255

    
256

    
257
// Object representing a script break point. The script is referenced by its
258
// script name or script id and the break point is represented as line and
259
// column.
260
function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
261
                          opt_groupId, opt_position_alignment) {
262
  this.type_ = type;
263
  if (type == Debug.ScriptBreakPointType.ScriptId) {
264
    this.script_id_ = script_id_or_name;
265
  } else if (type == Debug.ScriptBreakPointType.ScriptName) {
266
    this.script_name_ = script_id_or_name;
267
  } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
268
    this.script_regexp_object_ = new RegExp(script_id_or_name);
269
  } else {
270
    throw new Error("Unexpected breakpoint type " + type);
271
  }
272
  this.line_ = opt_line || 0;
273
  this.column_ = opt_column;
274
  this.groupId_ = opt_groupId;
275
  this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
276
      ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
277
  this.hit_count_ = 0;
278
  this.active_ = true;
279
  this.condition_ = null;
280
  this.ignoreCount_ = 0;
281
  this.break_points_ = [];
282
}
283

    
284

    
285
//Creates a clone of script breakpoint that is linked to another script.
286
ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) {
287
  var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
288
      other_script.id, this.line_, this.column_, this.groupId_,
289
      this.position_alignment_);
290
  copy.number_ = next_break_point_number++;
291
  script_break_points.push(copy);
292

    
293
  copy.hit_count_ = this.hit_count_;
294
  copy.active_ = this.active_;
295
  copy.condition_ = this.condition_;
296
  copy.ignoreCount_ = this.ignoreCount_;
297
  return copy;
298
};
299

    
300

    
301
ScriptBreakPoint.prototype.number = function() {
302
  return this.number_;
303
};
304

    
305

    
306
ScriptBreakPoint.prototype.groupId = function() {
307
  return this.groupId_;
308
};
309

    
310

    
311
ScriptBreakPoint.prototype.type = function() {
312
  return this.type_;
313
};
314

    
315

    
316
ScriptBreakPoint.prototype.script_id = function() {
317
  return this.script_id_;
318
};
319

    
320

    
321
ScriptBreakPoint.prototype.script_name = function() {
322
  return this.script_name_;
323
};
324

    
325

    
326
ScriptBreakPoint.prototype.script_regexp_object = function() {
327
  return this.script_regexp_object_;
328
};
329

    
330

    
331
ScriptBreakPoint.prototype.line = function() {
332
  return this.line_;
333
};
334

    
335

    
336
ScriptBreakPoint.prototype.column = function() {
337
  return this.column_;
338
};
339

    
340

    
341
ScriptBreakPoint.prototype.actual_locations = function() {
342
  var locations = [];
343
  for (var i = 0; i < this.break_points_.length; i++) {
344
    locations.push(this.break_points_[i].actual_location);
345
  }
346
  return locations;
347
};
348

    
349

    
350
ScriptBreakPoint.prototype.update_positions = function(line, column) {
351
  this.line_ = line;
352
  this.column_ = column;
353
};
354

    
355

    
356
ScriptBreakPoint.prototype.hit_count = function() {
357
  return this.hit_count_;
358
};
359

    
360

    
361
ScriptBreakPoint.prototype.active = function() {
362
  return this.active_;
363
};
364

    
365

    
366
ScriptBreakPoint.prototype.condition = function() {
367
  return this.condition_;
368
};
369

    
370

    
371
ScriptBreakPoint.prototype.ignoreCount = function() {
372
  return this.ignoreCount_;
373
};
374

    
375

    
376
ScriptBreakPoint.prototype.enable = function() {
377
  this.active_ = true;
378
};
379

    
380

    
381
ScriptBreakPoint.prototype.disable = function() {
382
  this.active_ = false;
383
};
384

    
385

    
386
ScriptBreakPoint.prototype.setCondition = function(condition) {
387
  this.condition_ = condition;
388
};
389

    
390

    
391
ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
392
  this.ignoreCount_ = ignoreCount;
393

    
394
  // Set ignore count on all break points created from this script break point.
395
  for (var i = 0; i < this.break_points_.length; i++) {
396
    this.break_points_[i].setIgnoreCount(ignoreCount);
397
  }
398
};
399

    
400

    
401
// Check whether a script matches this script break point. Currently this is
402
// only based on script name.
403
ScriptBreakPoint.prototype.matchesScript = function(script) {
404
  if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
405
    return this.script_id_ == script.id;
406
  } else {
407
    // We might want to account columns here as well.
408
    if (!(script.line_offset <= this.line_  &&
409
          this.line_ < script.line_offset + script.lineCount())) {
410
      return false;
411
    }
412
    if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
413
      return this.script_name_ == script.nameOrSourceURL();
414
    } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
415
      return this.script_regexp_object_.test(script.nameOrSourceURL());
416
    } else {
417
      throw new Error("Unexpected breakpoint type " + this.type_);
418
    }
419
  }
420
};
421

    
422

    
423
// Set the script break point in a script.
424
ScriptBreakPoint.prototype.set = function (script) {
425
  var column = this.column();
426
  var line = this.line();
427
  // If the column is undefined the break is on the line. To help locate the
428
  // first piece of breakable code on the line try to find the column on the
429
  // line which contains some source.
430
  if (IS_UNDEFINED(column)) {
431
    var source_line = script.sourceLine(this.line());
432

    
433
    // Allocate array for caching the columns where the actual source starts.
434
    if (!script.sourceColumnStart_) {
435
      script.sourceColumnStart_ = new Array(script.lineCount());
436
    }
437

    
438
    // Fill cache if needed and get column where the actual source starts.
439
    if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
440
      script.sourceColumnStart_[line] =
441
          source_line.match(sourceLineBeginningSkip)[0].length;
442
    }
443
    column = script.sourceColumnStart_[line];
444
  }
445

    
446
  // Convert the line and column into an absolute position within the script.
447
  var position = Debug.findScriptSourcePosition(script, this.line(), column);
448

    
449
  // If the position is not found in the script (the script might be shorter
450
  // than it used to be) just ignore it.
451
  if (IS_NULL(position)) return;
452

    
453
  // Create a break point object and set the break point.
454
  break_point = MakeBreakPoint(position, this);
455
  break_point.setIgnoreCount(this.ignoreCount());
456
  var actual_position = %SetScriptBreakPoint(script, position,
457
                                             this.position_alignment_,
458
                                             break_point);
459
  if (IS_UNDEFINED(actual_position)) {
460
    actual_position = position;
461
  }
462
  var actual_location = script.locationFromPosition(actual_position, true);
463
  break_point.actual_location = { line: actual_location.line,
464
                                  column: actual_location.column,
465
                                  script_id: script.id };
466
  this.break_points_.push(break_point);
467
  return break_point;
468
};
469

    
470

    
471
// Clear all the break points created from this script break point
472
ScriptBreakPoint.prototype.clear = function () {
473
  var remaining_break_points = [];
474
  for (var i = 0; i < break_points.length; i++) {
475
    if (break_points[i].script_break_point() &&
476
        break_points[i].script_break_point() === this) {
477
      %ClearBreakPoint(break_points[i]);
478
    } else {
479
      remaining_break_points.push(break_points[i]);
480
    }
481
  }
482
  break_points = remaining_break_points;
483
  this.break_points_ = [];
484
};
485

    
486

    
487
// Function called from runtime when a new script is compiled to set any script
488
// break points set in this script.
489
function UpdateScriptBreakPoints(script) {
490
  for (var i = 0; i < script_break_points.length; i++) {
491
    var break_point = script_break_points[i];
492
    if ((break_point.type() == Debug.ScriptBreakPointType.ScriptName ||
493
         break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) &&
494
        break_point.matchesScript(script)) {
495
      break_point.set(script);
496
    }
497
  }
498
}
499

    
500

    
501
function GetScriptBreakPoints(script) {
502
  var result = [];
503
  for (var i = 0; i < script_break_points.length; i++) {
504
    if (script_break_points[i].matchesScript(script)) {
505
      result.push(script_break_points[i]);
506
    }
507
  }
508
  return result;
509
}
510

    
511

    
512
Debug.setListener = function(listener, opt_data) {
513
  if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
514
    throw new Error('Parameters have wrong types.');
515
  }
516
  %SetDebugEventListener(listener, opt_data);
517
};
518

    
519

    
520
Debug.breakExecution = function(f) {
521
  %Break();
522
};
523

    
524
Debug.breakLocations = function(f, opt_position_aligment) {
525
  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
526
  var position_aligment = IS_UNDEFINED(opt_position_aligment)
527
      ? Debug.BreakPositionAlignment.Statement : opt_position_aligment;
528
  return %GetBreakLocations(f, position_aligment);
529
};
530

    
531
// Returns a Script object. If the parameter is a function the return value
532
// is the script in which the function is defined. If the parameter is a string
533
// the return value is the script for which the script name has that string
534
// value.  If it is a regexp and there is a unique script whose name matches
535
// we return that, otherwise undefined.
536
Debug.findScript = function(func_or_script_name) {
537
  if (IS_FUNCTION(func_or_script_name)) {
538
    return %FunctionGetScript(func_or_script_name);
539
  } else if (IS_REGEXP(func_or_script_name)) {
540
    var scripts = Debug.scripts();
541
    var last_result = null;
542
    var result_count = 0;
543
    for (var i in scripts) {
544
      var script = scripts[i];
545
      if (func_or_script_name.test(script.name)) {
546
        last_result = script;
547
        result_count++;
548
      }
549
    }
550
    // Return the unique script matching the regexp.  If there are more
551
    // than one we don't return a value since there is no good way to
552
    // decide which one to return.  Returning a "random" one, say the
553
    // first, would introduce nondeterminism (or something close to it)
554
    // because the order is the heap iteration order.
555
    if (result_count == 1) {
556
      return last_result;
557
    } else {
558
      return undefined;
559
    }
560
  } else {
561
    return %GetScript(func_or_script_name);
562
  }
563
};
564

    
565
// Returns the script source. If the parameter is a function the return value
566
// is the script source for the script in which the function is defined. If the
567
// parameter is a string the return value is the script for which the script
568
// name has that string value.
569
Debug.scriptSource = function(func_or_script_name) {
570
  return this.findScript(func_or_script_name).source;
571
};
572

    
573
Debug.source = function(f) {
574
  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
575
  return %FunctionGetSourceCode(f);
576
};
577

    
578
Debug.disassemble = function(f) {
579
  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
580
  return %DebugDisassembleFunction(f);
581
};
582

    
583
Debug.disassembleConstructor = function(f) {
584
  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
585
  return %DebugDisassembleConstructor(f);
586
};
587

    
588
Debug.ExecuteInDebugContext = function(f, without_debugger) {
589
  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
590
  return %ExecuteInDebugContext(f, !!without_debugger);
591
};
592

    
593
Debug.sourcePosition = function(f) {
594
  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
595
  return %FunctionGetScriptSourcePosition(f);
596
};
597

    
598

    
599
Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
600
  var script = %FunctionGetScript(func);
601
  var script_offset = %FunctionGetScriptSourcePosition(func);
602
  return script.locationFromLine(opt_line, opt_column, script_offset);
603
};
604

    
605

    
606
// Returns the character position in a script based on a line number and an
607
// optional position within that line.
608
Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
609
  var location = script.locationFromLine(opt_line, opt_column);
610
  return location ? location.position : null;
611
};
612

    
613

    
614
Debug.findBreakPoint = function(break_point_number, remove) {
615
  var break_point;
616
  for (var i = 0; i < break_points.length; i++) {
617
    if (break_points[i].number() == break_point_number) {
618
      break_point = break_points[i];
619
      // Remove the break point from the list if requested.
620
      if (remove) {
621
        break_points.splice(i, 1);
622
      }
623
      break;
624
    }
625
  }
626
  if (break_point) {
627
    return break_point;
628
  } else {
629
    return this.findScriptBreakPoint(break_point_number, remove);
630
  }
631
};
632

    
633
Debug.findBreakPointActualLocations = function(break_point_number) {
634
  for (var i = 0; i < script_break_points.length; i++) {
635
    if (script_break_points[i].number() == break_point_number) {
636
      return script_break_points[i].actual_locations();
637
    }
638
  }
639
  for (var i = 0; i < break_points.length; i++) {
640
    if (break_points[i].number() == break_point_number) {
641
      return [break_points[i].actual_location];
642
    }
643
  }
644
  return [];
645
};
646

    
647
Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
648
  if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
649
  // Break points in API functions are not supported.
650
  if (%FunctionIsAPIFunction(func)) {
651
    throw new Error('Cannot set break point in native code.');
652
  }
653
  // Find source position relative to start of the function
654
  var break_position =
655
      this.findFunctionSourceLocation(func, opt_line, opt_column).position;
656
  var source_position = break_position - this.sourcePosition(func);
657
  // Find the script for the function.
658
  var script = %FunctionGetScript(func);
659
  // Break in builtin JavaScript code is not supported.
660
  if (script.type == Debug.ScriptType.Native) {
661
    throw new Error('Cannot set break point in native code.');
662
  }
663
  // If the script for the function has a name convert this to a script break
664
  // point.
665
  if (script && script.id) {
666
    // Adjust the source position to be script relative.
667
    source_position += %FunctionGetScriptSourcePosition(func);
668
    // Find line and column for the position in the script and set a script
669
    // break point from that.
670
    var location = script.locationFromPosition(source_position, false);
671
    return this.setScriptBreakPointById(script.id,
672
                                        location.line, location.column,
673
                                        opt_condition);
674
  } else {
675
    // Set a break point directly on the function.
676
    var break_point = MakeBreakPoint(source_position);
677
    var actual_position =
678
        %SetFunctionBreakPoint(func, source_position, break_point);
679
    actual_position += this.sourcePosition(func);
680
    var actual_location = script.locationFromPosition(actual_position, true);
681
    break_point.actual_location = { line: actual_location.line,
682
                                    column: actual_location.column,
683
                                    script_id: script.id };
684
    break_point.setCondition(opt_condition);
685
    return break_point.number();
686
  }
687
};
688

    
689

    
690
Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
691
                                                    condition, enabled,
692
                                                    opt_position_alignment)
693
{
694
  break_point = MakeBreakPoint(position);
695
  break_point.setCondition(condition);
696
  if (!enabled) {
697
    break_point.disable();
698
  }
699
  var scripts = this.scripts();
700
  var position_alignment = IS_UNDEFINED(opt_position_alignment)
701
      ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
702
  for (var i = 0; i < scripts.length; i++) {
703
    if (script_id == scripts[i].id) {
704
      break_point.actual_position = %SetScriptBreakPoint(scripts[i], position,
705
          position_alignment, break_point);
706
      break;
707
    }
708
  }
709
  return break_point;
710
};
711

    
712

    
713
Debug.enableBreakPoint = function(break_point_number) {
714
  var break_point = this.findBreakPoint(break_point_number, false);
715
  // Only enable if the breakpoint hasn't been deleted:
716
  if (break_point) {
717
    break_point.enable();
718
  }
719
};
720

    
721

    
722
Debug.disableBreakPoint = function(break_point_number) {
723
  var break_point = this.findBreakPoint(break_point_number, false);
724
  // Only enable if the breakpoint hasn't been deleted:
725
  if (break_point) {
726
    break_point.disable();
727
  }
728
};
729

    
730

    
731
Debug.changeBreakPointCondition = function(break_point_number, condition) {
732
  var break_point = this.findBreakPoint(break_point_number, false);
733
  break_point.setCondition(condition);
734
};
735

    
736

    
737
Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
738
  if (ignoreCount < 0) {
739
    throw new Error('Invalid argument');
740
  }
741
  var break_point = this.findBreakPoint(break_point_number, false);
742
  break_point.setIgnoreCount(ignoreCount);
743
};
744

    
745

    
746
Debug.clearBreakPoint = function(break_point_number) {
747
  var break_point = this.findBreakPoint(break_point_number, true);
748
  if (break_point) {
749
    return %ClearBreakPoint(break_point);
750
  } else {
751
    break_point = this.findScriptBreakPoint(break_point_number, true);
752
    if (!break_point) {
753
      throw new Error('Invalid breakpoint');
754
    }
755
  }
756
};
757

    
758

    
759
Debug.clearAllBreakPoints = function() {
760
  for (var i = 0; i < break_points.length; i++) {
761
    break_point = break_points[i];
762
    %ClearBreakPoint(break_point);
763
  }
764
  break_points = [];
765
};
766

    
767

    
768
Debug.disableAllBreakPoints = function() {
769
  // Disable all user defined breakpoints:
770
  for (var i = 1; i < next_break_point_number; i++) {
771
    Debug.disableBreakPoint(i);
772
  }
773
  // Disable all exception breakpoints:
774
  %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
775
  %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
776
};
777

    
778

    
779
Debug.findScriptBreakPoint = function(break_point_number, remove) {
780
  var script_break_point;
781
  for (var i = 0; i < script_break_points.length; i++) {
782
    if (script_break_points[i].number() == break_point_number) {
783
      script_break_point = script_break_points[i];
784
      // Remove the break point from the list if requested.
785
      if (remove) {
786
        script_break_point.clear();
787
        script_break_points.splice(i,1);
788
      }
789
      break;
790
    }
791
  }
792
  return script_break_point;
793
};
794

    
795

    
796
// Sets a breakpoint in a script identified through id or name at the
797
// specified source line and column within that line.
798
Debug.setScriptBreakPoint = function(type, script_id_or_name,
799
                                     opt_line, opt_column, opt_condition,
800
                                     opt_groupId, opt_position_alignment) {
801
  // Create script break point object.
802
  var script_break_point =
803
      new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
804
                           opt_groupId, opt_position_alignment);
805

    
806
  // Assign number to the new script break point and add it.
807
  script_break_point.number_ = next_break_point_number++;
808
  script_break_point.setCondition(opt_condition);
809
  script_break_points.push(script_break_point);
810

    
811
  // Run through all scripts to see if this script break point matches any
812
  // loaded scripts.
813
  var scripts = this.scripts();
814
  for (var i = 0; i < scripts.length; i++) {
815
    if (script_break_point.matchesScript(scripts[i])) {
816
      script_break_point.set(scripts[i]);
817
    }
818
  }
819

    
820
  return script_break_point.number();
821
};
822

    
823

    
824
Debug.setScriptBreakPointById = function(script_id,
825
                                         opt_line, opt_column,
826
                                         opt_condition, opt_groupId,
827
                                         opt_position_alignment) {
828
  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
829
                                  script_id, opt_line, opt_column,
830
                                  opt_condition, opt_groupId,
831
                                  opt_position_alignment);
832
};
833

    
834

    
835
Debug.setScriptBreakPointByName = function(script_name,
836
                                           opt_line, opt_column,
837
                                           opt_condition, opt_groupId) {
838
  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
839
                                  script_name, opt_line, opt_column,
840
                                  opt_condition, opt_groupId);
841
};
842

    
843

    
844
Debug.setScriptBreakPointByRegExp = function(script_regexp,
845
                                             opt_line, opt_column,
846
                                             opt_condition, opt_groupId) {
847
  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
848
                                  script_regexp, opt_line, opt_column,
849
                                  opt_condition, opt_groupId);
850
};
851

    
852

    
853
Debug.enableScriptBreakPoint = function(break_point_number) {
854
  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
855
  script_break_point.enable();
856
};
857

    
858

    
859
Debug.disableScriptBreakPoint = function(break_point_number) {
860
  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
861
  script_break_point.disable();
862
};
863

    
864

    
865
Debug.changeScriptBreakPointCondition = function(
866
    break_point_number, condition) {
867
  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
868
  script_break_point.setCondition(condition);
869
};
870

    
871

    
872
Debug.changeScriptBreakPointIgnoreCount = function(
873
    break_point_number, ignoreCount) {
874
  if (ignoreCount < 0) {
875
    throw new Error('Invalid argument');
876
  }
877
  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
878
  script_break_point.setIgnoreCount(ignoreCount);
879
};
880

    
881

    
882
Debug.scriptBreakPoints = function() {
883
  return script_break_points;
884
};
885

    
886

    
887
Debug.clearStepping = function() {
888
  %ClearStepping();
889
};
890

    
891
Debug.setBreakOnException = function() {
892
  return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
893
};
894

    
895
Debug.clearBreakOnException = function() {
896
  return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
897
};
898

    
899
Debug.isBreakOnException = function() {
900
  return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
901
};
902

    
903
Debug.setBreakOnUncaughtException = function() {
904
  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
905
};
906

    
907
Debug.clearBreakOnUncaughtException = function() {
908
  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
909
};
910

    
911
Debug.isBreakOnUncaughtException = function() {
912
  return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
913
};
914

    
915
Debug.showBreakPoints = function(f, full, opt_position_alignment) {
916
  if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
917
  var source = full ? this.scriptSource(f) : this.source(f);
918
  var offset = full ? this.sourcePosition(f) : 0;
919
  var locations = this.breakLocations(f, opt_position_alignment);
920
  if (!locations) return source;
921
  locations.sort(function(x, y) { return x - y; });
922
  var result = "";
923
  var prev_pos = 0;
924
  var pos;
925
  for (var i = 0; i < locations.length; i++) {
926
    pos = locations[i] - offset;
927
    result += source.slice(prev_pos, pos);
928
    result += "[B" + i + "]";
929
    prev_pos = pos;
930
  }
931
  pos = source.length;
932
  result += source.substring(prev_pos, pos);
933
  return result;
934
};
935

    
936

    
937
// Get all the scripts currently loaded. Locating all the scripts is based on
938
// scanning the heap.
939
Debug.scripts = function() {
940
  // Collect all scripts in the heap.
941
  return %DebugGetLoadedScripts();
942
};
943

    
944

    
945
Debug.debuggerFlags = function() {
946
  return debugger_flags;
947
};
948

    
949
Debug.MakeMirror = MakeMirror;
950

    
951
function MakeExecutionState(break_id) {
952
  return new ExecutionState(break_id);
953
}
954

    
955
function ExecutionState(break_id) {
956
  this.break_id = break_id;
957
  this.selected_frame = 0;
958
}
959

    
960
ExecutionState.prototype.prepareStep = function(opt_action, opt_count,
961
    opt_callframe) {
962
  var action = Debug.StepAction.StepIn;
963
  if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
964
  var count = opt_count ? %ToNumber(opt_count) : 1;
965
  var callFrameId = 0;
966
  if (!IS_UNDEFINED(opt_callframe)) {
967
    callFrameId = opt_callframe.details_.frameId();
968
  }
969

    
970
  return %PrepareStep(this.break_id, action, count, callFrameId);
971
};
972

    
973
ExecutionState.prototype.evaluateGlobal = function(source, disable_break,
974
    opt_additional_context) {
975
  return MakeMirror(%DebugEvaluateGlobal(this.break_id, source,
976
                                         Boolean(disable_break),
977
                                         opt_additional_context));
978
};
979

    
980
ExecutionState.prototype.frameCount = function() {
981
  return %GetFrameCount(this.break_id);
982
};
983

    
984
ExecutionState.prototype.threadCount = function() {
985
  return %GetThreadCount(this.break_id);
986
};
987

    
988
ExecutionState.prototype.frame = function(opt_index) {
989
  // If no index supplied return the selected frame.
990
  if (opt_index == null) opt_index = this.selected_frame;
991
  if (opt_index < 0 || opt_index >= this.frameCount()) {
992
    throw new Error('Illegal frame index.');
993
  }
994
  return new FrameMirror(this.break_id, opt_index);
995
};
996

    
997
ExecutionState.prototype.setSelectedFrame = function(index) {
998
  var i = %ToNumber(index);
999
  if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
1000
  this.selected_frame = i;
1001
};
1002

    
1003
ExecutionState.prototype.selectedFrame = function() {
1004
  return this.selected_frame;
1005
};
1006

    
1007
ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
1008
  return new DebugCommandProcessor(this, opt_is_running);
1009
};
1010

    
1011

    
1012
function MakeBreakEvent(exec_state, break_points_hit) {
1013
  return new BreakEvent(exec_state, break_points_hit);
1014
}
1015

    
1016

    
1017
function BreakEvent(exec_state, break_points_hit) {
1018
  this.exec_state_ = exec_state;
1019
  this.break_points_hit_ = break_points_hit;
1020
}
1021

    
1022

    
1023
BreakEvent.prototype.executionState = function() {
1024
  return this.exec_state_;
1025
};
1026

    
1027

    
1028
BreakEvent.prototype.eventType = function() {
1029
  return Debug.DebugEvent.Break;
1030
};
1031

    
1032

    
1033
BreakEvent.prototype.func = function() {
1034
  return this.exec_state_.frame(0).func();
1035
};
1036

    
1037

    
1038
BreakEvent.prototype.sourceLine = function() {
1039
  return this.exec_state_.frame(0).sourceLine();
1040
};
1041

    
1042

    
1043
BreakEvent.prototype.sourceColumn = function() {
1044
  return this.exec_state_.frame(0).sourceColumn();
1045
};
1046

    
1047

    
1048
BreakEvent.prototype.sourceLineText = function() {
1049
  return this.exec_state_.frame(0).sourceLineText();
1050
};
1051

    
1052

    
1053
BreakEvent.prototype.breakPointsHit = function() {
1054
  return this.break_points_hit_;
1055
};
1056

    
1057

    
1058
BreakEvent.prototype.toJSONProtocol = function() {
1059
  var o = { seq: next_response_seq++,
1060
            type: "event",
1061
            event: "break",
1062
            body: { invocationText: this.exec_state_.frame(0).invocationText(),
1063
                  }
1064
          };
1065

    
1066
  // Add script related information to the event if available.
1067
  var script = this.func().script();
1068
  if (script) {
1069
    o.body.sourceLine = this.sourceLine(),
1070
    o.body.sourceColumn = this.sourceColumn(),
1071
    o.body.sourceLineText = this.sourceLineText(),
1072
    o.body.script = MakeScriptObject_(script, false);
1073
  }
1074

    
1075
  // Add an Array of break points hit if any.
1076
  if (this.breakPointsHit()) {
1077
    o.body.breakpoints = [];
1078
    for (var i = 0; i < this.breakPointsHit().length; i++) {
1079
      // Find the break point number. For break points originating from a
1080
      // script break point supply the script break point number.
1081
      var breakpoint = this.breakPointsHit()[i];
1082
      var script_break_point = breakpoint.script_break_point();
1083
      var number;
1084
      if (script_break_point) {
1085
        number = script_break_point.number();
1086
      } else {
1087
        number = breakpoint.number();
1088
      }
1089
      o.body.breakpoints.push(number);
1090
    }
1091
  }
1092
  return JSON.stringify(ObjectToProtocolObject_(o));
1093
};
1094

    
1095

    
1096
function MakeExceptionEvent(exec_state, exception, uncaught) {
1097
  return new ExceptionEvent(exec_state, exception, uncaught);
1098
}
1099

    
1100

    
1101
function ExceptionEvent(exec_state, exception, uncaught) {
1102
  this.exec_state_ = exec_state;
1103
  this.exception_ = exception;
1104
  this.uncaught_ = uncaught;
1105
}
1106

    
1107

    
1108
ExceptionEvent.prototype.executionState = function() {
1109
  return this.exec_state_;
1110
};
1111

    
1112

    
1113
ExceptionEvent.prototype.eventType = function() {
1114
  return Debug.DebugEvent.Exception;
1115
};
1116

    
1117

    
1118
ExceptionEvent.prototype.exception = function() {
1119
  return this.exception_;
1120
};
1121

    
1122

    
1123
ExceptionEvent.prototype.uncaught = function() {
1124
  return this.uncaught_;
1125
};
1126

    
1127

    
1128
ExceptionEvent.prototype.func = function() {
1129
  return this.exec_state_.frame(0).func();
1130
};
1131

    
1132

    
1133
ExceptionEvent.prototype.sourceLine = function() {
1134
  return this.exec_state_.frame(0).sourceLine();
1135
};
1136

    
1137

    
1138
ExceptionEvent.prototype.sourceColumn = function() {
1139
  return this.exec_state_.frame(0).sourceColumn();
1140
};
1141

    
1142

    
1143
ExceptionEvent.prototype.sourceLineText = function() {
1144
  return this.exec_state_.frame(0).sourceLineText();
1145
};
1146

    
1147

    
1148
ExceptionEvent.prototype.toJSONProtocol = function() {
1149
  var o = new ProtocolMessage();
1150
  o.event = "exception";
1151
  o.body = { uncaught: this.uncaught_,
1152
             exception: MakeMirror(this.exception_)
1153
           };
1154

    
1155
  // Exceptions might happen whithout any JavaScript frames.
1156
  if (this.exec_state_.frameCount() > 0) {
1157
    o.body.sourceLine = this.sourceLine();
1158
    o.body.sourceColumn = this.sourceColumn();
1159
    o.body.sourceLineText = this.sourceLineText();
1160

    
1161
    // Add script information to the event if available.
1162
    var script = this.func().script();
1163
    if (script) {
1164
      o.body.script = MakeScriptObject_(script, false);
1165
    }
1166
  } else {
1167
    o.body.sourceLine = -1;
1168
  }
1169

    
1170
  return o.toJSONProtocol();
1171
};
1172

    
1173

    
1174
function MakeCompileEvent(exec_state, script, before) {
1175
  return new CompileEvent(exec_state, script, before);
1176
}
1177

    
1178

    
1179
function CompileEvent(exec_state, script, before) {
1180
  this.exec_state_ = exec_state;
1181
  this.script_ = MakeMirror(script);
1182
  this.before_ = before;
1183
}
1184

    
1185

    
1186
CompileEvent.prototype.executionState = function() {
1187
  return this.exec_state_;
1188
};
1189

    
1190

    
1191
CompileEvent.prototype.eventType = function() {
1192
  if (this.before_) {
1193
    return Debug.DebugEvent.BeforeCompile;
1194
  } else {
1195
    return Debug.DebugEvent.AfterCompile;
1196
  }
1197
};
1198

    
1199

    
1200
CompileEvent.prototype.script = function() {
1201
  return this.script_;
1202
};
1203

    
1204

    
1205
CompileEvent.prototype.toJSONProtocol = function() {
1206
  var o = new ProtocolMessage();
1207
  o.running = true;
1208
  if (this.before_) {
1209
    o.event = "beforeCompile";
1210
  } else {
1211
    o.event = "afterCompile";
1212
  }
1213
  o.body = {};
1214
  o.body.script = this.script_;
1215

    
1216
  return o.toJSONProtocol();
1217
};
1218

    
1219

    
1220
function MakeNewFunctionEvent(func) {
1221
  return new NewFunctionEvent(func);
1222
}
1223

    
1224

    
1225
function NewFunctionEvent(func) {
1226
  this.func = func;
1227
}
1228

    
1229

    
1230
NewFunctionEvent.prototype.eventType = function() {
1231
  return Debug.DebugEvent.NewFunction;
1232
};
1233

    
1234

    
1235
NewFunctionEvent.prototype.name = function() {
1236
  return this.func.name;
1237
};
1238

    
1239

    
1240
NewFunctionEvent.prototype.setBreakPoint = function(p) {
1241
  Debug.setBreakPoint(this.func, p || 0);
1242
};
1243

    
1244

    
1245
function MakeScriptCollectedEvent(exec_state, id) {
1246
  return new ScriptCollectedEvent(exec_state, id);
1247
}
1248

    
1249

    
1250
function ScriptCollectedEvent(exec_state, id) {
1251
  this.exec_state_ = exec_state;
1252
  this.id_ = id;
1253
}
1254

    
1255

    
1256
ScriptCollectedEvent.prototype.id = function() {
1257
  return this.id_;
1258
};
1259

    
1260

    
1261
ScriptCollectedEvent.prototype.executionState = function() {
1262
  return this.exec_state_;
1263
};
1264

    
1265

    
1266
ScriptCollectedEvent.prototype.toJSONProtocol = function() {
1267
  var o = new ProtocolMessage();
1268
  o.running = true;
1269
  o.event = "scriptCollected";
1270
  o.body = {};
1271
  o.body.script = { id: this.id() };
1272
  return o.toJSONProtocol();
1273
};
1274

    
1275

    
1276
function MakeScriptObject_(script, include_source) {
1277
  var o = { id: script.id(),
1278
            name: script.name(),
1279
            lineOffset: script.lineOffset(),
1280
            columnOffset: script.columnOffset(),
1281
            lineCount: script.lineCount(),
1282
          };
1283
  if (!IS_UNDEFINED(script.data())) {
1284
    o.data = script.data();
1285
  }
1286
  if (include_source) {
1287
    o.source = script.source();
1288
  }
1289
  return o;
1290
}
1291

    
1292

    
1293
function DebugCommandProcessor(exec_state, opt_is_running) {
1294
  this.exec_state_ = exec_state;
1295
  this.running_ = opt_is_running || false;
1296
}
1297

    
1298

    
1299
DebugCommandProcessor.prototype.processDebugRequest = function (request) {
1300
  return this.processDebugJSONRequest(request);
1301
};
1302

    
1303

    
1304
function ProtocolMessage(request) {
1305
  // Update sequence number.
1306
  this.seq = next_response_seq++;
1307

    
1308
  if (request) {
1309
    // If message is based on a request this is a response. Fill the initial
1310
    // response from the request.
1311
    this.type = 'response';
1312
    this.request_seq = request.seq;
1313
    this.command = request.command;
1314
  } else {
1315
    // If message is not based on a request it is a dabugger generated event.
1316
    this.type = 'event';
1317
  }
1318
  this.success = true;
1319
  // Handler may set this field to control debugger state.
1320
  this.running = undefined;
1321
}
1322

    
1323

    
1324
ProtocolMessage.prototype.setOption = function(name, value) {
1325
  if (!this.options_) {
1326
    this.options_ = {};
1327
  }
1328
  this.options_[name] = value;
1329
};
1330

    
1331

    
1332
ProtocolMessage.prototype.failed = function(message, opt_details) {
1333
  this.success = false;
1334
  this.message = message;
1335
  if (IS_OBJECT(opt_details)) {
1336
    this.error_details = opt_details;
1337
  }
1338
};
1339

    
1340

    
1341
ProtocolMessage.prototype.toJSONProtocol = function() {
1342
  // Encode the protocol header.
1343
  var json = {};
1344
  json.seq= this.seq;
1345
  if (this.request_seq) {
1346
    json.request_seq = this.request_seq;
1347
  }
1348
  json.type = this.type;
1349
  if (this.event) {
1350
    json.event = this.event;
1351
  }
1352
  if (this.command) {
1353
    json.command = this.command;
1354
  }
1355
  if (this.success) {
1356
    json.success = this.success;
1357
  } else {
1358
    json.success = false;
1359
  }
1360
  if (this.body) {
1361
    // Encode the body part.
1362
    var bodyJson;
1363
    var serializer = MakeMirrorSerializer(true, this.options_);
1364
    if (this.body instanceof Mirror) {
1365
      bodyJson = serializer.serializeValue(this.body);
1366
    } else if (this.body instanceof Array) {
1367
      bodyJson = [];
1368
      for (var i = 0; i < this.body.length; i++) {
1369
        if (this.body[i] instanceof Mirror) {
1370
          bodyJson.push(serializer.serializeValue(this.body[i]));
1371
        } else {
1372
          bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
1373
        }
1374
      }
1375
    } else {
1376
      bodyJson = ObjectToProtocolObject_(this.body, serializer);
1377
    }
1378
    json.body = bodyJson;
1379
    json.refs = serializer.serializeReferencedObjects();
1380
  }
1381
  if (this.message) {
1382
    json.message = this.message;
1383
  }
1384
  if (this.error_details) {
1385
    json.error_details = this.error_details;
1386
  }
1387
  json.running = this.running;
1388
  return JSON.stringify(json);
1389
};
1390

    
1391

    
1392
DebugCommandProcessor.prototype.createResponse = function(request) {
1393
  return new ProtocolMessage(request);
1394
};
1395

    
1396

    
1397
DebugCommandProcessor.prototype.processDebugJSONRequest = function(
1398
    json_request) {
1399
  var request;  // Current request.
1400
  var response;  // Generated response.
1401
  try {
1402
    try {
1403
      // Convert the JSON string to an object.
1404
      request = JSON.parse(json_request);
1405

    
1406
      // Create an initial response.
1407
      response = this.createResponse(request);
1408

    
1409
      if (!request.type) {
1410
        throw new Error('Type not specified');
1411
      }
1412

    
1413
      if (request.type != 'request') {
1414
        throw new Error("Illegal type '" + request.type + "' in request");
1415
      }
1416

    
1417
      if (!request.command) {
1418
        throw new Error('Command not specified');
1419
      }
1420

    
1421
      if (request.arguments) {
1422
        var args = request.arguments;
1423
        // TODO(yurys): remove request.arguments.compactFormat check once
1424
        // ChromeDevTools are switched to 'inlineRefs'
1425
        if (args.inlineRefs || args.compactFormat) {
1426
          response.setOption('inlineRefs', true);
1427
        }
1428
        if (!IS_UNDEFINED(args.maxStringLength)) {
1429
          response.setOption('maxStringLength', args.maxStringLength);
1430
        }
1431
      }
1432

    
1433
      if (request.command == 'continue') {
1434
        this.continueRequest_(request, response);
1435
      } else if (request.command == 'break') {
1436
        this.breakRequest_(request, response);
1437
      } else if (request.command == 'setbreakpoint') {
1438
        this.setBreakPointRequest_(request, response);
1439
      } else if (request.command == 'changebreakpoint') {
1440
        this.changeBreakPointRequest_(request, response);
1441
      } else if (request.command == 'clearbreakpoint') {
1442
        this.clearBreakPointRequest_(request, response);
1443
      } else if (request.command == 'clearbreakpointgroup') {
1444
        this.clearBreakPointGroupRequest_(request, response);
1445
      } else if (request.command == 'disconnect') {
1446
        this.disconnectRequest_(request, response);
1447
      } else if (request.command == 'setexceptionbreak') {
1448
        this.setExceptionBreakRequest_(request, response);
1449
      } else if (request.command == 'listbreakpoints') {
1450
        this.listBreakpointsRequest_(request, response);
1451
      } else if (request.command == 'backtrace') {
1452
        this.backtraceRequest_(request, response);
1453
      } else if (request.command == 'frame') {
1454
        this.frameRequest_(request, response);
1455
      } else if (request.command == 'scopes') {
1456
        this.scopesRequest_(request, response);
1457
      } else if (request.command == 'scope') {
1458
        this.scopeRequest_(request, response);
1459
      } else if (request.command == 'setVariableValue') {
1460
        this.setVariableValueRequest_(request, response);
1461
      } else if (request.command == 'evaluate') {
1462
        this.evaluateRequest_(request, response);
1463
      } else if (request.command == 'lookup') {
1464
        this.lookupRequest_(request, response);
1465
      } else if (request.command == 'references') {
1466
        this.referencesRequest_(request, response);
1467
      } else if (request.command == 'source') {
1468
        this.sourceRequest_(request, response);
1469
      } else if (request.command == 'scripts') {
1470
        this.scriptsRequest_(request, response);
1471
      } else if (request.command == 'threads') {
1472
        this.threadsRequest_(request, response);
1473
      } else if (request.command == 'suspend') {
1474
        this.suspendRequest_(request, response);
1475
      } else if (request.command == 'version') {
1476
        this.versionRequest_(request, response);
1477
      } else if (request.command == 'changelive') {
1478
        this.changeLiveRequest_(request, response);
1479
      } else if (request.command == 'restartframe') {
1480
        this.restartFrameRequest_(request, response);
1481
      } else if (request.command == 'flags') {
1482
        this.debuggerFlagsRequest_(request, response);
1483
      } else if (request.command == 'v8flags') {
1484
        this.v8FlagsRequest_(request, response);
1485

    
1486
      // GC tools:
1487
      } else if (request.command == 'gc') {
1488
        this.gcRequest_(request, response);
1489

    
1490
      } else {
1491
        throw new Error('Unknown command "' + request.command + '" in request');
1492
      }
1493
    } catch (e) {
1494
      // If there is no response object created one (without command).
1495
      if (!response) {
1496
        response = this.createResponse();
1497
      }
1498
      response.success = false;
1499
      response.message = %ToString(e);
1500
    }
1501

    
1502
    // Return the response as a JSON encoded string.
1503
    try {
1504
      if (!IS_UNDEFINED(response.running)) {
1505
        // Response controls running state.
1506
        this.running_ = response.running;
1507
      }
1508
      response.running = this.running_;
1509
      return response.toJSONProtocol();
1510
    } catch (e) {
1511
      // Failed to generate response - return generic error.
1512
      return '{"seq":' + response.seq + ',' +
1513
              '"request_seq":' + request.seq + ',' +
1514
              '"type":"response",' +
1515
              '"success":false,' +
1516
              '"message":"Internal error: ' + %ToString(e) + '"}';
1517
    }
1518
  } catch (e) {
1519
    // Failed in one of the catch blocks above - most generic error.
1520
    return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
1521
  }
1522
};
1523

    
1524

    
1525
DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
1526
  // Check for arguments for continue.
1527
  if (request.arguments) {
1528
    var count = 1;
1529
    var action = Debug.StepAction.StepIn;
1530

    
1531
    // Pull out arguments.
1532
    var stepaction = request.arguments.stepaction;
1533
    var stepcount = request.arguments.stepcount;
1534

    
1535
    // Get the stepcount argument if any.
1536
    if (stepcount) {
1537
      count = %ToNumber(stepcount);
1538
      if (count < 0) {
1539
        throw new Error('Invalid stepcount argument "' + stepcount + '".');
1540
      }
1541
    }
1542

    
1543
    // Get the stepaction argument.
1544
    if (stepaction) {
1545
      if (stepaction == 'in') {
1546
        action = Debug.StepAction.StepIn;
1547
      } else if (stepaction == 'min') {
1548
        action = Debug.StepAction.StepMin;
1549
      } else if (stepaction == 'next') {
1550
        action = Debug.StepAction.StepNext;
1551
      } else if (stepaction == 'out') {
1552
        action = Debug.StepAction.StepOut;
1553
      } else {
1554
        throw new Error('Invalid stepaction argument "' + stepaction + '".');
1555
      }
1556
    }
1557

    
1558
    // Set up the VM for stepping.
1559
    this.exec_state_.prepareStep(action, count);
1560
  }
1561

    
1562
  // VM should be running after executing this request.
1563
  response.running = true;
1564
};
1565

    
1566

    
1567
DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
1568
  // Ignore as break command does not do anything when broken.
1569
};
1570

    
1571

    
1572
DebugCommandProcessor.prototype.setBreakPointRequest_ =
1573
    function(request, response) {
1574
  // Check for legal request.
1575
  if (!request.arguments) {
1576
    response.failed('Missing arguments');
1577
    return;
1578
  }
1579

    
1580
  // Pull out arguments.
1581
  var type = request.arguments.type;
1582
  var target = request.arguments.target;
1583
  var line = request.arguments.line;
1584
  var column = request.arguments.column;
1585
  var enabled = IS_UNDEFINED(request.arguments.enabled) ?
1586
      true : request.arguments.enabled;
1587
  var condition = request.arguments.condition;
1588
  var ignoreCount = request.arguments.ignoreCount;
1589
  var groupId = request.arguments.groupId;
1590

    
1591
  // Check for legal arguments.
1592
  if (!type || IS_UNDEFINED(target)) {
1593
    response.failed('Missing argument "type" or "target"');
1594
    return;
1595
  }
1596

    
1597
  // Either function or script break point.
1598
  var break_point_number;
1599
  if (type == 'function') {
1600
    // Handle function break point.
1601
    if (!IS_STRING(target)) {
1602
      response.failed('Argument "target" is not a string value');
1603
      return;
1604
    }
1605
    var f;
1606
    try {
1607
      // Find the function through a global evaluate.
1608
      f = this.exec_state_.evaluateGlobal(target).value();
1609
    } catch (e) {
1610
      response.failed('Error: "' + %ToString(e) +
1611
                      '" evaluating "' + target + '"');
1612
      return;
1613
    }
1614
    if (!IS_FUNCTION(f)) {
1615
      response.failed('"' + target + '" does not evaluate to a function');
1616
      return;
1617
    }
1618

    
1619
    // Set function break point.
1620
    break_point_number = Debug.setBreakPoint(f, line, column, condition);
1621
  } else if (type == 'handle') {
1622
    // Find the object pointed by the specified handle.
1623
    var handle = parseInt(target, 10);
1624
    var mirror = LookupMirror(handle);
1625
    if (!mirror) {
1626
      return response.failed('Object #' + handle + '# not found');
1627
    }
1628
    if (!mirror.isFunction()) {
1629
      return response.failed('Object #' + handle + '# is not a function');
1630
    }
1631

    
1632
    // Set function break point.
1633
    break_point_number = Debug.setBreakPoint(mirror.value(),
1634
                                             line, column, condition);
1635
  } else if (type == 'script') {
1636
    // set script break point.
1637
    break_point_number =
1638
        Debug.setScriptBreakPointByName(target, line, column, condition,
1639
                                        groupId);
1640
  } else if (type == 'scriptId') {
1641
    break_point_number =
1642
        Debug.setScriptBreakPointById(target, line, column, condition, groupId);
1643
  } else if (type == 'scriptRegExp') {
1644
    break_point_number =
1645
        Debug.setScriptBreakPointByRegExp(target, line, column, condition,
1646
                                          groupId);
1647
  } else {
1648
    response.failed('Illegal type "' + type + '"');
1649
    return;
1650
  }
1651

    
1652
  // Set additional break point properties.
1653
  var break_point = Debug.findBreakPoint(break_point_number);
1654
  if (ignoreCount) {
1655
    Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
1656
  }
1657
  if (!enabled) {
1658
    Debug.disableBreakPoint(break_point_number);
1659
  }
1660

    
1661
  // Add the break point number to the response.
1662
  response.body = { type: type,
1663
                    breakpoint: break_point_number };
1664

    
1665
  // Add break point information to the response.
1666
  if (break_point instanceof ScriptBreakPoint) {
1667
    if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1668
      response.body.type = 'scriptId';
1669
      response.body.script_id = break_point.script_id();
1670
    } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
1671
      response.body.type = 'scriptName';
1672
      response.body.script_name = break_point.script_name();
1673
    } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
1674
      response.body.type = 'scriptRegExp';
1675
      response.body.script_regexp = break_point.script_regexp_object().source;
1676
    } else {
1677
      throw new Error("Internal error: Unexpected breakpoint type: " +
1678
                      break_point.type());
1679
    }
1680
    response.body.line = break_point.line();
1681
    response.body.column = break_point.column();
1682
    response.body.actual_locations = break_point.actual_locations();
1683
  } else {
1684
    response.body.type = 'function';
1685
    response.body.actual_locations = [break_point.actual_location];
1686
  }
1687
};
1688

    
1689

    
1690
DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(
1691
    request, response) {
1692
  // Check for legal request.
1693
  if (!request.arguments) {
1694
    response.failed('Missing arguments');
1695
    return;
1696
  }
1697

    
1698
  // Pull out arguments.
1699
  var break_point = %ToNumber(request.arguments.breakpoint);
1700
  var enabled = request.arguments.enabled;
1701
  var condition = request.arguments.condition;
1702
  var ignoreCount = request.arguments.ignoreCount;
1703

    
1704
  // Check for legal arguments.
1705
  if (!break_point) {
1706
    response.failed('Missing argument "breakpoint"');
1707
    return;
1708
  }
1709

    
1710
  // Change enabled state if supplied.
1711
  if (!IS_UNDEFINED(enabled)) {
1712
    if (enabled) {
1713
      Debug.enableBreakPoint(break_point);
1714
    } else {
1715
      Debug.disableBreakPoint(break_point);
1716
    }
1717
  }
1718

    
1719
  // Change condition if supplied
1720
  if (!IS_UNDEFINED(condition)) {
1721
    Debug.changeBreakPointCondition(break_point, condition);
1722
  }
1723

    
1724
  // Change ignore count if supplied
1725
  if (!IS_UNDEFINED(ignoreCount)) {
1726
    Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
1727
  }
1728
};
1729

    
1730

    
1731
DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(
1732
    request, response) {
1733
  // Check for legal request.
1734
  if (!request.arguments) {
1735
    response.failed('Missing arguments');
1736
    return;
1737
  }
1738

    
1739
  // Pull out arguments.
1740
  var group_id = request.arguments.groupId;
1741

    
1742
  // Check for legal arguments.
1743
  if (!group_id) {
1744
    response.failed('Missing argument "groupId"');
1745
    return;
1746
  }
1747

    
1748
  var cleared_break_points = [];
1749
  var new_script_break_points = [];
1750
  for (var i = 0; i < script_break_points.length; i++) {
1751
    var next_break_point = script_break_points[i];
1752
    if (next_break_point.groupId() == group_id) {
1753
      cleared_break_points.push(next_break_point.number());
1754
      next_break_point.clear();
1755
    } else {
1756
      new_script_break_points.push(next_break_point);
1757
    }
1758
  }
1759
  script_break_points = new_script_break_points;
1760

    
1761
  // Add the cleared break point numbers to the response.
1762
  response.body = { breakpoints: cleared_break_points };
1763
};
1764

    
1765

    
1766
DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(
1767
    request, response) {
1768
  // Check for legal request.
1769
  if (!request.arguments) {
1770
    response.failed('Missing arguments');
1771
    return;
1772
  }
1773

    
1774
  // Pull out arguments.
1775
  var break_point = %ToNumber(request.arguments.breakpoint);
1776

    
1777
  // Check for legal arguments.
1778
  if (!break_point) {
1779
    response.failed('Missing argument "breakpoint"');
1780
    return;
1781
  }
1782

    
1783
  // Clear break point.
1784
  Debug.clearBreakPoint(break_point);
1785

    
1786
  // Add the cleared break point number to the response.
1787
  response.body = { breakpoint: break_point };
1788
};
1789

    
1790

    
1791
DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(
1792
    request, response) {
1793
  var array = [];
1794
  for (var i = 0; i < script_break_points.length; i++) {
1795
    var break_point = script_break_points[i];
1796

    
1797
    var description = {
1798
      number: break_point.number(),
1799
      line: break_point.line(),
1800
      column: break_point.column(),
1801
      groupId: break_point.groupId(),
1802
      hit_count: break_point.hit_count(),
1803
      active: break_point.active(),
1804
      condition: break_point.condition(),
1805
      ignoreCount: break_point.ignoreCount(),
1806
      actual_locations: break_point.actual_locations()
1807
    };
1808

    
1809
    if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1810
      description.type = 'scriptId';
1811
      description.script_id = break_point.script_id();
1812
    } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
1813
      description.type = 'scriptName';
1814
      description.script_name = break_point.script_name();
1815
    } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
1816
      description.type = 'scriptRegExp';
1817
      description.script_regexp = break_point.script_regexp_object().source;
1818
    } else {
1819
      throw new Error("Internal error: Unexpected breakpoint type: " +
1820
                      break_point.type());
1821
    }
1822
    array.push(description);
1823
  }
1824

    
1825
  response.body = {
1826
    breakpoints: array,
1827
    breakOnExceptions: Debug.isBreakOnException(),
1828
    breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException()
1829
  };
1830
};
1831

    
1832

    
1833
DebugCommandProcessor.prototype.disconnectRequest_ =
1834
    function(request, response) {
1835
  Debug.disableAllBreakPoints();
1836
  this.continueRequest_(request, response);
1837
};
1838

    
1839

    
1840
DebugCommandProcessor.prototype.setExceptionBreakRequest_ =
1841
    function(request, response) {
1842
  // Check for legal request.
1843
  if (!request.arguments) {
1844
    response.failed('Missing arguments');
1845
    return;
1846
  }
1847

    
1848
  // Pull out and check the 'type' argument:
1849
  var type = request.arguments.type;
1850
  if (!type) {
1851
    response.failed('Missing argument "type"');
1852
    return;
1853
  }
1854

    
1855
  // Initialize the default value of enable:
1856
  var enabled;
1857
  if (type == 'all') {
1858
    enabled = !Debug.isBreakOnException();
1859
  } else if (type == 'uncaught') {
1860
    enabled = !Debug.isBreakOnUncaughtException();
1861
  }
1862

    
1863
  // Pull out and check the 'enabled' argument if present:
1864
  if (!IS_UNDEFINED(request.arguments.enabled)) {
1865
    enabled = request.arguments.enabled;
1866
    if ((enabled != true) && (enabled != false)) {
1867
      response.failed('Illegal value for "enabled":"' + enabled + '"');
1868
    }
1869
  }
1870

    
1871
  // Now set the exception break state:
1872
  if (type == 'all') {
1873
    %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled);
1874
  } else if (type == 'uncaught') {
1875
    %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled);
1876
  } else {
1877
    response.failed('Unknown "type":"' + type + '"');
1878
  }
1879

    
1880
  // Add the cleared break point number to the response.
1881
  response.body = { 'type': type, 'enabled': enabled };
1882
};
1883

    
1884

    
1885
DebugCommandProcessor.prototype.backtraceRequest_ = function(
1886
    request, response) {
1887
  // Get the number of frames.
1888
  var total_frames = this.exec_state_.frameCount();
1889

    
1890
  // Create simple response if there are no frames.
1891
  if (total_frames == 0) {
1892
    response.body = {
1893
      totalFrames: total_frames
1894
    };
1895
    return;
1896
  }
1897

    
1898
  // Default frame range to include in backtrace.
1899
  var from_index = 0;
1900
  var to_index = kDefaultBacktraceLength;
1901

    
1902
  // Get the range from the arguments.
1903
  if (request.arguments) {
1904
    if (request.arguments.fromFrame) {
1905
      from_index = request.arguments.fromFrame;
1906
    }
1907
    if (request.arguments.toFrame) {
1908
      to_index = request.arguments.toFrame;
1909
    }
1910
    if (request.arguments.bottom) {
1911
      var tmp_index = total_frames - from_index;
1912
      from_index = total_frames - to_index;
1913
      to_index = tmp_index;
1914
    }
1915
    if (from_index < 0 || to_index < 0) {
1916
      return response.failed('Invalid frame number');
1917
    }
1918
  }
1919

    
1920
  // Adjust the index.
1921
  to_index = Math.min(total_frames, to_index);
1922

    
1923
  if (to_index <= from_index) {
1924
    var error = 'Invalid frame range';
1925
    return response.failed(error);
1926
  }
1927

    
1928
  // Create the response body.
1929
  var frames = [];
1930
  for (var i = from_index; i < to_index; i++) {
1931
    frames.push(this.exec_state_.frame(i));
1932
  }
1933
  response.body = {
1934
    fromFrame: from_index,
1935
    toFrame: to_index,
1936
    totalFrames: total_frames,
1937
    frames: frames
1938
  };
1939
};
1940

    
1941

    
1942
DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
1943
  // No frames no source.
1944
  if (this.exec_state_.frameCount() == 0) {
1945
    return response.failed('No frames');
1946
  }
1947

    
1948
  // With no arguments just keep the selected frame.
1949
  if (request.arguments) {
1950
    var index = request.arguments.number;
1951
    if (index < 0 || this.exec_state_.frameCount() <= index) {
1952
      return response.failed('Invalid frame number');
1953
    }
1954

    
1955
    this.exec_state_.setSelectedFrame(request.arguments.number);
1956
  }
1957
  response.body = this.exec_state_.frame();
1958
};
1959

    
1960

    
1961
DebugCommandProcessor.prototype.resolveFrameFromScopeDescription_ =
1962
    function(scope_description) {
1963
  // Get the frame for which the scope or scopes are requested.
1964
  // With no frameNumber argument use the currently selected frame.
1965
  if (scope_description && !IS_UNDEFINED(scope_description.frameNumber)) {
1966
    frame_index = scope_description.frameNumber;
1967
    if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
1968
      throw new Error('Invalid frame number');
1969
    }
1970
    return this.exec_state_.frame(frame_index);
1971
  } else {
1972
    return this.exec_state_.frame();
1973
  }
1974
};
1975

    
1976

    
1977
// Gets scope host object from request. It is either a function
1978
// ('functionHandle' argument must be specified) or a stack frame
1979
// ('frameNumber' may be specified and the current frame is taken by default).
1980
DebugCommandProcessor.prototype.resolveScopeHolder_ =
1981
    function(scope_description) {
1982
  if (scope_description && "functionHandle" in scope_description) {
1983
    if (!IS_NUMBER(scope_description.functionHandle)) {
1984
      throw new Error('Function handle must be a number');
1985
    }
1986
    var function_mirror = LookupMirror(scope_description.functionHandle);
1987
    if (!function_mirror) {
1988
      throw new Error('Failed to find function object by handle');
1989
    }
1990
    if (!function_mirror.isFunction()) {
1991
      throw new Error('Value of non-function type is found by handle');
1992
    }
1993
    return function_mirror;
1994
  } else {
1995
    // No frames no scopes.
1996
    if (this.exec_state_.frameCount() == 0) {
1997
      throw new Error('No scopes');
1998
    }
1999

    
2000
    // Get the frame for which the scopes are requested.
2001
    var frame = this.resolveFrameFromScopeDescription_(scope_description);
2002
    return frame;
2003
  }
2004
}
2005

    
2006

    
2007
DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
2008
  var scope_holder = this.resolveScopeHolder_(request.arguments);
2009

    
2010
  // Fill all scopes for this frame or function.
2011
  var total_scopes = scope_holder.scopeCount();
2012
  var scopes = [];
2013
  for (var i = 0; i < total_scopes; i++) {
2014
    scopes.push(scope_holder.scope(i));
2015
  }
2016
  response.body = {
2017
    fromScope: 0,
2018
    toScope: total_scopes,
2019
    totalScopes: total_scopes,
2020
    scopes: scopes
2021
  };
2022
};
2023

    
2024

    
2025
DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
2026
  // Get the frame or function for which the scope is requested.
2027
  var scope_holder = this.resolveScopeHolder_(request.arguments);
2028

    
2029
  // With no scope argument just return top scope.
2030
  var scope_index = 0;
2031
  if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
2032
    scope_index = %ToNumber(request.arguments.number);
2033
    if (scope_index < 0 || scope_holder.scopeCount() <= scope_index) {
2034
      return response.failed('Invalid scope number');
2035
    }
2036
  }
2037

    
2038
  response.body = scope_holder.scope(scope_index);
2039
};
2040

    
2041

    
2042
// Reads value from protocol description. Description may be in form of type
2043
// (for singletons), raw value (primitive types supported in JSON),
2044
// string value description plus type (for primitive values) or handle id.
2045
// Returns raw value or throws exception.
2046
DebugCommandProcessor.resolveValue_ = function(value_description) {
2047
  if ("handle" in value_description) {
2048
    var value_mirror = LookupMirror(value_description.handle);
2049
    if (!value_mirror) {
2050
      throw new Error("Failed to resolve value by handle, ' #" +
2051
          mapping.handle + "# not found");
2052
    }
2053
    return value_mirror.value();
2054
  } else if ("stringDescription" in value_description) {
2055
    if (value_description.type == BOOLEAN_TYPE) {
2056
      return Boolean(value_description.stringDescription);
2057
    } else if (value_description.type == NUMBER_TYPE) {
2058
      return Number(value_description.stringDescription);
2059
    } if (value_description.type == STRING_TYPE) {
2060
      return String(value_description.stringDescription);
2061
    } else {
2062
      throw new Error("Unknown type");
2063
    }
2064
  } else if ("value" in value_description) {
2065
    return value_description.value;
2066
  } else if (value_description.type == UNDEFINED_TYPE) {
2067
    return UNDEFINED;
2068
  } else if (value_description.type == NULL_TYPE) {
2069
    return null;
2070
  } else {
2071
    throw new Error("Failed to parse value description");
2072
  }
2073
};
2074

    
2075

    
2076
DebugCommandProcessor.prototype.setVariableValueRequest_ =
2077
    function(request, response) {
2078
  if (!request.arguments) {
2079
    response.failed('Missing arguments');
2080
    return;
2081
  }
2082

    
2083
  if (IS_UNDEFINED(request.arguments.name)) {
2084
    response.failed('Missing variable name');
2085
  }
2086
  var variable_name = request.arguments.name;
2087

    
2088
  var scope_description = request.arguments.scope;
2089

    
2090
  // Get the frame or function for which the scope is requested.
2091
  var scope_holder = this.resolveScopeHolder_(scope_description);
2092

    
2093
  if (IS_UNDEFINED(scope_description.number)) {
2094
    response.failed('Missing scope number');
2095
  }
2096
  var scope_index = %ToNumber(scope_description.number);
2097

    
2098
  var scope = scope_holder.scope(scope_index);
2099

    
2100
  var new_value =
2101
      DebugCommandProcessor.resolveValue_(request.arguments.newValue);
2102

    
2103
  scope.setVariableValue(variable_name, new_value);
2104

    
2105
  var new_value_mirror = MakeMirror(new_value);
2106

    
2107
  response.body = {
2108
    newValue: new_value_mirror
2109
  };
2110
};
2111

    
2112

    
2113
DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
2114
  if (!request.arguments) {
2115
    return response.failed('Missing arguments');
2116
  }
2117

    
2118
  // Pull out arguments.
2119
  var expression = request.arguments.expression;
2120
  var frame = request.arguments.frame;
2121
  var global = request.arguments.global;
2122
  var disable_break = request.arguments.disable_break;
2123
  var additional_context = request.arguments.additional_context;
2124

    
2125
  // The expression argument could be an integer so we convert it to a
2126
  // string.
2127
  try {
2128
    expression = String(expression);
2129
  } catch(e) {
2130
    return response.failed('Failed to convert expression argument to string');
2131
  }
2132

    
2133
  // Check for legal arguments.
2134
  if (!IS_UNDEFINED(frame) && global) {
2135
    return response.failed('Arguments "frame" and "global" are exclusive');
2136
  }
2137

    
2138
  var additional_context_object;
2139
  if (additional_context) {
2140
    additional_context_object = {};
2141
    for (var i = 0; i < additional_context.length; i++) {
2142
      var mapping = additional_context[i];
2143

    
2144
      if (!IS_STRING(mapping.name)) {
2145
        return response.failed("Context element #" + i +
2146
            " doesn't contain name:string property");
2147
      }
2148

    
2149
      var raw_value = DebugCommandProcessor.resolveValue_(mapping);
2150
      additional_context_object[mapping.name] = raw_value;
2151
    }
2152
  }
2153

    
2154
  // Global evaluate.
2155
  if (global) {
2156
    // Evaluate in the native context.
2157
    response.body = this.exec_state_.evaluateGlobal(
2158
        expression, Boolean(disable_break), additional_context_object);
2159
    return;
2160
  }
2161

    
2162
  // Default value for disable_break is true.
2163
  if (IS_UNDEFINED(disable_break)) {
2164
    disable_break = true;
2165
  }
2166

    
2167
  // No frames no evaluate in frame.
2168
  if (this.exec_state_.frameCount() == 0) {
2169
    return response.failed('No frames');
2170
  }
2171

    
2172
  // Check whether a frame was specified.
2173
  if (!IS_UNDEFINED(frame)) {
2174
    var frame_number = %ToNumber(frame);
2175
    if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
2176
      return response.failed('Invalid frame "' + frame + '"');
2177
    }
2178
    // Evaluate in the specified frame.
2179
    response.body = this.exec_state_.frame(frame_number).evaluate(
2180
        expression, Boolean(disable_break), additional_context_object);
2181
    return;
2182
  } else {
2183
    // Evaluate in the selected frame.
2184
    response.body = this.exec_state_.frame().evaluate(
2185
        expression, Boolean(disable_break), additional_context_object);
2186
    return;
2187
  }
2188
};
2189

    
2190

    
2191
DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
2192
  if (!request.arguments) {
2193
    return response.failed('Missing arguments');
2194
  }
2195

    
2196
  // Pull out arguments.
2197
  var handles = request.arguments.handles;
2198

    
2199
  // Check for legal arguments.
2200
  if (IS_UNDEFINED(handles)) {
2201
    return response.failed('Argument "handles" missing');
2202
  }
2203

    
2204
  // Set 'includeSource' option for script lookup.
2205
  if (!IS_UNDEFINED(request.arguments.includeSource)) {
2206
    includeSource = %ToBoolean(request.arguments.includeSource);
2207
    response.setOption('includeSource', includeSource);
2208
  }
2209

    
2210
  // Lookup handles.
2211
  var mirrors = {};
2212
  for (var i = 0; i < handles.length; i++) {
2213
    var handle = handles[i];
2214
    var mirror = LookupMirror(handle);
2215
    if (!mirror) {
2216
      return response.failed('Object #' + handle + '# not found');
2217
    }
2218
    mirrors[handle] = mirror;
2219
  }
2220
  response.body = mirrors;
2221
};
2222

    
2223

    
2224
DebugCommandProcessor.prototype.referencesRequest_ =
2225
    function(request, response) {
2226
  if (!request.arguments) {
2227
    return response.failed('Missing arguments');
2228
  }
2229

    
2230
  // Pull out arguments.
2231
  var type = request.arguments.type;
2232
  var handle = request.arguments.handle;
2233

    
2234
  // Check for legal arguments.
2235
  if (IS_UNDEFINED(type)) {
2236
    return response.failed('Argument "type" missing');
2237
  }
2238
  if (IS_UNDEFINED(handle)) {
2239
    return response.failed('Argument "handle" missing');
2240
  }
2241
  if (type != 'referencedBy' && type != 'constructedBy') {
2242
    return response.failed('Invalid type "' + type + '"');
2243
  }
2244

    
2245
  // Lookup handle and return objects with references the object.
2246
  var mirror = LookupMirror(handle);
2247
  if (mirror) {
2248
    if (type == 'referencedBy') {
2249
      response.body = mirror.referencedBy();
2250
    } else {
2251
      response.body = mirror.constructedBy();
2252
    }
2253
  } else {
2254
    return response.failed('Object #' + handle + '# not found');
2255
  }
2256
};
2257

    
2258

    
2259
DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
2260
  // No frames no source.
2261
  if (this.exec_state_.frameCount() == 0) {
2262
    return response.failed('No source');
2263
  }
2264

    
2265
  var from_line;
2266
  var to_line;
2267
  var frame = this.exec_state_.frame();
2268
  if (request.arguments) {
2269
    // Pull out arguments.
2270
    from_line = request.arguments.fromLine;
2271
    to_line = request.arguments.toLine;
2272

    
2273
    if (!IS_UNDEFINED(request.arguments.frame)) {
2274
      var frame_number = %ToNumber(request.arguments.frame);
2275
      if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
2276
        return response.failed('Invalid frame "' + frame + '"');
2277
      }
2278
      frame = this.exec_state_.frame(frame_number);
2279
    }
2280
  }
2281

    
2282
  // Get the script selected.
2283
  var script = frame.func().script();
2284
  if (!script) {
2285
    return response.failed('No source');
2286
  }
2287

    
2288
  // Get the source slice and fill it into the response.
2289
  var slice = script.sourceSlice(from_line, to_line);
2290
  if (!slice) {
2291
    return response.failed('Invalid line interval');
2292
  }
2293
  response.body = {};
2294
  response.body.source = slice.sourceText();
2295
  response.body.fromLine = slice.from_line;
2296
  response.body.toLine = slice.to_line;
2297
  response.body.fromPosition = slice.from_position;
2298
  response.body.toPosition = slice.to_position;
2299
  response.body.totalLines = script.lineCount();
2300
};
2301

    
2302

    
2303
DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
2304
  var types = ScriptTypeFlag(Debug.ScriptType.Normal);
2305
  var includeSource = false;
2306
  var idsToInclude = null;
2307
  if (request.arguments) {
2308
    // Pull out arguments.
2309
    if (!IS_UNDEFINED(request.arguments.types)) {
2310
      types = %ToNumber(request.arguments.types);
2311
      if (isNaN(types) || types < 0) {
2312
        return response.failed('Invalid types "' +
2313
                               request.arguments.types + '"');
2314
      }
2315
    }
2316

    
2317
    if (!IS_UNDEFINED(request.arguments.includeSource)) {
2318
      includeSource = %ToBoolean(request.arguments.includeSource);
2319
      response.setOption('includeSource', includeSource);
2320
    }
2321

    
2322
    if (IS_ARRAY(request.arguments.ids)) {
2323
      idsToInclude = {};
2324
      var ids = request.arguments.ids;
2325
      for (var i = 0; i < ids.length; i++) {
2326
        idsToInclude[ids[i]] = true;
2327
      }
2328
    }
2329

    
2330
    var filterStr = null;
2331
    var filterNum = null;
2332
    if (!IS_UNDEFINED(request.arguments.filter)) {
2333
      var num = %ToNumber(request.arguments.filter);
2334
      if (!isNaN(num)) {
2335
        filterNum = num;
2336
      }
2337
      filterStr = request.arguments.filter;
2338
    }
2339
  }
2340

    
2341
  // Collect all scripts in the heap.
2342
  var scripts = %DebugGetLoadedScripts();
2343

    
2344
  response.body = [];
2345

    
2346
  for (var i = 0; i < scripts.length; i++) {
2347
    if (idsToInclude && !idsToInclude[scripts[i].id]) {
2348
      continue;
2349
    }
2350
    if (filterStr || filterNum) {
2351
      var script = scripts[i];
2352
      var found = false;
2353
      if (filterNum && !found) {
2354
        if (script.id && script.id === filterNum) {
2355
          found = true;
2356
        }
2357
      }
2358
      if (filterStr && !found) {
2359
        if (script.name && script.name.indexOf(filterStr) >= 0) {
2360
          found = true;
2361
        }
2362
      }
2363
      if (!found) continue;
2364
    }
2365
    if (types & ScriptTypeFlag(scripts[i].type)) {
2366
      response.body.push(MakeMirror(scripts[i]));
2367
    }
2368
  }
2369
};
2370

    
2371

    
2372
DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
2373
  // Get the number of threads.
2374
  var total_threads = this.exec_state_.threadCount();
2375

    
2376
  // Get information for all threads.
2377
  var threads = [];
2378
  for (var i = 0; i < total_threads; i++) {
2379
    var details = %GetThreadDetails(this.exec_state_.break_id, i);
2380
    var thread_info = { current: details[0],
2381
                        id: details[1]
2382
                      };
2383
    threads.push(thread_info);
2384
  }
2385

    
2386
  // Create the response body.
2387
  response.body = {
2388
    totalThreads: total_threads,
2389
    threads: threads
2390
  };
2391
};
2392

    
2393

    
2394
DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
2395
  response.running = false;
2396
};
2397

    
2398

    
2399
DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
2400
  response.body = {
2401
    V8Version: %GetV8Version()
2402
  };
2403
};
2404

    
2405

    
2406
DebugCommandProcessor.prototype.changeLiveRequest_ = function(
2407
    request, response) {
2408
  if (!request.arguments) {
2409
    return response.failed('Missing arguments');
2410
  }
2411
  var script_id = request.arguments.script_id;
2412
  var preview_only = !!request.arguments.preview_only;
2413

    
2414
  var scripts = %DebugGetLoadedScripts();
2415

    
2416
  var the_script = null;
2417
  for (var i = 0; i < scripts.length; i++) {
2418
    if (scripts[i].id == script_id) {
2419
      the_script = scripts[i];
2420
    }
2421
  }
2422
  if (!the_script) {
2423
    response.failed('Script not found');
2424
    return;
2425
  }
2426

    
2427
  var change_log = new Array();
2428

    
2429
  if (!IS_STRING(request.arguments.new_source)) {
2430
    throw "new_source argument expected";
2431
  }
2432

    
2433
  var new_source = request.arguments.new_source;
2434

    
2435
  var result_description;
2436
  try {
2437
    result_description = Debug.LiveEdit.SetScriptSource(the_script,
2438
        new_source, preview_only, change_log);
2439
  } catch (e) {
2440
    if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
2441
      response.failed(e.message, e.details);
2442
      return;
2443
    }
2444
    throw e;
2445
  }
2446
  response.body = {change_log: change_log, result: result_description};
2447

    
2448
  if (!preview_only && !this.running_ && result_description.stack_modified) {
2449
    response.body.stepin_recommended = true;
2450
  }
2451
};
2452

    
2453

    
2454
DebugCommandProcessor.prototype.restartFrameRequest_ = function(
2455
    request, response) {
2456
  if (!request.arguments) {
2457
    return response.failed('Missing arguments');
2458
  }
2459
  var frame = request.arguments.frame;
2460

    
2461
  // No frames to evaluate in frame.
2462
  if (this.exec_state_.frameCount() == 0) {
2463
    return response.failed('No frames');
2464
  }
2465

    
2466
  var frame_mirror;
2467
  // Check whether a frame was specified.
2468
  if (!IS_UNDEFINED(frame)) {
2469
    var frame_number = %ToNumber(frame);
2470
    if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
2471
      return response.failed('Invalid frame "' + frame + '"');
2472
    }
2473
    // Restart specified frame.
2474
    frame_mirror = this.exec_state_.frame(frame_number);
2475
  } else {
2476
    // Restart selected frame.
2477
    frame_mirror = this.exec_state_.frame();
2478
  }
2479

    
2480
  var result_description = Debug.LiveEdit.RestartFrame(frame_mirror);
2481
  response.body = {result: result_description};
2482
};
2483

    
2484

    
2485
DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request,
2486
                                                                 response) {
2487
  // Check for legal request.
2488
  if (!request.arguments) {
2489
    response.failed('Missing arguments');
2490
    return;
2491
  }
2492

    
2493
  // Pull out arguments.
2494
  var flags = request.arguments.flags;
2495

    
2496
  response.body = { flags: [] };
2497
  if (!IS_UNDEFINED(flags)) {
2498
    for (var i = 0; i < flags.length; i++) {
2499
      var name = flags[i].name;
2500
      var debugger_flag = debugger_flags[name];
2501
      if (!debugger_flag) {
2502
        continue;
2503
      }
2504
      if ('value' in flags[i]) {
2505
        debugger_flag.setValue(flags[i].value);
2506
      }
2507
      response.body.flags.push({ name: name, value: debugger_flag.getValue() });
2508
    }
2509
  } else {
2510
    for (var name in debugger_flags) {
2511
      var value = debugger_flags[name].getValue();
2512
      response.body.flags.push({ name: name, value: value });
2513
    }
2514
  }
2515
};
2516

    
2517

    
2518
DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) {
2519
  var flags = request.arguments.flags;
2520
  if (!flags) flags = '';
2521
  %SetFlags(flags);
2522
};
2523

    
2524

    
2525
DebugCommandProcessor.prototype.gcRequest_ = function(request, response) {
2526
  var type = request.arguments.type;
2527
  if (!type) type = 'all';
2528

    
2529
  var before = %GetHeapUsage();
2530
  %CollectGarbage(type);
2531
  var after = %GetHeapUsage();
2532

    
2533
  response.body = { "before": before, "after": after };
2534
};
2535

    
2536

    
2537
// Check whether the previously processed command caused the VM to become
2538
// running.
2539
DebugCommandProcessor.prototype.isRunning = function() {
2540
  return this.running_;
2541
};
2542

    
2543

    
2544
DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
2545
  return %SystemBreak();
2546
};
2547

    
2548

    
2549
function NumberToHex8Str(n) {
2550
  var r = "";
2551
  for (var i = 0; i < 8; ++i) {
2552
    var c = hexCharArray[n & 0x0F];  // hexCharArray is defined in uri.js
2553
    r = c + r;
2554
    n = n >>> 4;
2555
  }
2556
  return r;
2557
}
2558

    
2559

    
2560
/**
2561
 * Convert an Object to its debugger protocol representation. The representation
2562
 * may be serilized to a JSON object using JSON.stringify().
2563
 * This implementation simply runs through all string property names, converts
2564
 * each property value to a protocol value and adds the property to the result
2565
 * object. For type "object" the function will be called recursively. Note that
2566
 * circular structures will cause infinite recursion.
2567
 * @param {Object} object The object to format as protocol object.
2568
 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2569
 *     mirror objects are encountered.
2570
 * @return {Object} Protocol object value.
2571
 */
2572
function ObjectToProtocolObject_(object, mirror_serializer) {
2573
  var content = {};
2574
  for (var key in object) {
2575
    // Only consider string keys.
2576
    if (typeof key == 'string') {
2577
      // Format the value based on its type.
2578
      var property_value_json = ValueToProtocolValue_(object[key],
2579
                                                      mirror_serializer);
2580
      // Add the property if relevant.
2581
      if (!IS_UNDEFINED(property_value_json)) {
2582
        content[key] = property_value_json;
2583
      }
2584
    }
2585
  }
2586

    
2587
  return content;
2588
}
2589

    
2590

    
2591
/**
2592
 * Convert an array to its debugger protocol representation. It will convert
2593
 * each array element to a protocol value.
2594
 * @param {Array} array The array to format as protocol array.
2595
 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2596
 *     mirror objects are encountered.
2597
 * @return {Array} Protocol array value.
2598
 */
2599
function ArrayToProtocolArray_(array, mirror_serializer) {
2600
  var json = [];
2601
  for (var i = 0; i < array.length; i++) {
2602
    json.push(ValueToProtocolValue_(array[i], mirror_serializer));
2603
  }
2604
  return json;
2605
}
2606

    
2607

    
2608
/**
2609
 * Convert a value to its debugger protocol representation.
2610
 * @param {*} value The value to format as protocol value.
2611
 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2612
 *     mirror objects are encountered.
2613
 * @return {*} Protocol value.
2614
 */
2615
function ValueToProtocolValue_(value, mirror_serializer) {
2616
  // Format the value based on its type.
2617
  var json;
2618
  switch (typeof value) {
2619
    case 'object':
2620
      if (value instanceof Mirror) {
2621
        json = mirror_serializer.serializeValue(value);
2622
      } else if (IS_ARRAY(value)){
2623
        json = ArrayToProtocolArray_(value, mirror_serializer);
2624
      } else {
2625
        json = ObjectToProtocolObject_(value, mirror_serializer);
2626
      }
2627
      break;
2628

    
2629
    case 'boolean':
2630
    case 'string':
2631
    case 'number':
2632
      json = value;
2633
      break;
2634

    
2635
    default:
2636
      json = null;
2637
  }
2638
  return json;
2639
}
2640

    
2641
Debug.TestApi = {
2642
  CommandProcessorResolveValue: DebugCommandProcessor.resolveValue_
2643
};