| // Copyright 2012 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| (function (global, utils) { |
| "use strict"; |
| |
| // ---------------------------------------------------------------------------- |
| // Imports |
| |
| var FrameMirror = global.FrameMirror; |
| var GlobalArray = global.Array; |
| var GlobalRegExp = global.RegExp; |
| var IsNaN = global.isNaN; |
| var MakeMirror = global.MakeMirror; |
| var MathMin = global.Math.min; |
| var Mirror = global.Mirror; |
| var ValueMirror = global.ValueMirror; |
| |
| //---------------------------------------------------------------------------- |
| |
| // Default number of frames to include in the response to backtrace request. |
| var kDefaultBacktraceLength = 10; |
| |
| var Debug = {}; |
| |
| // Regular expression to skip "crud" at the beginning of a source line which is |
| // not really code. Currently the regular expression matches whitespace and |
| // comments. |
| var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/; |
| |
| // Debug events which can occur in the V8 JavaScript engine. These originate |
| // from the API include file debug.h. |
| Debug.DebugEvent = { Break: 1, |
| Exception: 2, |
| AfterCompile: 3, |
| CompileError: 4, |
| AsyncTaskEvent: 5 }; |
| |
| // Types of exceptions that can be broken upon. |
| Debug.ExceptionBreak = { Caught : 0, |
| Uncaught: 1 }; |
| |
| // The different types of steps. |
| Debug.StepAction = { StepOut: 0, |
| StepNext: 1, |
| StepIn: 2 }; |
| |
| // The different types of scripts matching enum ScriptType in objects.h. |
| Debug.ScriptType = { Native: 0, |
| Extension: 1, |
| Normal: 2, |
| Wasm: 3}; |
| |
| // The different types of script compilations matching enum |
| // Script::CompilationType in objects.h. |
| Debug.ScriptCompilationType = { Host: 0, |
| Eval: 1, |
| JSON: 2 }; |
| |
| // The different script break point types. |
| Debug.ScriptBreakPointType = { ScriptId: 0, |
| ScriptName: 1, |
| ScriptRegExp: 2 }; |
| |
| function ScriptTypeFlag(type) { |
| return (1 << type); |
| } |
| |
| // Globals. |
| var next_response_seq = 0; |
| var next_break_point_number = 1; |
| var break_points = []; |
| var script_break_points = []; |
| var debugger_flags = { |
| breakPointsActive: { |
| value: true, |
| getValue: function() { return this.value; }, |
| setValue: function(value) { |
| this.value = !!value; |
| %SetBreakPointsActive(this.value); |
| } |
| }, |
| breakOnCaughtException: { |
| getValue: function() { return Debug.isBreakOnException(); }, |
| setValue: function(value) { |
| if (value) { |
| Debug.setBreakOnException(); |
| } else { |
| Debug.clearBreakOnException(); |
| } |
| } |
| }, |
| breakOnUncaughtException: { |
| getValue: function() { return Debug.isBreakOnUncaughtException(); }, |
| setValue: function(value) { |
| if (value) { |
| Debug.setBreakOnUncaughtException(); |
| } else { |
| Debug.clearBreakOnUncaughtException(); |
| } |
| } |
| }, |
| }; |
| |
| |
| // Create a new break point object and add it to the list of break points. |
| function MakeBreakPoint(source_position, opt_script_break_point) { |
| var break_point = new BreakPoint(source_position, opt_script_break_point); |
| break_points.push(break_point); |
| return break_point; |
| } |
| |
| |
| // Object representing a break point. |
| // NOTE: This object does not have a reference to the function having break |
| // point as this would cause function not to be garbage collected when it is |
| // not used any more. We do not want break points to keep functions alive. |
| function BreakPoint(source_position, opt_script_break_point) { |
| this.source_position_ = source_position; |
| if (opt_script_break_point) { |
| this.script_break_point_ = opt_script_break_point; |
| } else { |
| this.number_ = next_break_point_number++; |
| } |
| this.active_ = true; |
| this.condition_ = null; |
| } |
| |
| |
| BreakPoint.prototype.number = function() { |
| return this.number_; |
| }; |
| |
| |
| BreakPoint.prototype.func = function() { |
| return this.func_; |
| }; |
| |
| |
| BreakPoint.prototype.source_position = function() { |
| return this.source_position_; |
| }; |
| |
| |
| BreakPoint.prototype.active = function() { |
| if (this.script_break_point()) { |
| return this.script_break_point().active(); |
| } |
| return this.active_; |
| }; |
| |
| |
| BreakPoint.prototype.condition = function() { |
| if (this.script_break_point() && this.script_break_point().condition()) { |
| return this.script_break_point().condition(); |
| } |
| return this.condition_; |
| }; |
| |
| |
| BreakPoint.prototype.script_break_point = function() { |
| return this.script_break_point_; |
| }; |
| |
| |
| BreakPoint.prototype.enable = function() { |
| this.active_ = true; |
| }; |
| |
| |
| BreakPoint.prototype.disable = function() { |
| this.active_ = false; |
| }; |
| |
| |
| BreakPoint.prototype.setCondition = function(condition) { |
| this.condition_ = condition; |
| }; |
| |
| |
| BreakPoint.prototype.isTriggered = function(exec_state) { |
| // Break point not active - not triggered. |
| if (!this.active()) return false; |
| |
| // Check for conditional break point. |
| if (this.condition()) { |
| // If break point has condition try to evaluate it in the top frame. |
| try { |
| var mirror = exec_state.frame(0).evaluate(this.condition()); |
| // If no sensible mirror or non true value break point not triggered. |
| if (!(mirror instanceof ValueMirror) || !mirror.value_) { |
| return false; |
| } |
| } catch (e) { |
| // Exception evaluating condition counts as not triggered. |
| return false; |
| } |
| } |
| |
| // Break point triggered. |
| return true; |
| }; |
| |
| |
| // Function called from the runtime when a break point is hit. Returns true if |
| // the break point is triggered and supposed to break execution. |
| function IsBreakPointTriggered(break_id, break_point) { |
| return break_point.isTriggered(MakeExecutionState(break_id)); |
| } |
| |
| |
| // Object representing a script break point. The script is referenced by its |
| // script name or script id and the break point is represented as line and |
| // column. |
| function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, |
| opt_groupId) { |
| this.type_ = type; |
| if (type == Debug.ScriptBreakPointType.ScriptId) { |
| this.script_id_ = script_id_or_name; |
| } else if (type == Debug.ScriptBreakPointType.ScriptName) { |
| this.script_name_ = script_id_or_name; |
| } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) { |
| this.script_regexp_object_ = new GlobalRegExp(script_id_or_name); |
| } else { |
| throw %make_error(kDebugger, "Unexpected breakpoint type " + type); |
| } |
| this.line_ = opt_line || 0; |
| this.column_ = opt_column; |
| this.groupId_ = opt_groupId; |
| this.active_ = true; |
| this.condition_ = null; |
| this.break_points_ = []; |
| } |
| |
| |
| ScriptBreakPoint.prototype.number = function() { |
| return this.number_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.groupId = function() { |
| return this.groupId_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.type = function() { |
| return this.type_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.script_id = function() { |
| return this.script_id_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.script_name = function() { |
| return this.script_name_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.script_regexp_object = function() { |
| return this.script_regexp_object_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.line = function() { |
| return this.line_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.column = function() { |
| return this.column_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.actual_locations = function() { |
| var locations = []; |
| for (var i = 0; i < this.break_points_.length; i++) { |
| locations.push(this.break_points_[i].actual_location); |
| } |
| return locations; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.update_positions = function(line, column) { |
| this.line_ = line; |
| this.column_ = column; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.active = function() { |
| return this.active_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.condition = function() { |
| return this.condition_; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.enable = function() { |
| this.active_ = true; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.disable = function() { |
| this.active_ = false; |
| }; |
| |
| |
| ScriptBreakPoint.prototype.setCondition = function(condition) { |
| this.condition_ = condition; |
| }; |
| |
| |
| // Check whether a script matches this script break point. Currently this is |
| // only based on script name. |
| ScriptBreakPoint.prototype.matchesScript = function(script) { |
| if (this.type_ == Debug.ScriptBreakPointType.ScriptId) { |
| return this.script_id_ == script.id; |
| } else { |
| // We might want to account columns here as well. |
| if (!(script.line_offset <= this.line_ && |
| this.line_ < script.line_offset + %ScriptLineCount(script))) { |
| return false; |
| } |
| if (this.type_ == Debug.ScriptBreakPointType.ScriptName) { |
| return this.script_name_ == script.nameOrSourceURL(); |
| } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) { |
| return this.script_regexp_object_.test(script.nameOrSourceURL()); |
| } else { |
| throw %make_error(kDebugger, "Unexpected breakpoint type " + this.type_); |
| } |
| } |
| }; |
| |
| |
| // Set the script break point in a script. |
| ScriptBreakPoint.prototype.set = function (script) { |
| var column = this.column(); |
| var line = this.line(); |
| // If the column is undefined the break is on the line. To help locate the |
| // first piece of breakable code on the line try to find the column on the |
| // line which contains some source. |
| if (IS_UNDEFINED(column)) { |
| var source_line = %ScriptSourceLine(script, line || script.line_offset); |
| |
| // Allocate array for caching the columns where the actual source starts. |
| if (!script.sourceColumnStart_) { |
| script.sourceColumnStart_ = new GlobalArray(%ScriptLineCount(script)); |
| } |
| |
| // Fill cache if needed and get column where the actual source starts. |
| if (IS_UNDEFINED(script.sourceColumnStart_[line])) { |
| script.sourceColumnStart_[line] = |
| source_line.match(sourceLineBeginningSkip)[0].length; |
| } |
| column = script.sourceColumnStart_[line]; |
| } |
| |
| // Convert the line and column into an absolute position within the script. |
| var position = Debug.findScriptSourcePosition(script, this.line(), column); |
| |
| // If the position is not found in the script (the script might be shorter |
| // than it used to be) just ignore it. |
| if (IS_NULL(position)) return; |
| |
| // Create a break point object and set the break point. |
| var break_point = MakeBreakPoint(position, this); |
| var actual_position = %SetScriptBreakPoint(script, position, |
| break_point); |
| if (IS_UNDEFINED(actual_position)) { |
| actual_position = position; |
| } |
| var actual_location = script.locationFromPosition(actual_position, true); |
| break_point.actual_location = { line: actual_location.line, |
| column: actual_location.column, |
| script_id: script.id }; |
| this.break_points_.push(break_point); |
| return break_point; |
| }; |
| |
| |
| // Clear all the break points created from this script break point |
| ScriptBreakPoint.prototype.clear = function () { |
| var remaining_break_points = []; |
| for (var i = 0; i < break_points.length; i++) { |
| if (break_points[i].script_break_point() && |
| break_points[i].script_break_point() === this) { |
| %ClearBreakPoint(break_points[i]); |
| } else { |
| remaining_break_points.push(break_points[i]); |
| } |
| } |
| break_points = remaining_break_points; |
| this.break_points_ = []; |
| }; |
| |
| |
| Debug.setListener = function(listener, opt_data) { |
| if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) { |
| throw %make_type_error(kDebuggerType); |
| } |
| %SetDebugEventListener(listener, opt_data); |
| }; |
| |
| |
| // Returns a Script object. If the parameter is a function the return value |
| // is the script in which the function is defined. If the parameter is a string |
| // the return value is the script for which the script name has that string |
| // value. If it is a regexp and there is a unique script whose name matches |
| // we return that, otherwise undefined. |
| Debug.findScript = function(func_or_script_name) { |
| if (IS_FUNCTION(func_or_script_name)) { |
| return %FunctionGetScript(func_or_script_name); |
| } else if (%IsRegExp(func_or_script_name)) { |
| var scripts = this.scripts(); |
| var last_result = null; |
| var result_count = 0; |
| for (var i in scripts) { |
| var script = scripts[i]; |
| if (func_or_script_name.test(script.name)) { |
| last_result = script; |
| result_count++; |
| } |
| } |
| // Return the unique script matching the regexp. If there are more |
| // than one we don't return a value since there is no good way to |
| // decide which one to return. Returning a "random" one, say the |
| // first, would introduce nondeterminism (or something close to it) |
| // because the order is the heap iteration order. |
| if (result_count == 1) { |
| return last_result; |
| } else { |
| return UNDEFINED; |
| } |
| } else { |
| return %GetScript(func_or_script_name); |
| } |
| }; |
| |
| // Returns the script source. If the parameter is a function the return value |
| // is the script source for the script in which the function is defined. If the |
| // parameter is a string the return value is the script for which the script |
| // name has that string value. |
| Debug.scriptSource = function(func_or_script_name) { |
| return this.findScript(func_or_script_name).source; |
| }; |
| |
| |
| Debug.source = function(f) { |
| if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType); |
| return %FunctionGetSourceCode(f); |
| }; |
| |
| |
| Debug.sourcePosition = function(f) { |
| if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType); |
| return %FunctionGetScriptSourcePosition(f); |
| }; |
| |
| |
| Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { |
| var script = %FunctionGetScript(func); |
| var script_offset = %FunctionGetScriptSourcePosition(func); |
| return %ScriptLocationFromLine(script, opt_line, opt_column, script_offset); |
| }; |
| |
| |
| // Returns the character position in a script based on a line number and an |
| // optional position within that line. |
| Debug.findScriptSourcePosition = function(script, opt_line, opt_column) { |
| var location = %ScriptLocationFromLine(script, opt_line, opt_column, 0); |
| return location ? location.position : null; |
| }; |
| |
| |
| Debug.findBreakPoint = function(break_point_number, remove) { |
| var break_point; |
| for (var i = 0; i < break_points.length; i++) { |
| if (break_points[i].number() == break_point_number) { |
| break_point = break_points[i]; |
| // Remove the break point from the list if requested. |
| if (remove) { |
| break_points.splice(i, 1); |
| } |
| break; |
| } |
| } |
| if (break_point) { |
| return break_point; |
| } else { |
| return this.findScriptBreakPoint(break_point_number, remove); |
| } |
| }; |
| |
| Debug.findBreakPointActualLocations = function(break_point_number) { |
| for (var i = 0; i < script_break_points.length; i++) { |
| if (script_break_points[i].number() == break_point_number) { |
| return script_break_points[i].actual_locations(); |
| } |
| } |
| for (var i = 0; i < break_points.length; i++) { |
| if (break_points[i].number() == break_point_number) { |
| return [break_points[i].actual_location]; |
| } |
| } |
| return []; |
| }; |
| |
| Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) { |
| if (!IS_FUNCTION(func)) throw %make_type_error(kDebuggerType); |
| // Break points in API functions are not supported. |
| if (%FunctionIsAPIFunction(func)) { |
| throw %make_error(kDebugger, 'Cannot set break point in native code.'); |
| } |
| // Find source position. |
| var source_position = |
| this.findFunctionSourceLocation(func, opt_line, opt_column).position; |
| // Find the script for the function. |
| var script = %FunctionGetScript(func); |
| // Break in builtin JavaScript code is not supported. |
| if (script.type == Debug.ScriptType.Native) { |
| throw %make_error(kDebugger, 'Cannot set break point in native code.'); |
| } |
| // If the script for the function has a name convert this to a script break |
| // point. |
| if (script && script.id) { |
| // Find line and column for the position in the script and set a script |
| // break point from that. |
| var location = script.locationFromPosition(source_position, false); |
| return this.setScriptBreakPointById(script.id, |
| location.line, location.column, |
| opt_condition); |
| } else { |
| // Set a break point directly on the function. |
| var break_point = MakeBreakPoint(source_position); |
| var actual_position = |
| %SetFunctionBreakPoint(func, source_position, break_point); |
| var actual_location = script.locationFromPosition(actual_position, true); |
| break_point.actual_location = { line: actual_location.line, |
| column: actual_location.column, |
| script_id: script.id }; |
| break_point.setCondition(opt_condition); |
| return break_point.number(); |
| } |
| }; |
| |
| |
| Debug.setBreakPointByScriptIdAndPosition = function(script_id, position, |
| condition, enabled) |
| { |
| var break_point = MakeBreakPoint(position); |
| break_point.setCondition(condition); |
| if (!enabled) { |
| break_point.disable(); |
| } |
| var script = scriptById(script_id); |
| if (script) { |
| break_point.actual_position = %SetScriptBreakPoint(script, position, break_point); |
| } |
| return break_point; |
| }; |
| |
| |
| Debug.enableBreakPoint = function(break_point_number) { |
| var break_point = this.findBreakPoint(break_point_number, false); |
| // Only enable if the breakpoint hasn't been deleted: |
| if (break_point) { |
| break_point.enable(); |
| } |
| }; |
| |
| |
| Debug.disableBreakPoint = function(break_point_number) { |
| var break_point = this.findBreakPoint(break_point_number, false); |
| // Only enable if the breakpoint hasn't been deleted: |
| if (break_point) { |
| break_point.disable(); |
| } |
| }; |
| |
| |
| Debug.changeBreakPointCondition = function(break_point_number, condition) { |
| var break_point = this.findBreakPoint(break_point_number, false); |
| break_point.setCondition(condition); |
| }; |
| |
| |
| Debug.clearBreakPoint = function(break_point_number) { |
| var break_point = this.findBreakPoint(break_point_number, true); |
| if (break_point) { |
| return %ClearBreakPoint(break_point); |
| } else { |
| break_point = this.findScriptBreakPoint(break_point_number, true); |
| if (!break_point) throw %make_error(kDebugger, 'Invalid breakpoint'); |
| } |
| }; |
| |
| |
| Debug.clearAllBreakPoints = function() { |
| for (var i = 0; i < break_points.length; i++) { |
| var break_point = break_points[i]; |
| %ClearBreakPoint(break_point); |
| } |
| break_points = []; |
| }; |
| |
| |
| Debug.disableAllBreakPoints = function() { |
| // Disable all user defined breakpoints: |
| for (var i = 1; i < next_break_point_number; i++) { |
| Debug.disableBreakPoint(i); |
| } |
| // Disable all exception breakpoints: |
| %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); |
| %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); |
| }; |
| |
| |
| Debug.findScriptBreakPoint = function(break_point_number, remove) { |
| var script_break_point; |
| for (var i = 0; i < script_break_points.length; i++) { |
| if (script_break_points[i].number() == break_point_number) { |
| script_break_point = script_break_points[i]; |
| // Remove the break point from the list if requested. |
| if (remove) { |
| script_break_point.clear(); |
| script_break_points.splice(i,1); |
| } |
| break; |
| } |
| } |
| return script_break_point; |
| }; |
| |
| |
| // Sets a breakpoint in a script identified through id or name at the |
| // specified source line and column within that line. |
| Debug.setScriptBreakPoint = function(type, script_id_or_name, |
| opt_line, opt_column, opt_condition, |
| opt_groupId) { |
| // Create script break point object. |
| var script_break_point = |
| new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, |
| opt_groupId); |
| |
| // Assign number to the new script break point and add it. |
| script_break_point.number_ = next_break_point_number++; |
| script_break_point.setCondition(opt_condition); |
| script_break_points.push(script_break_point); |
| |
| // Run through all scripts to see if this script break point matches any |
| // loaded scripts. |
| var scripts = this.scripts(); |
| for (var i = 0; i < scripts.length; i++) { |
| if (script_break_point.matchesScript(scripts[i])) { |
| script_break_point.set(scripts[i]); |
| } |
| } |
| |
| return script_break_point.number(); |
| }; |
| |
| |
| Debug.setScriptBreakPointById = function(script_id, |
| opt_line, opt_column, |
| opt_condition, opt_groupId) { |
| return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, |
| script_id, opt_line, opt_column, |
| opt_condition, opt_groupId); |
| }; |
| |
| |
| Debug.setScriptBreakPointByName = function(script_name, |
| opt_line, opt_column, |
| opt_condition, opt_groupId) { |
| return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName, |
| script_name, opt_line, opt_column, |
| opt_condition, opt_groupId); |
| }; |
| |
| |
| Debug.setScriptBreakPointByRegExp = function(script_regexp, |
| opt_line, opt_column, |
| opt_condition, opt_groupId) { |
| return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp, |
| script_regexp, opt_line, opt_column, |
| opt_condition, opt_groupId); |
| }; |
| |
| |
| Debug.enableScriptBreakPoint = function(break_point_number) { |
| var script_break_point = this.findScriptBreakPoint(break_point_number, false); |
| script_break_point.enable(); |
| }; |
| |
| |
| Debug.disableScriptBreakPoint = function(break_point_number) { |
| var script_break_point = this.findScriptBreakPoint(break_point_number, false); |
| script_break_point.disable(); |
| }; |
| |
| |
| Debug.changeScriptBreakPointCondition = function( |
| break_point_number, condition) { |
| var script_break_point = this.findScriptBreakPoint(break_point_number, false); |
| script_break_point.setCondition(condition); |
| }; |
| |
| |
| Debug.scriptBreakPoints = function() { |
| return script_break_points; |
| }; |
| |
| |
| Debug.clearStepping = function() { |
| %ClearStepping(); |
| }; |
| |
| Debug.setBreakOnException = function() { |
| return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true); |
| }; |
| |
| Debug.clearBreakOnException = function() { |
| return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); |
| }; |
| |
| Debug.isBreakOnException = function() { |
| return !!%IsBreakOnException(Debug.ExceptionBreak.Caught); |
| }; |
| |
| Debug.setBreakOnUncaughtException = function() { |
| return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true); |
| }; |
| |
| Debug.clearBreakOnUncaughtException = function() { |
| return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); |
| }; |
| |
| Debug.isBreakOnUncaughtException = function() { |
| return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught); |
| }; |
| |
| Debug.showBreakPoints = function(f, full) { |
| if (!IS_FUNCTION(f)) throw %make_error(kDebuggerType); |
| var source = full ? this.scriptSource(f) : this.source(f); |
| var offset = full ? 0 : this.sourcePosition(f); |
| var locations = %GetBreakLocations(f); |
| if (!locations) return source; |
| locations.sort(function(x, y) { return x - y; }); |
| var result = ""; |
| var prev_pos = 0; |
| var pos; |
| for (var i = 0; i < locations.length; i++) { |
| pos = locations[i] - offset; |
| result += source.slice(prev_pos, pos); |
| result += "[B" + i + "]"; |
| prev_pos = pos; |
| } |
| pos = source.length; |
| result += source.substring(prev_pos, pos); |
| return result; |
| }; |
| |
| |
| // Get all the scripts currently loaded. Locating all the scripts is based on |
| // scanning the heap. |
| Debug.scripts = function() { |
| // Collect all scripts in the heap. |
| return %DebugGetLoadedScripts(); |
| }; |
| |
| |
| // Get a specific script currently loaded. This is based on scanning the heap. |
| // TODO(clemensh): Create a runtime function for this. |
| function scriptById(scriptId) { |
| var scripts = Debug.scripts(); |
| for (var script of scripts) { |
| if (script.id == scriptId) return script; |
| } |
| return UNDEFINED; |
| }; |
| |
| |
| Debug.debuggerFlags = function() { |
| return debugger_flags; |
| }; |
| |
| Debug.MakeMirror = MakeMirror; |
| |
| function MakeExecutionState(break_id) { |
| return new ExecutionState(break_id); |
| } |
| |
| function ExecutionState(break_id) { |
| this.break_id = break_id; |
| this.selected_frame = 0; |
| } |
| |
| ExecutionState.prototype.prepareStep = function(action) { |
| if (action === Debug.StepAction.StepIn || |
| action === Debug.StepAction.StepOut || |
| action === Debug.StepAction.StepNext) { |
| return %PrepareStep(this.break_id, action); |
| } |
| throw %make_type_error(kDebuggerType); |
| }; |
| |
| ExecutionState.prototype.evaluateGlobal = function(source) { |
| return MakeMirror(%DebugEvaluateGlobal(this.break_id, source)); |
| }; |
| |
| ExecutionState.prototype.frameCount = function() { |
| return %GetFrameCount(this.break_id); |
| }; |
| |
| ExecutionState.prototype.frame = function(opt_index) { |
| // If no index supplied return the selected frame. |
| if (opt_index == null) opt_index = this.selected_frame; |
| if (opt_index < 0 || opt_index >= this.frameCount()) { |
| throw %make_type_error(kDebuggerFrame); |
| } |
| return new FrameMirror(this.break_id, opt_index); |
| }; |
| |
| ExecutionState.prototype.setSelectedFrame = function(index) { |
| var i = TO_NUMBER(index); |
| if (i < 0 || i >= this.frameCount()) { |
| throw %make_type_error(kDebuggerFrame); |
| } |
| this.selected_frame = i; |
| }; |
| |
| ExecutionState.prototype.selectedFrame = function() { |
| return this.selected_frame; |
| }; |
| |
| function MakeBreakEvent(break_id, break_points_hit) { |
| return new BreakEvent(break_id, break_points_hit); |
| } |
| |
| |
| function BreakEvent(break_id, break_points_hit) { |
| this.frame_ = new FrameMirror(break_id, 0); |
| this.break_points_hit_ = break_points_hit; |
| } |
| |
| |
| BreakEvent.prototype.eventType = function() { |
| return Debug.DebugEvent.Break; |
| }; |
| |
| |
| BreakEvent.prototype.func = function() { |
| return this.frame_.func(); |
| }; |
| |
| |
| BreakEvent.prototype.sourceLine = function() { |
| return this.frame_.sourceLine(); |
| }; |
| |
| |
| BreakEvent.prototype.sourceColumn = function() { |
| return this.frame_.sourceColumn(); |
| }; |
| |
| |
| BreakEvent.prototype.sourceLineText = function() { |
| return this.frame_.sourceLineText(); |
| }; |
| |
| |
| BreakEvent.prototype.breakPointsHit = function() { |
| return this.break_points_hit_; |
| }; |
| |
| |
| function MakeExceptionEvent(break_id, exception, uncaught, promise) { |
| return new ExceptionEvent(break_id, exception, uncaught, promise); |
| } |
| |
| |
| function ExceptionEvent(break_id, exception, uncaught, promise) { |
| this.exec_state_ = new ExecutionState(break_id); |
| this.exception_ = exception; |
| this.uncaught_ = uncaught; |
| this.promise_ = promise; |
| } |
| |
| |
| ExceptionEvent.prototype.eventType = function() { |
| return Debug.DebugEvent.Exception; |
| }; |
| |
| |
| ExceptionEvent.prototype.exception = function() { |
| return this.exception_; |
| }; |
| |
| |
| ExceptionEvent.prototype.uncaught = function() { |
| return this.uncaught_; |
| }; |
| |
| |
| ExceptionEvent.prototype.promise = function() { |
| return this.promise_; |
| }; |
| |
| |
| ExceptionEvent.prototype.func = function() { |
| return this.exec_state_.frame(0).func(); |
| }; |
| |
| |
| ExceptionEvent.prototype.sourceLine = function() { |
| return this.exec_state_.frame(0).sourceLine(); |
| }; |
| |
| |
| ExceptionEvent.prototype.sourceColumn = function() { |
| return this.exec_state_.frame(0).sourceColumn(); |
| }; |
| |
| |
| ExceptionEvent.prototype.sourceLineText = function() { |
| return this.exec_state_.frame(0).sourceLineText(); |
| }; |
| |
| |
| function MakeCompileEvent(script, type) { |
| return new CompileEvent(script, type); |
| } |
| |
| |
| function CompileEvent(script, type) { |
| this.script_ = MakeMirror(script); |
| this.type_ = type; |
| } |
| |
| |
| CompileEvent.prototype.eventType = function() { |
| return this.type_; |
| }; |
| |
| |
| CompileEvent.prototype.script = function() { |
| return this.script_; |
| }; |
| |
| |
| function MakeScriptObject_(script, include_source) { |
| var o = { id: script.id(), |
| name: script.name(), |
| lineOffset: script.lineOffset(), |
| columnOffset: script.columnOffset(), |
| lineCount: script.lineCount(), |
| }; |
| if (!IS_UNDEFINED(script.data())) { |
| o.data = script.data(); |
| } |
| if (include_source) { |
| o.source = script.source(); |
| } |
| return o; |
| } |
| |
| |
| function MakeAsyncTaskEvent(type, id) { |
| return new AsyncTaskEvent(type, id); |
| } |
| |
| |
| function AsyncTaskEvent(type, id) { |
| this.type_ = type; |
| this.id_ = id; |
| } |
| |
| |
| AsyncTaskEvent.prototype.type = function() { |
| return this.type_; |
| } |
| |
| |
| AsyncTaskEvent.prototype.id = function() { |
| return this.id_; |
| } |
| |
| // ------------------------------------------------------------------- |
| // Exports |
| |
| utils.InstallConstants(global, [ |
| "Debug", Debug, |
| "BreakEvent", BreakEvent, |
| "CompileEvent", CompileEvent, |
| "BreakPoint", BreakPoint, |
| ]); |
| |
| // Functions needed by the debugger runtime. |
| utils.InstallConstants(utils, [ |
| "MakeExecutionState", MakeExecutionState, |
| "MakeExceptionEvent", MakeExceptionEvent, |
| "MakeBreakEvent", MakeBreakEvent, |
| "MakeCompileEvent", MakeCompileEvent, |
| "MakeAsyncTaskEvent", MakeAsyncTaskEvent, |
| "IsBreakPointTriggered", IsBreakPointTriggered, |
| ]); |
| |
| }) |