// 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; //---------------------------------------------------------------------------- var Debug = {}; // 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 }; function ScriptTypeFlag(type) { return (1 << type); } // Globals. var debugger_flags = { 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(); } } }, }; // 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.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); }; // 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 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, "CompileEvent", CompileEvent, ]); // Functions needed by the debugger runtime. utils.InstallConstants(utils, [ "MakeExecutionState", MakeExecutionState, "MakeExceptionEvent", MakeExceptionEvent, "MakeCompileEvent", MakeCompileEvent, "MakeAsyncTaskEvent", MakeAsyncTaskEvent, ]); })