diff options
Diffstat (limited to 'chrome/browser/resources')
-rw-r--r-- | chrome/browser/resources/debugger_shell.js | 1246 |
1 files changed, 0 insertions, 1246 deletions
diff --git a/chrome/browser/resources/debugger_shell.js b/chrome/browser/resources/debugger_shell.js deleted file mode 100644 index 4685896..0000000 --- a/chrome/browser/resources/debugger_shell.js +++ /dev/null @@ -1,1246 +0,0 @@ -// Copyright 2008 Google Inc. -// All Rights Reserved. - -// TODO(erikkay): look into how this can be split up into multiple files -// It's currently loaded explicitly by Chrome, so maybe I need an "include" -// or "source" builtin to allow a core source file to reference multiple -// sub-files. - -/** - * @fileoverview Shell objects and global helper functions for Chrome - * automation shell / debugger. This file is loaded into the global namespace - * of the interactive shell, so users can simply call global functions - * directly. - * @author erikkay@google.com (Erik Kay) - */ - -/** - * Sequence number of the DebugCommand. - */ -DebugCommand.next_seq_ = 0; - -/** - * Command messages to be sent to the debugger. - * @constructor - */ -function DebugCommand(str) { - this.command = undefined; - // first, strip off of the leading word as the command - var argv = str.split(' '); - this.user_command = argv.shift(); - // the rest of the string is argv to the command - str = argv.join(' '); - if (DebugCommand.aliases[this.user_command]) - this.user_command = DebugCommand.aliases[this.user_command]; - if (this.parseArgs_(str) == 1) - this.type = "request"; - if (this.command == undefined) - this.command = this.user_command; -}; - -// Mapping of some control characters to avoid the \uXXXX syntax for most -// commonly used control cahracters. -const ctrlCharMap_ = { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' -}; - -// Regular expression matching ", \ and control characters (0x00 - 0x1F) -// globally. -const ctrlCharMatch_ = /["\\\\\x00-\x1F]/g; - -/** - * Convert a String to its JSON representation. - * @param {String} value - String to be converted - * @return {String} JSON formatted String - */ -DebugCommand.stringToJSON = function(value) { - // Check for" , \ and control characters (0x00 - 0x1F). - if (ctrlCharMatch_.test(value)) { - // Replace ", \ and control characters (0x00 - 0x1F). - return '"' + value.replace(ctrlCharMatch_, function (char) { - // Use charmap if possible. - var mapped = ctrlCharMap_[char]; - if (mapped) return mapped; - mapped = char.charCodeAt(); - // Convert control character to unicode escape sequence. - var dig1 = (Math.floor(mapped / 16)); - var dig2 = (mapped % 16) - return '\\u00' + dig1.toString(16) + dig2.toString(16); - }) - + '"'; - } - - // Simple string with no special characters. - return '"' + value + '"'; -}; - -/** - * @return {bool} True if x is an integer. - */ -DebugCommand.isInt = function(x) { - var y = parseInt(x); - if (isNaN(y)) - return false; - return x == y && x.toString() == y.toString(); -}; - -/** - * @return {float} log base 10 of num - */ -DebugCommand.log10 = function(num) { - return Math.log(num)/Math.log(10); -}; - -/** - * Take an object and encode it (non-recursively) as a JSON dict. - * @param {Object} obj - object to encode - */ -DebugCommand.toJSON = function(obj) { - var json = '{'; - for (var key in obj) { - var val = obj[key]; - if (!DebugCommand.isInt(val)) { - val = DebugCommand.stringToJSON(val.toString()); - } - json += '"' + key + '":' + val + ','; - } - json += '}'; - return json; -}; - -/** - * Encode the DebugCommand object into the V8 debugger JSON protocol format. - * @see http://wiki/Main/V8Debugger - */ -DebugCommand.prototype.toJSONProtocol = function() { - var json = '{'; - json += '"seq":"' + this.seq; - json += '","type":"' + this.type; - json += '","command":"' + this.command + '"'; - if (this.arguments) { - json += ',"arguments":' + DebugCommand.toJSON(this.arguments); - } - json += '}' - return json; -} - -/** - * Encode the contents of this message and send it to the debugger. - * @param {Object} tab - tab being debugged. This is an internal - * Chrome object. - */ -DebugCommand.prototype.sendToDebugger = function(tab) { - this.seq = DebugCommand.next_seq_++; - str = this.toJSONProtocol(); - dprint("sending: " + str); - tab.sendToDebugger(str); -}; - -DebugCommand.trim = function(str) { - return str.replace(/^\s*/, '').replace(/\s*$/, ''); -}; - -/** - * Strip off a trailing parameter after a ':'. As the identifier for the - * source can contain ':' characters (e.g. 'http://www....) something after - * a ':' is only considered a parameter if it is numeric. - * @return {Array} two element array, the trimmed string and the parameter, - * or -1 if no parameter - */ -DebugCommand.stripTrailingParameter = function(str, opt_separator) { - var sep = opt_separator || ':'; - var index = str.lastIndexOf(sep); - // If a separator character if found strip if numeric. - if (index != -1) { - var value = parseInt(str.substring(index + 1, str.length), 10); - if (isNaN(value) || value < 0) { - return [str, -1]; - } - str = str.substring(0, index); - return [str, value]; - } - return [str, -1]; -}; - -/** - * Format source and location strings based on source location input data. - * @param {Object} script - script information object - * @param {String} source - source code for the current location - * @param {int} line - line number (0-based) - * @param {String} func - function name - * @return {array} [location(string), source line(string), line number(int)] - */ -DebugCommand.getSourceLocation = function(script, source, line, func) { - // source line is 0-based, we present as 1-based - line++; - - // TODO(erikkay): take column into account as well - if (source) - source = "" + line + ": " + source; - var location; - if (func) { - location = func + ", "; - } - location += script ? script.name : '[no source]'; - return [location, source, line]; -}; - -/** - * Aliases for debugger commands. - */ -DebugCommand.aliases = { - 'b': 'break', - 'bi': 'break_info', - 'br': 'break', - 'bt': 'backtrace', - 'c': 'continue', - 'f': 'frame', - 'h': 'help', - '?': 'help', - 'ls': 'source', - 'n': 'next', - 'p': 'print', - 's': 'step', - 'so': 'stepout', -}; - -/** - * Parses arguments to simple commands which have no arguments. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.parseSimpleCommand_ = function(str) { - return str.length ? -1 : 1; -}; - -/** - * Parses arguments to "args" and "locals" command, and initializes - * the underlying DebugCommand (which is a frame request). - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseArgsAndLocals_ = function(str) { - this.command = "frame"; - return str.length ? -1 : 1; -}; - -/** - * Parses arguments to "break_info" command, and executes it. - * "break_info" has an optional argument, which is the breakpoint - * identifier. - * @see DebugCommand.commands - * @param {string} str - The arguments to be parsed. - * @return -1 for usage error, 0 for success - */ -DebugCommand.prototype.parseBreakInfo_ = function(str) { - this.type = "shell"; - - // Array of breakpoints to be printed by this command - // (default to all breakpoints) - var breakpointsToPrint = shell_.breakpoints; - - if (str.length > 0) { - // User specified an invalid breakpoint (not a number) - if (!str.match(/^\s*\d+\s*$/)) - return -1; // invalid usage - - // Check that the specified breakpoint identifier exists - var id = parseInt(str); - var info = shell_.breakpoints[id]; - if (!info) { - print("Error: Invalid breakpoint"); - return 0; // success (of sorts) - } - breakpointsToPrint = [info]; - } else { - // breakpointsToPrint.length isn't accurate, because of - // deletions - var num_breakpoints = 0; - for (var i in breakpointsToPrint) num_breakpoints++; - - print("Num breakpoints: " + num_breakpoints); - } - - DebugShell.printBreakpoints_(breakpointsToPrint); - - return 0; // success -} - -/** - * Parses arguments to "step" command. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseStep_ = function(str, opt_stepaction) { - this.command = "continue"; - action = opt_stepaction || "in"; - this.arguments = {"stepaction" : action} - if (str.length) { - count = parseInt(str); - if (count > 0) { - this.arguments["stepcount"] = count; - } else { - return -1; - } - } - return 1; -}; - -/** - * Parses arguments to "step" command. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseStepOut_ = function(str) { - return this.parseStep_(str, "out"); -}; - -/** - * Parses arguments to "next" command. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseNext_ = function(str) { - return this.parseStep_(str, "next"); -}; - -/** - * Parse the arguments to "print" command. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return 1 - always succeeds - */ -DebugCommand.prototype.parsePrint_ = function(str) { - this.command = "evaluate"; - this.arguments = { "expression" : str }; - // If the page is in the running state, then we force the expression to - // evaluate in the global context to avoid evaluating in a random context. - if (shell_.running) - this.arguments["global"] = true; - return 1; -}; - -/** - * Handle the response to a "print" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object - */ -DebugCommand.responsePrint_ = function(msg) { - body = msg["body"]; - if (body['text'] != undefined) { - print(body['text']); - } else { - // TODO(erikkay): is "text" ever not set? - print("can't print response"); - } -}; - -/** - * Parses arguments to "break" command. See DebugCommand.commands below - * for syntax details. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success, 0 for handled internally - */ -DebugCommand.prototype.parseBreak_ = function(str) { - function stripTrailingParameter() { - var ret = DebugCommand.stripTrailingParameter(str, ':'); - str = ret[0]; - return ret[1]; - } - - if (str.length == 0) { - this.type = "shell"; - return 0; - } else { - var parts = str.split(/\s+/); - var condition = null; - if (parts.length > 1) { - str = parts.shift(); - condition = parts.join(" "); - } - - this.command = "setbreakpoint"; - - // Locate ...[:line[:column]] if present. - var line = -1; - var column = -1; - line = stripTrailingParameter(); - if (line != -1) { - line -= 1; - var l = stripTrailingParameter(); - if (l != -1) { - column = line; - line = l - 1; - } - } - - if (line == -1 && column == -1) { - this.arguments = { 'type' : 'function', - 'target' : str }; - } else { - var script = shell_.matchScript(str, line); - if (script) { - this.arguments = { 'type' : 'script', - 'target' : script.name }; - } else { - this.arguments = { 'type' : 'function', - 'target' : str }; - } - this.arguments.line = line; - if (column != -1) - this.arguments.position = column; - } - if (condition) - this.arguments.condition = condition; - } - return 1; -}; - -/** - * Handle the response to a "break" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object - */ -DebugCommand.responseBreak_ = function(msg) { - var info = new BreakpointInfo( - parseInt(msg.body.breakpoint), - msg.command.arguments.type, - msg.command.arguments.target, - msg.command.arguments.line, - msg.command.arguments.position, - msg.command.arguments.condition); - shell_.addedBreakpoint(info); -}; - -/** - * Parses arguments to "backtrace" command. See DebugCommand.commands below - * for syntax details. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ - DebugCommand.prototype.parseBacktrace_ = function(str) { - if (str.length > 0) { - var parts = str.split(/\s+/); - var non_empty_parts = parts.filter(function(s) { return s.length > 0; }); - // We need exactly two arguments. - if (non_empty_parts.length != 2) { - return -1; - } - var from = parseInt(non_empty_parts[0], 10); - var to = parseInt(non_empty_parts[1], 10); - // The two arguments have to be integers. - if (from != non_empty_parts[0] || to != non_empty_parts[1]) { - return -1; - } - this.arguments = { 'fromFrame': from, 'toFrame': to + 1 }; - } else { - // Default to fetching the first 10 frames. - this.arguments = { 'fromFrame': 0, 'toFrame': 10 }; - } - return 1; -}; - -/** - * Handle the response to a "backtrace" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object - */ -DebugCommand.responseBacktrace_ = function(msg) { - body = msg["body"]; - if (body && body.totalFrames) { - print('Frames #' + body.fromFrame + ' to #' + (body.toFrame - 1) + - ' of ' + body.totalFrames + ":"); - for (var i = 0; i < body.frames.length; i++) { - print(body.frames[i].text); - } - } else { - print("unimplemented (sorry)"); - } -}; - - -/** - * Parses arguments to "clear" command. See DebugCommand.commands below - * for syntax details. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseClearCommand_ = function(str) { - this.command = "clearbreakpoint"; - if (str.length > 0) { - var i = parseInt(str, 10); - if (i != str) { - return -1; - } - this.arguments = { 'breakpoint': i }; - } - return 1; -} - -/** - * Handle the response to a "clear" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object - */ -DebugCommand.responseClear_ = function(msg) { - shell_.clearedBreakpoint(parseInt(msg.command.arguments.breakpoint)); -} - -/** - * Parses arguments to "frame" command. See DebugCommand.commands below - * for syntax details. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseFrame_ = function(str) { - if (str.length > 0) { - var i = parseInt(str, 10); - if (i != str) { - return -1; - } - this.arguments = { 'number': i }; - } - return 1; -}; - -/** - * Handle the response to a "frame" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object - */ -DebugCommand.responseFrame_ = function(msg) { - body = msg.body; - loc = DebugCommand.getSourceLocation(body.func.script, - body.sourceLineText, body.line, body.func.name); - print("#" + body.index + " " + loc[0]); - print(loc[1]); - shell_.current_frame = body.index; - shell_.current_line = loc[2]; - shell_.current_script = body.func.script; -}; - -/** - * Handle the response to a "args" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object (for "frame" command) - */ -DebugCommand.responseArgs_ = function(msg) { - DebugCommand.printVariables_(msg.body.arguments); -} - -/** - * Handle the response to a "locals" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object (for "frame" command) - */ -DebugCommand.responseLocals_ = function(msg) { - DebugCommand.printVariables_(msg.body.locals); -} - -DebugCommand.printVariables_ = function(variables) { - for (var i = 0; i < variables.length; i++) { - print(variables[i].name + " = " + - DebugCommand.toPreviewString_(variables[i].value)); - } -} - -DebugCommand.toPreviewString_ = function(value) { - // TODO(ericroman): pretty print arrays and objects, recursively. - // TODO(ericroman): truncate length of preview if too long? - if (value.type == "string") { - // Wrap the string in quote marks and JS-escape - return DebugCommand.stringToJSON(value.text); - } - return value.text; -} - -/** - * Parses arguments to "scripts" command. - * @see DebugCommand.commands - * @param {string} str - The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseScripts_ = function(str) { - return 1 -}; - -/** - * Handle the response to a "scripts" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object - */ -DebugCommand.responseScripts_ = function(msg) { - scripts = msg.body; - shell_.scripts = []; - for (var i in scripts) { - var script = scripts[i]; - - // Add this script to the internal list of scripts. - shell_.scripts.push(script); - - // Print result if this response was the result of a user command. - if (msg.command.from_user) { - var name = script.name; - if (name) { - if (script.lineOffset > 0) { - print(name + " (lines " + script.lineOffset + "-" + - (script.lineOffset + script.lineCount - 1) + ")"); - } else { - print(name + " (lines " + script.lineCount + ")"); - } - } else { - // For unnamed scripts (typically eval) display some source. - var sourceStart = script.sourceStart; - if (sourceStart.length > 40) - sourceStart = sourceStart.substring(0, 37) + '...'; - print("[unnamed] (source:\"" + sourceStart + "\")"); - } - } - } -}; - -/** - * Parses arguments to "source" command. - * @see DebugCommand.commands - * @param {string} str - The arguments to be parsed. - * @return -1 for usage error, 1 for success - */ -DebugCommand.prototype.parseSource_ = function(str) { - this.arguments = {}; - if (this.current_frame > 0) - this.arguments.frame = this.current_frame; - if (str.length) { - var args = str.split(" "); - if (args.length == 1) { - // with 1 argument n, we print 10 lines starting at n - var num = parseInt(args[0]); - if (num > 0) { - this.arguments.fromLine = num - 1; - this.arguments.toLine = this.arguments.fromLine + 10; - } else { - return -1; - } - } else if (args.length == 2) { - // with 2 arguments x and y, we print from line x to line x + y - var from = parseInt(args[0]); - var len = parseInt(args[1]); - if (from > 0 && len > 0) { - this.arguments.fromLine = from - 1; - this.arguments.toLine = this.arguments.fromLine + len; - } else { - return -1; - } - } else { - return -1; - } - if (this.arguments.fromLine < 0) - return -1; - if (this.arguments.toLine <= this.arguments.fromLine) - return -1; - } else if (shell_.current_line > 0) { - // with no arguments, we print 11 lines with the current line as the center - this.arguments.fromLine = - Math.max(0, shell_.current_line - 6); - this.arguments.toLine = this.arguments.fromLine + 11; - } - return 1; -}; - -/** - * Handle the response to a "source" command and display output to user. - * @see http://wiki/Main/V8Debugger - * @param {Object} msg - the V8 debugger response object - */ -DebugCommand.responseSource_ = function(msg) { - var body = msg.body; - var from_line = parseInt(body.fromLine) + 1; - var source = body.source; - var lines = source.split('\n'); - var maxdigits = 1 + Math.floor(DebugCommand.log10(from_line + lines.length)) - for (var num in lines) { - // there's an extra newline at the end - if (num >= (lines.length - 1) && lines[num].length == 0) - break; - spacer = maxdigits - (1 + Math.floor(DebugCommand.log10(from_line))) - var line = ""; - if (from_line == shell_.current_line) { - for (var i = 0; i < (maxdigits + 2); i++) - line += ">"; - } else { - for (var i = 0; i < spacer; i++) - line += " "; - line += from_line + ": "; - } - line += lines[num]; - print(line); - from_line++; - } -}; - -/** - * Parses arguments to "help" command. See DebugCommand.commands below - * for syntax details. - * @see DebugCommand.commands - * @param {string} str The arguments to be parsed. - * @return 0 for handled internally - */ -DebugCommand.parseHelp_ = function(str) { - DebugCommand.help(str); - return 0; -}; - -/** - * Takes argument and evaluates it in the context of the shell to allow commands - * to be escaped to the outer shell. Used primarily for development purposes. - * @see DebugCommand.commands - * @param {string} str The expression to be evaluated - * @return 0 for handled internally - */ -DebugCommand.parseShell_ = function(str) { - print(eval(str)); - return 0; -} - -DebugCommand.parseShellDebug_ = function(str) { - shell_.debug = !shell_.debug; - if (shell_.debug) { - print("shell debugging enabled"); - } else { - print("shell debugging disabled"); - } - return 0; -} - -/** - * Parses a user-entered command string. - * @param {string} str The arguments to be parsed. - */ -DebugCommand.prototype.parseArgs_ = function(str) { - if (str.length) - str = DebugCommand.trim(str); - var cmd = DebugCommand.commands[this.user_command]; - if (cmd) { - var parse = cmd['parse']; - if (parse == undefined) { - print('>>>can\'t find parse func for ' + this.user_command); - this.type = "error"; - } else { - var ret = parse.call(this, str); - if (ret > 0) { - this.type = "request"; - } else if (ret < 0) { - this.type = "handled"; - DebugCommand.help(this.user_command); - } - } - } else { - this.type = "handled"; - print('unknown command: ' + this.user_command); - DebugCommand.help(); - } -}; - -/** - * Displays command help or all help. - * @param {string} opt_str Which command to print help for. - */ -DebugCommand.help = function(opt_str) { - if (opt_str) { - var cmd = DebugCommand.commands[opt_str]; - var usage = cmd.usage; - print('usage: ' + usage); - // Print additional details for the command. - if (cmd.help) { - print(cmd.help); - } - } else { - if (shell_.running) { - print('Status: page is running'); - } else { - print('Status: page is paused'); - } - print('Available commands:'); - for (var key in DebugCommand.commands) { - var cmd = DebugCommand.commands[key]; - if (!cmd['hidden'] && (!shell_.running || cmd['while_running'])) { - var usage = cmd.usage; - print(' ' + usage); - } - } - } -}; - -/** - * Valid commands, their argument parser and their associated usage text. - */ -DebugCommand.commands = { - 'args': { 'parse': DebugCommand.prototype.parseArgsAndLocals_, - 'usage': 'args', - 'help': 'summarize the arguments to the current function.', - 'response': DebugCommand.responseArgs_ }, - 'break': { 'parse': DebugCommand.prototype.parseBreak_, - 'response': DebugCommand.responseBreak_, - 'usage': 'break [location] <condition>', - 'help': 'location is one of <function> | <script:function> | <script:line> | <script:line:pos>', - 'while_running': true }, - 'break_info': { 'parse': DebugCommand.prototype.parseBreakInfo_, - 'usage': 'break_info [breakpoint #]', - 'help': 'list the current breakpoints, or the details on a single one', - 'while_running': true }, - 'backtrace': { 'parse': DebugCommand.prototype.parseBacktrace_, - 'response': DebugCommand.responseBacktrace_, - 'usage': 'backtrace [from frame #] [to frame #]' }, - 'clear': { 'parse': DebugCommand.prototype.parseClearCommand_, - 'response': DebugCommand.responseClear_, - 'usage': 'clear <breakpoint #>', - 'while_running': true }, - 'continue': { 'parse': DebugCommand.parseSimpleCommand_, - 'usage': 'continue' }, - 'frame': { 'parse': DebugCommand.prototype.parseFrame_, - 'response': DebugCommand.responseFrame_, - 'usage': 'frame <frame #>' }, - 'help': { 'parse': DebugCommand.parseHelp_, - 'usage': 'help [command]', - 'while_running': true }, - 'locals': { 'parse': DebugCommand.prototype.parseArgsAndLocals_, - 'usage': 'locals', - 'help': 'summarize the local variables for current frame', - 'response': DebugCommand.responseLocals_ }, - 'next': { 'parse': DebugCommand.prototype.parseNext_, - 'usage': 'next' } , - 'print': { 'parse': DebugCommand.prototype.parsePrint_, - 'response': DebugCommand.responsePrint_, - 'usage': 'print <expression>', - 'while_running': true }, - 'scripts': { 'parse': DebugCommand.prototype.parseScripts_, - 'response': DebugCommand.responseScripts_, - 'usage': 'scripts', - 'while_running': true }, - 'source': { 'parse': DebugCommand.prototype.parseSource_, - 'response': DebugCommand.responseSource_, - 'usage': 'source [from line] | [<from line> <num lines>]' }, - 'step': { 'parse': DebugCommand.prototype.parseStep_, - 'usage': 'step' }, - 'stepout': { 'parse': DebugCommand.prototype.parseStepOut_, - 'usage': 'stepout' }, - // local eval for debugging - remove this later - 'shell': { 'parse': DebugCommand.parseShell_, - 'usage': 'shell <expression>', - 'while_running': true, - 'hidden': true }, - 'shelldebug': { 'parse': DebugCommand.parseShellDebug_, - 'usage': 'shelldebug', - 'while_running': true, - 'hidden': true }, -}; - - -/** - * Debug shell using the new JSON protocol - * @param {Object} tab - which tab is to be debugged. This is an internal - * Chrome object. - * @constructor - */ -function DebugShell(tab) { - this.tab = tab; - this.tab.attach(); - this.ready = true; - this.running = true; - this.current_command = undefined; - this.pending_commands = []; - this.debug = false; - this.last_msg = undefined; - this.last_command = undefined; - this.current_line = -1; - this.current_pos = -1; - this.current_frame = 0; - this.current_script = undefined; - this.scripts = []; - - // Mapping of breakpoints id --> info. - // Must use numeric keys. - this.breakpoints = []; -}; - -DebugShell.prototype.set_ready = function(ready) { - if (ready != this.ready) { - this.ready = ready; - chrome.setDebuggerReady(this.ready); - } -}; - -DebugShell.prototype.set_running = function(running) { - if (running != this.running) { - this.running = running; - chrome.setDebuggerBreak(!this.running); - } -}; - -/** - * Execute a constructed DebugCommand object if possible, otherwise pend. - * @param cmd {DebugCommand} - command to execute - */ -DebugShell.prototype.process_command = function(cmd) { - if (this.current_command) { - this.pending_commands.push(cmd); - dprint("pending command: " + DebugCommand.toJSON(cmd)); - } else if (cmd.type == "shell") { - if (cmd.user_command == "break") { - if (this.running) { - this.tab.debugBreak(); - this.set_ready(false); - } else { - print(">>already paused"); - } - } - this.last_command = cmd; - } else if (cmd.type == "request") { - // If the page is running, then the debugger isn't listening to certain - // requests. - var cmd_info = DebugCommand.commands[cmd.user_command]; - if (this.running && !cmd_info['while_running']) { - print(cmd.user_command + " can only be run while paused"); - } else { - this.current_command = cmd; - cmd.sendToDebugger(this.tab); - this.set_ready(false); - } - } - this.last_command = cmd; -}; - -/** - * Handle a break event from the debugger. - * @param msg {Object} - event protocol message to handle - */ -DebugShell.prototype.event_break = function(msg) { - this.current_frame = 0; - this.set_running(false); - this.set_ready(true); - if (msg.body) { - var body = msg.body; - this.current_script = body.script; - var loc = DebugCommand.getSourceLocation(body.script, - body.sourceLineText, body.sourceLine, body.invocationText); - var location = loc[0]; - var source = loc[1]; - this.current_line = loc[2]; - if (msg.body.breakpoints) { - var breakpoints = msg.body.breakpoints; - print("paused at breakpoint " + breakpoints.join(",") + ": " + - location); - for (var i = 0; i < breakpoints.length; i++) - this.didHitBreakpoint(parseInt(breakpoints[i])); - } else if (body.scriptData == "") { - print("paused"); - } else { - // step, stepout, next, "break" and a "debugger" line in the code - // are all treated the same (they're not really distinguishable anyway) - if (location != this.last_break_location) { - // We only print the location (function + script) when it changes, - // so as we step, you only see the source line when you transition - // to a new script and/or function. - print(location); - } - } - if (source) - print(source); - this.last_break_location = location; - } -}; - -/** - * Handle an exception event from the debugger. - * @param msg {Object} - event protocol message to handle - */ -DebugShell.prototype.event_exception = function(msg) { - this.set_running(false); - this.set_ready(true); - if (msg.body) { - if (msg.body["uncaught"]) { - print("uncaught exception " + msg.body["exception"].text); - } else { - print("paused at exception " + msg.body["exception"].text); - } - } -}; - -DebugShell.prototype.matchScript = function(script_match, line) { - var script = null; - // In the v8 debugger, all scripts have a name, line offset and line count - // Script names are usually URLs which are a pain to have to type again and - // again, so we match the tail end of the script name. This makes it easy - // to type break foo.js:23 rather than - // http://www.foo.com/bar/baz/quux/test/foo.js:23. In addition to the tail - // of the name we also look at the lines the script cover. If there are - // several scripts with the same tail including the requested line we match - // the first one encountered. - // TODO(sgjesse) Find how to handle several matching scripts. - var candidate_scripts = []; - for (var i in this.scripts) { - if (this.scripts[i].name && - this.scripts[i].name.indexOf(script_match) >= 0) { - candidate_scripts.push(this.scripts[i]); - } - } - for (var i in candidate_scripts) { - var s = candidate_scripts[i]; - var from = s.lineOffset; - var to = from + s.lineCount; - if (from <= line && line < to) { - script = s; - break; - } - } - if (script) - return script; - else - return null; -} - -// The Chrome Subshell interface requires: -// prompt(), command(), response(), exit() and on_disconnect() - -/** - * Called by Chrome Shell to get a prompt string to display. - */ -DebugShell.prototype.prompt = function() { - if (this.current_command) - return ''; - if (!this.running) - return 'v8(paused)> '; - else - return 'v8(running)> '; -}; - -/** - * Called by Chrome Shell when command input has been received from the user. - */ -DebugShell.prototype.command = function(str) { - if (this.tab) { - str = DebugCommand.trim(str); - if (str.length) { - var cmd = new DebugCommand(str); - cmd.from_user = true; - this.process_command(cmd); - } - } else { - print(">>not connected to a tab"); - } -}; - -/** - * Called by Chrome Shell when a response to a previous command has been - * received. - */ -DebugShell.prototype.response = function(str) { - var msg; - try { - dprint("received: " + str); - msg = eval('(' + str + ')'); - this.last_msg = msg; - } catch (error) { - print(error.toString(), str); - return; - } - if (msg.type == "event") { - ev = msg["event"] - if (ev == "break") { - this.event_break(msg); - } else if (ev == "exception") { - this.event_exception(msg); - } else if (ev == "attach") { - var title = this.tab.title; - if (!title) - title = "Untitled"; - print('attached to ' + title); - // on attach, we update our current script list - var cmd = new DebugCommand("scripts"); - cmd.from_user = false; - this.process_command(cmd); - } - } else if (msg.type == "response") { - if (msg.request_seq != undefined) { - if (!this.current_command || this.current_command.seq != msg.request_seq){ - throw("received response to unknown command " + DebugCommand.toJSON(msg)); - } - } else { - // TODO(erikkay): should we reject these when they happen? - print(">>no request_seq in response " + DebugCommand.toJSON(msg)); - } - var cmd = DebugCommand.commands[this.current_command.user_command] - msg.command = this.current_command; - this.current_command = null - if (msg.running != undefined) { - this.set_running(msg.running); - } - if (!msg['success']) { - print(msg['message']); - } else { - var response = cmd['response']; - if (response != undefined) { - response.call(this, msg); - } - } - this.set_ready(true); - if (this.pending_commands.length) { - this.process_command(this.pending_commands.shift()); - } - } -}; - -/** - * Called when a breakpoint has been set. - * @param {BreakpointInfo} info - details of breakpoint set. - */ -DebugShell.prototype.addedBreakpoint = function(info) { - print("set breakpoint #" + info.id); - this.breakpoints[info.id] = info; -} - -/** - * Called when a breakpoint has been cleared. - * @param {int} id - the breakpoint number that was cleared. - */ -DebugShell.prototype.clearedBreakpoint = function(id) { - assertIsNumberType(id, "clearedBreakpoint called with invalid id"); - - print("cleared breakpoint #" + id); - delete this.breakpoints[id]; -} - -/** - * Called when a breakpoint has been reached. - * @param {int} id - the breakpoint number that was hit. - */ -DebugShell.prototype.didHitBreakpoint = function(id) { - assertIsNumberType(id, "didHitBreakpoint called with invalid id"); - - var info = this.breakpoints[id]; - if (!info) - throw "Could not find breakpoint #" + id; - - info.hit_count ++; -} - -/** - * Print a summary of the specified breakpoints. - * - * @param {Array<BreakpointInfo>} breakpointsToPrint - List of breakpoints. The - * index is unused (id is determined from the info). - */ -DebugShell.printBreakpoints_ = function(breakpoints) { - // TODO(ericroman): this would look much nicer if we could output as an HTML - // table. I tried outputting as formatted text table, but this looks aweful - // once it triggers wrapping (which is very likely if the target is a script) - - // Output as a comma separated list of key=value - for (var i in breakpoints) { - var b = breakpoints[i]; - var props = ["id", "hit_count", "type", "target", "line", "position", - "condition"]; - var propertyList = []; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - var val = b[prop]; - if (val != undefined) - propertyList.push(prop + "=" + val); - } - print(propertyList.join(", ")); - } -} - -/** - * Called by Chrome Shell when the outer shell is detaching from debugging - * this tab. - */ -DebugShell.prototype.exit = function() { - if (this.tab) { - this.tab.detach(); - this.tab = null; - } -}; - -/** - * Called by the Chrome Shell when the tab that the shell is debugging - * went away. - */ -DebugShell.prototype.on_disconnect = function() { - print(">>lost connection to tab"); - this.tab = null; -}; - - -/** - * Structure that holds the details about a breakpoint. - * @constructor - * - * @param {int} id - breakpoint number - * @param {string} type - "script" or "function" - * @param {string} target - either a function name, or script url - * @param {int} line - line number in the script, or undefined - * @param {int} position - column in the script, or undefined - * @param {string} condition - boolean expression, or undefined - */ -function BreakpointInfo(id, type, target, line, position, condition) { - this.id = id; - this.type = type; - this.target = target; - - if (line != undefined) - this.line = line; - if (position != undefined) - this.position = position; - if (condition != undefined) - this.condition = condition; - - this.hit_count = 0; - - // Check that the id is numeric, otherwise will run into problems later - assertIsNumberType(this.id, "id is not a number"); -} - -/** - * Global function to enter the debugger using DebugShell. - * User can access this in the external shell by simply typing "debug()". - * This is called by the Chrome Shell when the shell attaches to a tab. - * @param {Object} opt_tab - which tab is to be debugged. This is an internal - * Chrome object. - */ -function debug(opt_tab) { - shell(new DebugShell(opt_tab || chrome.browser[0].tab[0])); -}; - -/** - * Print debugging message when DebugShell's debug flag is true. - */ -function dprint(str) { - if (shell_ && shell_.debug) { - print(str); - } -}; - -/** - * Helper that throws error if x is not a number - * @param x {object} - object to test type of - * @param error_message {string} - error to throw on failure - */ -function assertIsNumberType(x, error_message) { - if (typeof x != "number") - throw error_message; -} |