1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5(function (global, utils) {
6"use strict";
7
8// ----------------------------------------------------------------------------
9// Imports
10
11var FrameMirror = global.FrameMirror;
12var GlobalArray = global.Array;
13var GlobalRegExp = global.RegExp;
14var IsNaN = global.isNaN;
15var MakeMirror = global.MakeMirror;
16var MathMin = global.Math.min;
17var Mirror = global.Mirror;
18var ValueMirror = global.ValueMirror;
19
20//----------------------------------------------------------------------------
21
22// Default number of frames to include in the response to backtrace request.
23var kDefaultBacktraceLength = 10;
24
25var Debug = {};
26
27// Regular expression to skip "crud" at the beginning of a source line which is
28// not really code. Currently the regular expression matches whitespace and
29// comments.
30var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
31
32// Debug events which can occour in the V8 JavaScript engine. These originate
33// from the API include file debug.h.
34Debug.DebugEvent = { Break: 1,
35                     Exception: 2,
36                     AfterCompile: 3,
37                     CompileError: 4,
38                     AsyncTaskEvent: 5 };
39
40// Types of exceptions that can be broken upon.
41Debug.ExceptionBreak = { Caught : 0,
42                         Uncaught: 1 };
43
44// The different types of steps.
45Debug.StepAction = { StepOut: 0,
46                     StepNext: 1,
47                     StepIn: 2 };
48
49// The different types of scripts matching enum ScriptType in objects.h.
50Debug.ScriptType = { Native: 0,
51                     Extension: 1,
52                     Normal: 2,
53                     Wasm: 3};
54
55// The different types of script compilations matching enum
56// Script::CompilationType in objects.h.
57Debug.ScriptCompilationType = { Host: 0,
58                                Eval: 1,
59                                JSON: 2 };
60
61// The different script break point types.
62Debug.ScriptBreakPointType = { ScriptId: 0,
63                               ScriptName: 1,
64                               ScriptRegExp: 2 };
65
66// The different types of breakpoint position alignments.
67// Must match BreakPositionAlignment in debug.h.
68Debug.BreakPositionAlignment = {
69  Statement: 0,
70  BreakPosition: 1
71};
72
73function ScriptTypeFlag(type) {
74  return (1 << type);
75}
76
77// Globals.
78var next_response_seq = 0;
79var next_break_point_number = 1;
80var break_points = [];
81var script_break_points = [];
82var debugger_flags = {
83  breakPointsActive: {
84    value: true,
85    getValue: function() { return this.value; },
86    setValue: function(value) {
87      this.value = !!value;
88      %SetBreakPointsActive(this.value);
89    }
90  },
91  breakOnCaughtException: {
92    getValue: function() { return Debug.isBreakOnException(); },
93    setValue: function(value) {
94      if (value) {
95        Debug.setBreakOnException();
96      } else {
97        Debug.clearBreakOnException();
98      }
99    }
100  },
101  breakOnUncaughtException: {
102    getValue: function() { return Debug.isBreakOnUncaughtException(); },
103    setValue: function(value) {
104      if (value) {
105        Debug.setBreakOnUncaughtException();
106      } else {
107        Debug.clearBreakOnUncaughtException();
108      }
109    }
110  },
111};
112
113
114// Create a new break point object and add it to the list of break points.
115function MakeBreakPoint(source_position, opt_script_break_point) {
116  var break_point = new BreakPoint(source_position, opt_script_break_point);
117  break_points.push(break_point);
118  return break_point;
119}
120
121
122// Object representing a break point.
123// NOTE: This object does not have a reference to the function having break
124// point as this would cause function not to be garbage collected when it is
125// not used any more. We do not want break points to keep functions alive.
126function BreakPoint(source_position, opt_script_break_point) {
127  this.source_position_ = source_position;
128  if (opt_script_break_point) {
129    this.script_break_point_ = opt_script_break_point;
130  } else {
131    this.number_ = next_break_point_number++;
132  }
133  this.active_ = true;
134  this.condition_ = null;
135}
136
137
138BreakPoint.prototype.number = function() {
139  return this.number_;
140};
141
142
143BreakPoint.prototype.func = function() {
144  return this.func_;
145};
146
147
148BreakPoint.prototype.source_position = function() {
149  return this.source_position_;
150};
151
152
153BreakPoint.prototype.active = function() {
154  if (this.script_break_point()) {
155    return this.script_break_point().active();
156  }
157  return this.active_;
158};
159
160
161BreakPoint.prototype.condition = function() {
162  if (this.script_break_point() && this.script_break_point().condition()) {
163    return this.script_break_point().condition();
164  }
165  return this.condition_;
166};
167
168
169BreakPoint.prototype.script_break_point = function() {
170  return this.script_break_point_;
171};
172
173
174BreakPoint.prototype.enable = function() {
175  this.active_ = true;
176};
177
178
179BreakPoint.prototype.disable = function() {
180  this.active_ = false;
181};
182
183
184BreakPoint.prototype.setCondition = function(condition) {
185  this.condition_ = condition;
186};
187
188
189BreakPoint.prototype.isTriggered = function(exec_state) {
190  // Break point not active - not triggered.
191  if (!this.active()) return false;
192
193  // Check for conditional break point.
194  if (this.condition()) {
195    // If break point has condition try to evaluate it in the top frame.
196    try {
197      var mirror = exec_state.frame(0).evaluate(this.condition());
198      // If no sensible mirror or non true value break point not triggered.
199      if (!(mirror instanceof ValueMirror) || !mirror.value_) {
200        return false;
201      }
202    } catch (e) {
203      // Exception evaluating condition counts as not triggered.
204      return false;
205    }
206  }
207
208  // Break point triggered.
209  return true;
210};
211
212
213// Function called from the runtime when a break point is hit. Returns true if
214// the break point is triggered and supposed to break execution.
215function IsBreakPointTriggered(break_id, break_point) {
216  return break_point.isTriggered(MakeExecutionState(break_id));
217}
218
219
220// Object representing a script break point. The script is referenced by its
221// script name or script id and the break point is represented as line and
222// column.
223function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
224                          opt_groupId, opt_position_alignment) {
225  this.type_ = type;
226  if (type == Debug.ScriptBreakPointType.ScriptId) {
227    this.script_id_ = script_id_or_name;
228  } else if (type == Debug.ScriptBreakPointType.ScriptName) {
229    this.script_name_ = script_id_or_name;
230  } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
231    this.script_regexp_object_ = new GlobalRegExp(script_id_or_name);
232  } else {
233    throw %make_error(kDebugger, "Unexpected breakpoint type " + type);
234  }
235  this.line_ = opt_line || 0;
236  this.column_ = opt_column;
237  this.groupId_ = opt_groupId;
238  this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
239      ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
240  this.active_ = true;
241  this.condition_ = null;
242  this.break_points_ = [];
243}
244
245
246ScriptBreakPoint.prototype.number = function() {
247  return this.number_;
248};
249
250
251ScriptBreakPoint.prototype.groupId = function() {
252  return this.groupId_;
253};
254
255
256ScriptBreakPoint.prototype.type = function() {
257  return this.type_;
258};
259
260
261ScriptBreakPoint.prototype.script_id = function() {
262  return this.script_id_;
263};
264
265
266ScriptBreakPoint.prototype.script_name = function() {
267  return this.script_name_;
268};
269
270
271ScriptBreakPoint.prototype.script_regexp_object = function() {
272  return this.script_regexp_object_;
273};
274
275
276ScriptBreakPoint.prototype.line = function() {
277  return this.line_;
278};
279
280
281ScriptBreakPoint.prototype.column = function() {
282  return this.column_;
283};
284
285
286ScriptBreakPoint.prototype.actual_locations = function() {
287  var locations = [];
288  for (var i = 0; i < this.break_points_.length; i++) {
289    locations.push(this.break_points_[i].actual_location);
290  }
291  return locations;
292};
293
294
295ScriptBreakPoint.prototype.update_positions = function(line, column) {
296  this.line_ = line;
297  this.column_ = column;
298};
299
300
301ScriptBreakPoint.prototype.active = function() {
302  return this.active_;
303};
304
305
306ScriptBreakPoint.prototype.condition = function() {
307  return this.condition_;
308};
309
310
311ScriptBreakPoint.prototype.enable = function() {
312  this.active_ = true;
313};
314
315
316ScriptBreakPoint.prototype.disable = function() {
317  this.active_ = false;
318};
319
320
321ScriptBreakPoint.prototype.setCondition = function(condition) {
322  this.condition_ = condition;
323};
324
325
326// Check whether a script matches this script break point. Currently this is
327// only based on script name.
328ScriptBreakPoint.prototype.matchesScript = function(script) {
329  if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
330    return this.script_id_ == script.id;
331  } else {
332    // We might want to account columns here as well.
333    if (!(script.line_offset <= this.line_  &&
334          this.line_ < script.line_offset + %ScriptLineCount(script))) {
335      return false;
336    }
337    if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
338      return this.script_name_ == script.nameOrSourceURL();
339    } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
340      return this.script_regexp_object_.test(script.nameOrSourceURL());
341    } else {
342      throw %make_error(kDebugger, "Unexpected breakpoint type " + this.type_);
343    }
344  }
345};
346
347
348// Set the script break point in a script.
349ScriptBreakPoint.prototype.set = function (script) {
350  var column = this.column();
351  var line = this.line();
352  // If the column is undefined the break is on the line. To help locate the
353  // first piece of breakable code on the line try to find the column on the
354  // line which contains some source.
355  if (IS_UNDEFINED(column)) {
356    var source_line = %ScriptSourceLine(script, line || script.line_offset);
357
358    // Allocate array for caching the columns where the actual source starts.
359    if (!script.sourceColumnStart_) {
360      script.sourceColumnStart_ = new GlobalArray(%ScriptLineCount(script));
361    }
362
363    // Fill cache if needed and get column where the actual source starts.
364    if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
365      script.sourceColumnStart_[line] =
366          source_line.match(sourceLineBeginningSkip)[0].length;
367    }
368    column = script.sourceColumnStart_[line];
369  }
370
371  // Convert the line and column into an absolute position within the script.
372  var position = Debug.findScriptSourcePosition(script, this.line(), column);
373
374  // If the position is not found in the script (the script might be shorter
375  // than it used to be) just ignore it.
376  if (IS_NULL(position)) return;
377
378  // Create a break point object and set the break point.
379  var break_point = MakeBreakPoint(position, this);
380  var actual_position = %SetScriptBreakPoint(script, position,
381                                             this.position_alignment_,
382                                             break_point);
383  if (IS_UNDEFINED(actual_position)) {
384    actual_position = position;
385  }
386  var actual_location = script.locationFromPosition(actual_position, true);
387  break_point.actual_location = { line: actual_location.line,
388                                  column: actual_location.column,
389                                  script_id: script.id };
390  this.break_points_.push(break_point);
391  return break_point;
392};
393
394
395// Clear all the break points created from this script break point
396ScriptBreakPoint.prototype.clear = function () {
397  var remaining_break_points = [];
398  for (var i = 0; i < break_points.length; i++) {
399    if (break_points[i].script_break_point() &&
400        break_points[i].script_break_point() === this) {
401      %ClearBreakPoint(break_points[i]);
402    } else {
403      remaining_break_points.push(break_points[i]);
404    }
405  }
406  break_points = remaining_break_points;
407  this.break_points_ = [];
408};
409
410
411Debug.setListener = function(listener, opt_data) {
412  if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
413    throw %make_type_error(kDebuggerType);
414  }
415  %SetDebugEventListener(listener, opt_data);
416};
417
418
419// Returns a Script object. If the parameter is a function the return value
420// is the script in which the function is defined. If the parameter is a string
421// the return value is the script for which the script name has that string
422// value.  If it is a regexp and there is a unique script whose name matches
423// we return that, otherwise undefined.
424Debug.findScript = function(func_or_script_name) {
425  if (IS_FUNCTION(func_or_script_name)) {
426    return %FunctionGetScript(func_or_script_name);
427  } else if (%IsRegExp(func_or_script_name)) {
428    var scripts = this.scripts();
429    var last_result = null;
430    var result_count = 0;
431    for (var i in scripts) {
432      var script = scripts[i];
433      if (func_or_script_name.test(script.name)) {
434        last_result = script;
435        result_count++;
436      }
437    }
438    // Return the unique script matching the regexp.  If there are more
439    // than one we don't return a value since there is no good way to
440    // decide which one to return.  Returning a "random" one, say the
441    // first, would introduce nondeterminism (or something close to it)
442    // because the order is the heap iteration order.
443    if (result_count == 1) {
444      return last_result;
445    } else {
446      return UNDEFINED;
447    }
448  } else {
449    return %GetScript(func_or_script_name);
450  }
451};
452
453// Returns the script source. If the parameter is a function the return value
454// is the script source for the script in which the function is defined. If the
455// parameter is a string the return value is the script for which the script
456// name has that string value.
457Debug.scriptSource = function(func_or_script_name) {
458  return this.findScript(func_or_script_name).source;
459};
460
461
462Debug.source = function(f) {
463  if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType);
464  return %FunctionGetSourceCode(f);
465};
466
467
468Debug.sourcePosition = function(f) {
469  if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType);
470  return %FunctionGetScriptSourcePosition(f);
471};
472
473
474Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
475  var script = %FunctionGetScript(func);
476  var script_offset = %FunctionGetScriptSourcePosition(func);
477  return %ScriptLocationFromLine(script, opt_line, opt_column, script_offset);
478};
479
480
481// Returns the character position in a script based on a line number and an
482// optional position within that line.
483Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
484  var location = %ScriptLocationFromLine(script, opt_line, opt_column, 0);
485  return location ? location.position : null;
486};
487
488
489Debug.findBreakPoint = function(break_point_number, remove) {
490  var break_point;
491  for (var i = 0; i < break_points.length; i++) {
492    if (break_points[i].number() == break_point_number) {
493      break_point = break_points[i];
494      // Remove the break point from the list if requested.
495      if (remove) {
496        break_points.splice(i, 1);
497      }
498      break;
499    }
500  }
501  if (break_point) {
502    return break_point;
503  } else {
504    return this.findScriptBreakPoint(break_point_number, remove);
505  }
506};
507
508Debug.findBreakPointActualLocations = function(break_point_number) {
509  for (var i = 0; i < script_break_points.length; i++) {
510    if (script_break_points[i].number() == break_point_number) {
511      return script_break_points[i].actual_locations();
512    }
513  }
514  for (var i = 0; i < break_points.length; i++) {
515    if (break_points[i].number() == break_point_number) {
516      return [break_points[i].actual_location];
517    }
518  }
519  return [];
520};
521
522Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
523  if (!IS_FUNCTION(func)) throw %make_type_error(kDebuggerType);
524  // Break points in API functions are not supported.
525  if (%FunctionIsAPIFunction(func)) {
526    throw %make_error(kDebugger, 'Cannot set break point in native code.');
527  }
528  // Find source position.
529  var source_position =
530      this.findFunctionSourceLocation(func, opt_line, opt_column).position;
531  // Find the script for the function.
532  var script = %FunctionGetScript(func);
533  // Break in builtin JavaScript code is not supported.
534  if (script.type == Debug.ScriptType.Native) {
535    throw %make_error(kDebugger, 'Cannot set break point in native code.');
536  }
537  // If the script for the function has a name convert this to a script break
538  // point.
539  if (script && script.id) {
540    // Find line and column for the position in the script and set a script
541    // break point from that.
542    var location = script.locationFromPosition(source_position, false);
543    return this.setScriptBreakPointById(script.id,
544                                        location.line, location.column,
545                                        opt_condition);
546  } else {
547    // Set a break point directly on the function.
548    var break_point = MakeBreakPoint(source_position);
549    var actual_position =
550        %SetFunctionBreakPoint(func, source_position, break_point);
551    var actual_location = script.locationFromPosition(actual_position, true);
552    break_point.actual_location = { line: actual_location.line,
553                                    column: actual_location.column,
554                                    script_id: script.id };
555    break_point.setCondition(opt_condition);
556    return break_point.number();
557  }
558};
559
560
561Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
562                                                    condition, enabled,
563                                                    opt_position_alignment)
564{
565  var break_point = MakeBreakPoint(position);
566  break_point.setCondition(condition);
567  if (!enabled) {
568    break_point.disable();
569  }
570  var script = scriptById(script_id);
571  if (script) {
572    var position_alignment = IS_UNDEFINED(opt_position_alignment)
573        ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
574    break_point.actual_position = %SetScriptBreakPoint(script, position,
575        position_alignment, break_point);
576  }
577  return break_point;
578};
579
580
581Debug.enableBreakPoint = function(break_point_number) {
582  var break_point = this.findBreakPoint(break_point_number, false);
583  // Only enable if the breakpoint hasn't been deleted:
584  if (break_point) {
585    break_point.enable();
586  }
587};
588
589
590Debug.disableBreakPoint = function(break_point_number) {
591  var break_point = this.findBreakPoint(break_point_number, false);
592  // Only enable if the breakpoint hasn't been deleted:
593  if (break_point) {
594    break_point.disable();
595  }
596};
597
598
599Debug.changeBreakPointCondition = function(break_point_number, condition) {
600  var break_point = this.findBreakPoint(break_point_number, false);
601  break_point.setCondition(condition);
602};
603
604
605Debug.clearBreakPoint = function(break_point_number) {
606  var break_point = this.findBreakPoint(break_point_number, true);
607  if (break_point) {
608    return %ClearBreakPoint(break_point);
609  } else {
610    break_point = this.findScriptBreakPoint(break_point_number, true);
611    if (!break_point) throw %make_error(kDebugger, 'Invalid breakpoint');
612  }
613};
614
615
616Debug.clearAllBreakPoints = function() {
617  for (var i = 0; i < break_points.length; i++) {
618    var break_point = break_points[i];
619    %ClearBreakPoint(break_point);
620  }
621  break_points = [];
622};
623
624
625Debug.disableAllBreakPoints = function() {
626  // Disable all user defined breakpoints:
627  for (var i = 1; i < next_break_point_number; i++) {
628    Debug.disableBreakPoint(i);
629  }
630  // Disable all exception breakpoints:
631  %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
632  %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
633};
634
635
636Debug.findScriptBreakPoint = function(break_point_number, remove) {
637  var script_break_point;
638  for (var i = 0; i < script_break_points.length; i++) {
639    if (script_break_points[i].number() == break_point_number) {
640      script_break_point = script_break_points[i];
641      // Remove the break point from the list if requested.
642      if (remove) {
643        script_break_point.clear();
644        script_break_points.splice(i,1);
645      }
646      break;
647    }
648  }
649  return script_break_point;
650};
651
652
653// Sets a breakpoint in a script identified through id or name at the
654// specified source line and column within that line.
655Debug.setScriptBreakPoint = function(type, script_id_or_name,
656                                     opt_line, opt_column, opt_condition,
657                                     opt_groupId, opt_position_alignment) {
658  // Create script break point object.
659  var script_break_point =
660      new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
661                           opt_groupId, opt_position_alignment);
662
663  // Assign number to the new script break point and add it.
664  script_break_point.number_ = next_break_point_number++;
665  script_break_point.setCondition(opt_condition);
666  script_break_points.push(script_break_point);
667
668  // Run through all scripts to see if this script break point matches any
669  // loaded scripts.
670  var scripts = this.scripts();
671  for (var i = 0; i < scripts.length; i++) {
672    if (script_break_point.matchesScript(scripts[i])) {
673      script_break_point.set(scripts[i]);
674    }
675  }
676
677  return script_break_point.number();
678};
679
680
681Debug.setScriptBreakPointById = function(script_id,
682                                         opt_line, opt_column,
683                                         opt_condition, opt_groupId,
684                                         opt_position_alignment) {
685  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
686                                  script_id, opt_line, opt_column,
687                                  opt_condition, opt_groupId,
688                                  opt_position_alignment);
689};
690
691
692Debug.setScriptBreakPointByName = function(script_name,
693                                           opt_line, opt_column,
694                                           opt_condition, opt_groupId) {
695  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
696                                  script_name, opt_line, opt_column,
697                                  opt_condition, opt_groupId);
698};
699
700
701Debug.setScriptBreakPointByRegExp = function(script_regexp,
702                                             opt_line, opt_column,
703                                             opt_condition, opt_groupId) {
704  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
705                                  script_regexp, opt_line, opt_column,
706                                  opt_condition, opt_groupId);
707};
708
709
710Debug.enableScriptBreakPoint = function(break_point_number) {
711  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
712  script_break_point.enable();
713};
714
715
716Debug.disableScriptBreakPoint = function(break_point_number) {
717  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
718  script_break_point.disable();
719};
720
721
722Debug.changeScriptBreakPointCondition = function(
723    break_point_number, condition) {
724  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
725  script_break_point.setCondition(condition);
726};
727
728
729Debug.scriptBreakPoints = function() {
730  return script_break_points;
731};
732
733
734Debug.clearStepping = function() {
735  %ClearStepping();
736};
737
738Debug.setBreakOnException = function() {
739  return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
740};
741
742Debug.clearBreakOnException = function() {
743  return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
744};
745
746Debug.isBreakOnException = function() {
747  return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
748};
749
750Debug.setBreakOnUncaughtException = function() {
751  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
752};
753
754Debug.clearBreakOnUncaughtException = function() {
755  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
756};
757
758Debug.isBreakOnUncaughtException = function() {
759  return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
760};
761
762Debug.showBreakPoints = function(f, full, opt_position_alignment) {
763  if (!IS_FUNCTION(f)) throw %make_error(kDebuggerType);
764  var source = full ? this.scriptSource(f) : this.source(f);
765  var offset = full ? 0 : this.sourcePosition(f);
766  var position_alignment = IS_UNDEFINED(opt_position_alignment)
767      ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
768  var locations = %GetBreakLocations(f, position_alignment);
769  if (!locations) return source;
770  locations.sort(function(x, y) { return x - y; });
771  var result = "";
772  var prev_pos = 0;
773  var pos;
774  for (var i = 0; i < locations.length; i++) {
775    pos = locations[i] - offset;
776    result += source.slice(prev_pos, pos);
777    result += "[B" + i + "]";
778    prev_pos = pos;
779  }
780  pos = source.length;
781  result += source.substring(prev_pos, pos);
782  return result;
783};
784
785
786// Get all the scripts currently loaded. Locating all the scripts is based on
787// scanning the heap.
788Debug.scripts = function() {
789  // Collect all scripts in the heap.
790  return %DebugGetLoadedScripts();
791};
792
793
794// Get a specific script currently loaded. This is based on scanning the heap.
795// TODO(clemensh): Create a runtime function for this.
796function scriptById(scriptId) {
797  var scripts = Debug.scripts();
798  for (var script of scripts) {
799    if (script.id == scriptId) return script;
800  }
801  return UNDEFINED;
802};
803
804
805Debug.debuggerFlags = function() {
806  return debugger_flags;
807};
808
809Debug.MakeMirror = MakeMirror;
810
811function MakeExecutionState(break_id) {
812  return new ExecutionState(break_id);
813}
814
815function ExecutionState(break_id) {
816  this.break_id = break_id;
817  this.selected_frame = 0;
818}
819
820ExecutionState.prototype.prepareStep = function(action) {
821  if (action === Debug.StepAction.StepIn ||
822      action === Debug.StepAction.StepOut ||
823      action === Debug.StepAction.StepNext) {
824    return %PrepareStep(this.break_id, action);
825  }
826  throw %make_type_error(kDebuggerType);
827};
828
829ExecutionState.prototype.evaluateGlobal = function(source) {
830  return MakeMirror(%DebugEvaluateGlobal(this.break_id, source));
831};
832
833ExecutionState.prototype.frameCount = function() {
834  return %GetFrameCount(this.break_id);
835};
836
837ExecutionState.prototype.frame = function(opt_index) {
838  // If no index supplied return the selected frame.
839  if (opt_index == null) opt_index = this.selected_frame;
840  if (opt_index < 0 || opt_index >= this.frameCount()) {
841    throw %make_type_error(kDebuggerFrame);
842  }
843  return new FrameMirror(this.break_id, opt_index);
844};
845
846ExecutionState.prototype.setSelectedFrame = function(index) {
847  var i = TO_NUMBER(index);
848  if (i < 0 || i >= this.frameCount()) {
849    throw %make_type_error(kDebuggerFrame);
850  }
851  this.selected_frame = i;
852};
853
854ExecutionState.prototype.selectedFrame = function() {
855  return this.selected_frame;
856};
857
858function MakeBreakEvent(break_id, break_points_hit) {
859  return new BreakEvent(break_id, break_points_hit);
860}
861
862
863function BreakEvent(break_id, break_points_hit) {
864  this.frame_ = new FrameMirror(break_id, 0);
865  this.break_points_hit_ = break_points_hit;
866}
867
868
869BreakEvent.prototype.eventType = function() {
870  return Debug.DebugEvent.Break;
871};
872
873
874BreakEvent.prototype.func = function() {
875  return this.frame_.func();
876};
877
878
879BreakEvent.prototype.sourceLine = function() {
880  return this.frame_.sourceLine();
881};
882
883
884BreakEvent.prototype.sourceColumn = function() {
885  return this.frame_.sourceColumn();
886};
887
888
889BreakEvent.prototype.sourceLineText = function() {
890  return this.frame_.sourceLineText();
891};
892
893
894BreakEvent.prototype.breakPointsHit = function() {
895  return this.break_points_hit_;
896};
897
898
899function MakeExceptionEvent(break_id, exception, uncaught, promise) {
900  return new ExceptionEvent(break_id, exception, uncaught, promise);
901}
902
903
904function ExceptionEvent(break_id, exception, uncaught, promise) {
905  this.exec_state_ = new ExecutionState(break_id);
906  this.exception_ = exception;
907  this.uncaught_ = uncaught;
908  this.promise_ = promise;
909}
910
911
912ExceptionEvent.prototype.eventType = function() {
913  return Debug.DebugEvent.Exception;
914};
915
916
917ExceptionEvent.prototype.exception = function() {
918  return this.exception_;
919};
920
921
922ExceptionEvent.prototype.uncaught = function() {
923  return this.uncaught_;
924};
925
926
927ExceptionEvent.prototype.promise = function() {
928  return this.promise_;
929};
930
931
932ExceptionEvent.prototype.func = function() {
933  return this.exec_state_.frame(0).func();
934};
935
936
937ExceptionEvent.prototype.sourceLine = function() {
938  return this.exec_state_.frame(0).sourceLine();
939};
940
941
942ExceptionEvent.prototype.sourceColumn = function() {
943  return this.exec_state_.frame(0).sourceColumn();
944};
945
946
947ExceptionEvent.prototype.sourceLineText = function() {
948  return this.exec_state_.frame(0).sourceLineText();
949};
950
951
952function MakeCompileEvent(script, type) {
953  return new CompileEvent(script, type);
954}
955
956
957function CompileEvent(script, type) {
958  this.script_ = MakeMirror(script);
959  this.type_ = type;
960}
961
962
963CompileEvent.prototype.eventType = function() {
964  return this.type_;
965};
966
967
968CompileEvent.prototype.script = function() {
969  return this.script_;
970};
971
972
973function MakeScriptObject_(script, include_source) {
974  var o = { id: script.id(),
975            name: script.name(),
976            lineOffset: script.lineOffset(),
977            columnOffset: script.columnOffset(),
978            lineCount: script.lineCount(),
979          };
980  if (!IS_UNDEFINED(script.data())) {
981    o.data = script.data();
982  }
983  if (include_source) {
984    o.source = script.source();
985  }
986  return o;
987}
988
989
990function MakeAsyncTaskEvent(type, id) {
991  return new AsyncTaskEvent(type, id);
992}
993
994
995function AsyncTaskEvent(type, id) {
996  this.type_ = type;
997  this.id_ = id;
998}
999
1000
1001AsyncTaskEvent.prototype.type = function() {
1002  return this.type_;
1003}
1004
1005
1006AsyncTaskEvent.prototype.id = function() {
1007  return this.id_;
1008}
1009
1010// -------------------------------------------------------------------
1011// Exports
1012
1013utils.InstallConstants(global, [
1014  "Debug", Debug,
1015  "BreakEvent", BreakEvent,
1016  "CompileEvent", CompileEvent,
1017  "BreakPoint", BreakPoint,
1018]);
1019
1020// Functions needed by the debugger runtime.
1021utils.InstallFunctions(utils, DONT_ENUM, [
1022  "MakeExecutionState", MakeExecutionState,
1023  "MakeExceptionEvent", MakeExceptionEvent,
1024  "MakeBreakEvent", MakeBreakEvent,
1025  "MakeCompileEvent", MakeCompileEvent,
1026  "MakeAsyncTaskEvent", MakeAsyncTaskEvent,
1027  "IsBreakPointTriggered", IsBreakPointTriggered,
1028]);
1029
1030})
1031