diff options
Diffstat (limited to 'src/3rdparty/v8/src/d8.js')
-rw-r--r-- | src/3rdparty/v8/src/d8.js | 2798 |
1 files changed, 2798 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/d8.js b/src/3rdparty/v8/src/d8.js new file mode 100644 index 0000000..9798078 --- /dev/null +++ b/src/3rdparty/v8/src/d8.js @@ -0,0 +1,2798 @@ +// Copyright 2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +String.prototype.startsWith = function (str) { + if (str.length > this.length) + return false; + return this.substr(0, str.length) == str; +} + +function log10(num) { + return Math.log(num)/Math.log(10); +} + +function ToInspectableObject(obj) { + if (!obj && typeof obj === 'object') { + return void 0; + } else { + return Object(obj); + } +} + +function GetCompletions(global, last, full) { + var full_tokens = full.split(); + full = full_tokens.pop(); + var parts = full.split('.'); + parts.pop(); + var current = global; + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + var next = current[part]; + if (!next) + return []; + current = next; + } + var result = []; + current = ToInspectableObject(current); + while (typeof current !== 'undefined') { + var mirror = new $debug.ObjectMirror(current); + var properties = mirror.properties(); + for (var i = 0; i < properties.length; i++) { + var name = properties[i].name(); + if (typeof name === 'string' && name.startsWith(last)) + result.push(name); + } + current = ToInspectableObject(current.__proto__); + } + return result; +} + + +// Global object holding debugger related constants and state. +const Debug = {}; + + +// Debug events which can occour in the V8 JavaScript engine. These originate +// from the API include file v8-debug.h. +Debug.DebugEvent = { Break: 1, + Exception: 2, + NewFunction: 3, + BeforeCompile: 4, + AfterCompile: 5 }; + + +// The different types of scripts matching enum ScriptType in objects.h. +Debug.ScriptType = { Native: 0, + Extension: 1, + Normal: 2 }; + + +// The different types of script compilations matching enum +// Script::CompilationType in objects.h. +Debug.ScriptCompilationType = { Host: 0, + Eval: 1, + JSON: 2 }; + + +// The different types of scopes matching constants runtime.cc. +Debug.ScopeType = { Global: 0, + Local: 1, + With: 2, + Closure: 3, + Catch: 4 }; + + +// Current debug state. +const kNoFrame = -1; +Debug.State = { + currentFrame: kNoFrame, + displaySourceStartLine: -1, + displaySourceEndLine: -1, + currentSourceLine: -1 +} +var trace_compile = false; // Tracing all compile events? +var trace_debug_json = false; // Tracing all debug json packets? +var last_cmd_line = ''; +//var lol_is_enabled; // Set to true in d8.cc if LIVE_OBJECT_LIST is defined. +var lol_next_dump_index = 0; +const kDefaultLolLinesToPrintAtATime = 10; +const kMaxLolLinesToPrintAtATime = 1000; +var repeat_cmd_line = ''; +var is_running = true; + +// Copied from debug-delay.js. This is needed below: +function ScriptTypeFlag(type) { + return (1 << type); +} + + +// Process a debugger JSON message into a display text and a running status. +// This function returns an object with properties "text" and "running" holding +// this information. +function DebugMessageDetails(message) { + if (trace_debug_json) { + print("received: '" + message + "'"); + } + // Convert the JSON string to an object. + var response = new ProtocolPackage(message); + is_running = response.running(); + + if (response.type() == 'event') { + return DebugEventDetails(response); + } else { + return DebugResponseDetails(response); + } +} + +function DebugEventDetails(response) { + details = {text:'', running:false} + + // Get the running state. + details.running = response.running(); + + var body = response.body(); + var result = ''; + switch (response.event()) { + case 'break': + if (body.breakpoints) { + result += 'breakpoint'; + if (body.breakpoints.length > 1) { + result += 's'; + } + result += ' #'; + for (var i = 0; i < body.breakpoints.length; i++) { + if (i > 0) { + result += ', #'; + } + result += body.breakpoints[i]; + } + } else { + result += 'break'; + } + result += ' in '; + result += body.invocationText; + result += ', '; + result += SourceInfo(body); + result += '\n'; + result += SourceUnderline(body.sourceLineText, body.sourceColumn); + Debug.State.currentSourceLine = body.sourceLine; + Debug.State.displaySourceStartLine = -1; + Debug.State.displaySourceEndLine = -1; + Debug.State.currentFrame = 0; + details.text = result; + break; + + case 'exception': + if (body.uncaught) { + result += 'Uncaught: '; + } else { + result += 'Exception: '; + } + result += '"'; + result += body.exception.text; + result += '"'; + if (body.sourceLine >= 0) { + result += ', '; + result += SourceInfo(body); + result += '\n'; + result += SourceUnderline(body.sourceLineText, body.sourceColumn); + Debug.State.currentSourceLine = body.sourceLine; + Debug.State.displaySourceStartLine = -1; + Debug.State.displaySourceEndLine = -1; + Debug.State.currentFrame = 0; + } else { + result += ' (empty stack)'; + Debug.State.currentSourceLine = -1; + Debug.State.displaySourceStartLine = -1; + Debug.State.displaySourceEndLine = -1; + Debug.State.currentFrame = kNoFrame; + } + details.text = result; + break; + + case 'afterCompile': + if (trace_compile) { + result = 'Source ' + body.script.name + ' compiled:\n' + var source = body.script.source; + if (!(source[source.length - 1] == '\n')) { + result += source; + } else { + result += source.substring(0, source.length - 1); + } + } + details.text = result; + break; + + case 'scriptCollected': + details.text = result; + break; + + default: + details.text = 'Unknown debug event ' + response.event(); + } + + return details; +}; + + +function SourceInfo(body) { + var result = ''; + + if (body.script) { + if (body.script.name) { + result += body.script.name; + } else { + result += '[unnamed]'; + } + } + result += ' line '; + result += body.sourceLine + 1; + result += ' column '; + result += body.sourceColumn + 1; + + return result; +} + + +function SourceUnderline(source_text, position) { + if (!source_text) { + return; + } + + // Create an underline with a caret pointing to the source position. If the + // source contains a tab character the underline will have a tab character in + // the same place otherwise the underline will have a space character. + var underline = ''; + for (var i = 0; i < position; i++) { + if (source_text[i] == '\t') { + underline += '\t'; + } else { + underline += ' '; + } + } + underline += '^'; + + // Return the source line text with the underline beneath. + return source_text + '\n' + underline; +}; + + +// Converts a text command to a JSON request. +function DebugCommandToJSONRequest(cmd_line) { + var result = new DebugRequest(cmd_line).JSONRequest(); + if (trace_debug_json && result) { + print("sending: '" + result + "'"); + } + return result; +}; + + +function DebugRequest(cmd_line) { + // If the very first character is a { assume that a JSON request have been + // entered as a command. Converting that to a JSON request is trivial. + if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') { + this.request_ = cmd_line; + return; + } + + // Check for a simple carriage return to repeat the last command: + var is_repeating = false; + if (cmd_line == '\n') { + if (is_running) { + cmd_line = 'break'; // Not in debugger mode, break with a frame request. + } else { + cmd_line = repeat_cmd_line; // use command to repeat. + is_repeating = true; + } + } + if (!is_running) { // Only save the command if in debugger mode. + repeat_cmd_line = cmd_line; // save last command. + } + + // Trim string for leading and trailing whitespace. + cmd_line = cmd_line.replace(/^\s+|\s+$/g, ''); + + // Find the command. + var pos = cmd_line.indexOf(' '); + var cmd; + var args; + if (pos == -1) { + cmd = cmd_line; + args = ''; + } else { + cmd = cmd_line.slice(0, pos); + args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, ''); + } + + if ((cmd === undefined) || !cmd) { + this.request_ = void 0; + return; + } + + last_cmd = cmd; + + // Switch on command. + switch (cmd) { + case 'continue': + case 'c': + this.request_ = this.continueCommandToJSONRequest_(args); + break; + + case 'step': + case 's': + this.request_ = this.stepCommandToJSONRequest_(args, 'in'); + break; + + case 'stepi': + case 'si': + this.request_ = this.stepCommandToJSONRequest_(args, 'min'); + break; + + case 'next': + case 'n': + this.request_ = this.stepCommandToJSONRequest_(args, 'next'); + break; + + case 'finish': + case 'fin': + this.request_ = this.stepCommandToJSONRequest_(args, 'out'); + break; + + case 'backtrace': + case 'bt': + this.request_ = this.backtraceCommandToJSONRequest_(args); + break; + + case 'frame': + case 'f': + this.request_ = this.frameCommandToJSONRequest_(args); + break; + + case 'scopes': + this.request_ = this.scopesCommandToJSONRequest_(args); + break; + + case 'scope': + this.request_ = this.scopeCommandToJSONRequest_(args); + break; + + case 'disconnect': + case 'exit': + case 'quit': + this.request_ = this.disconnectCommandToJSONRequest_(args); + break; + + case 'up': + this.request_ = + this.frameCommandToJSONRequest_('' + + (Debug.State.currentFrame + 1)); + break; + + case 'down': + case 'do': + this.request_ = + this.frameCommandToJSONRequest_('' + + (Debug.State.currentFrame - 1)); + break; + + case 'set': + case 'print': + case 'p': + this.request_ = this.printCommandToJSONRequest_(args); + break; + + case 'dir': + this.request_ = this.dirCommandToJSONRequest_(args); + break; + + case 'references': + this.request_ = this.referencesCommandToJSONRequest_(args); + break; + + case 'instances': + this.request_ = this.instancesCommandToJSONRequest_(args); + break; + + case 'list': + case 'l': + this.request_ = this.listCommandToJSONRequest_(args); + break; + case 'source': + this.request_ = this.sourceCommandToJSONRequest_(args); + break; + + case 'scripts': + case 'script': + case 'scr': + this.request_ = this.scriptsCommandToJSONRequest_(args); + break; + + case 'break': + case 'b': + this.request_ = this.breakCommandToJSONRequest_(args); + break; + + case 'breakpoints': + case 'bb': + this.request_ = this.breakpointsCommandToJSONRequest_(args); + break; + + case 'clear': + case 'delete': + case 'd': + this.request_ = this.clearCommandToJSONRequest_(args); + break; + + case 'threads': + this.request_ = this.threadsCommandToJSONRequest_(args); + break; + + case 'cond': + this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond'); + break; + + case 'enable': + case 'en': + this.request_ = + this.changeBreakpointCommandToJSONRequest_(args, 'enable'); + break; + + case 'disable': + case 'dis': + this.request_ = + this.changeBreakpointCommandToJSONRequest_(args, 'disable'); + break; + + case 'ignore': + this.request_ = + this.changeBreakpointCommandToJSONRequest_(args, 'ignore'); + break; + + case 'info': + case 'inf': + this.request_ = this.infoCommandToJSONRequest_(args); + break; + + case 'flags': + this.request_ = this.v8FlagsToJSONRequest_(args); + break; + + case 'gc': + this.request_ = this.gcToJSONRequest_(args); + break; + + case 'trace': + case 'tr': + // Return undefined to indicate command handled internally (no JSON). + this.request_ = void 0; + this.traceCommand_(args); + break; + + case 'help': + case '?': + this.helpCommand_(args); + // Return undefined to indicate command handled internally (no JSON). + this.request_ = void 0; + break; + + case 'liveobjectlist': + case 'lol': + if (lol_is_enabled) { + this.request_ = this.lolToJSONRequest_(args, is_repeating); + break; + } + + default: + throw new Error('Unknown command "' + cmd + '"'); + } +} + +DebugRequest.prototype.JSONRequest = function() { + return this.request_; +} + + +function RequestPacket(command) { + this.seq = 0; + this.type = 'request'; + this.command = command; +} + + +RequestPacket.prototype.toJSONProtocol = function() { + // Encode the protocol header. + var json = '{'; + json += '"seq":' + this.seq; + json += ',"type":"' + this.type + '"'; + if (this.command) { + json += ',"command":' + StringToJSON_(this.command); + } + if (this.arguments) { + json += ',"arguments":'; + // Encode the arguments part. + if (this.arguments.toJSONProtocol) { + json += this.arguments.toJSONProtocol() + } else { + json += SimpleObjectToJSON_(this.arguments); + } + } + json += '}'; + return json; +} + + +DebugRequest.prototype.createRequest = function(command) { + return new RequestPacket(command); +}; + + +// Note: we use detected command repetition as a signal for continuation here. +DebugRequest.prototype.createLOLRequest = function(command, + start_index, + lines_to_dump, + is_continuation) { + if (is_continuation) { + start_index = lol_next_dump_index; + } + + if (lines_to_dump) { + lines_to_dump = parseInt(lines_to_dump); + } else { + lines_to_dump = kDefaultLolLinesToPrintAtATime; + } + if (lines_to_dump > kMaxLolLinesToPrintAtATime) { + lines_to_dump = kMaxLolLinesToPrintAtATime; + } + + // Save the next start_index to dump from: + lol_next_dump_index = start_index + lines_to_dump; + + var request = this.createRequest(command); + request.arguments = {}; + request.arguments.start = start_index; + request.arguments.count = lines_to_dump; + + return request; +}; + + +// Create a JSON request for the evaluation command. +DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) { + // Global varaible used to store whether a handle was requested. + lookup_handle = null; + + if (lol_is_enabled) { + // Check if the expression is a obj id in the form @<obj id>. + var obj_id_match = expression.match(/^@([0-9]+)$/); + if (obj_id_match) { + var obj_id = parseInt(obj_id_match[1]); + // Build a dump request. + var request = this.createRequest('getobj'); + request.arguments = {}; + request.arguments.obj_id = obj_id; + return request.toJSONProtocol(); + } + } + + // Check if the expression is a handle id in the form #<handle>#. + var handle_match = expression.match(/^#([0-9]*)#$/); + if (handle_match) { + // Remember the handle requested in a global variable. + lookup_handle = parseInt(handle_match[1]); + // Build a lookup request. + var request = this.createRequest('lookup'); + request.arguments = {}; + request.arguments.handles = [ lookup_handle ]; + return request.toJSONProtocol(); + } else { + // Build an evaluate request. + var request = this.createRequest('evaluate'); + request.arguments = {}; + request.arguments.expression = expression; + // Request a global evaluation if there is no current frame. + if (Debug.State.currentFrame == kNoFrame) { + request.arguments.global = true; + } + return request.toJSONProtocol(); + } +}; + + +// Create a JSON request for the references/instances command. +DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) { + // Build a references request. + var handle_match = handle.match(/^#([0-9]*)#$/); + if (handle_match) { + var request = this.createRequest('references'); + request.arguments = {}; + request.arguments.type = type; + request.arguments.handle = parseInt(handle_match[1]); + return request.toJSONProtocol(); + } else { + throw new Error('Invalid object id.'); + } +}; + + +// Create a JSON request for the continue command. +DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) { + var request = this.createRequest('continue'); + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the step command. +DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) { + // Requesting a step is through the continue command with additional + // arguments. + var request = this.createRequest('continue'); + request.arguments = {}; + + // Process arguments if any. + + // Only process args if the command is 'step' which is indicated by type being + // set to 'in'. For all other commands, ignore the args. + if (args && args.length > 0) { + args = args.split(/\s+/g); + + if (args.length > 2) { + throw new Error('Invalid step arguments.'); + } + + if (args.length > 0) { + // Check if we have a gdb stype step command. If so, the 1st arg would + // be the step count. If it's not a number, then assume that we're + // parsing for the legacy v8 step command. + var stepcount = Number(args[0]); + if (stepcount == Number.NaN) { + // No step count at arg 1. Process as legacy d8 step command: + if (args.length == 2) { + var stepcount = parseInt(args[1]); + if (isNaN(stepcount) || stepcount <= 0) { + throw new Error('Invalid step count argument "' + args[0] + '".'); + } + request.arguments.stepcount = stepcount; + } + + // Get the step action. + switch (args[0]) { + case 'in': + case 'i': + request.arguments.stepaction = 'in'; + break; + + case 'min': + case 'm': + request.arguments.stepaction = 'min'; + break; + + case 'next': + case 'n': + request.arguments.stepaction = 'next'; + break; + + case 'out': + case 'o': + request.arguments.stepaction = 'out'; + break; + + default: + throw new Error('Invalid step argument "' + args[0] + '".'); + } + + } else { + // gdb style step commands: + request.arguments.stepaction = type; + request.arguments.stepcount = stepcount; + } + } + } else { + // Default is step of the specified type. + request.arguments.stepaction = type; + } + + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the backtrace command. +DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) { + // Build a backtrace request from the text command. + var request = this.createRequest('backtrace'); + + // Default is to show top 10 frames. + request.arguments = {}; + request.arguments.fromFrame = 0; + request.arguments.toFrame = 10; + + args = args.split(/\s*[ ]+\s*/g); + if (args.length == 1 && args[0].length > 0) { + var frameCount = parseInt(args[0]); + if (frameCount > 0) { + // Show top frames. + request.arguments.fromFrame = 0; + request.arguments.toFrame = frameCount; + } else { + // Show bottom frames. + request.arguments.fromFrame = 0; + request.arguments.toFrame = -frameCount; + request.arguments.bottom = true; + } + } else if (args.length == 2) { + var fromFrame = parseInt(args[0]); + var toFrame = parseInt(args[1]); + if (isNaN(fromFrame) || fromFrame < 0) { + throw new Error('Invalid start frame argument "' + args[0] + '".'); + } + if (isNaN(toFrame) || toFrame < 0) { + throw new Error('Invalid end frame argument "' + args[1] + '".'); + } + if (fromFrame > toFrame) { + throw new Error('Invalid arguments start frame cannot be larger ' + + 'than end frame.'); + } + // Show frame range. + request.arguments.fromFrame = fromFrame; + request.arguments.toFrame = toFrame + 1; + } else if (args.length > 2) { + throw new Error('Invalid backtrace arguments.'); + } + + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the frame command. +DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) { + // Build a frame request from the text command. + var request = this.createRequest('frame'); + args = args.split(/\s*[ ]+\s*/g); + if (args.length > 0 && args[0].length > 0) { + request.arguments = {}; + request.arguments.number = args[0]; + } + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the scopes command. +DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) { + // Build a scopes request from the text command. + var request = this.createRequest('scopes'); + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the scope command. +DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) { + // Build a scope request from the text command. + var request = this.createRequest('scope'); + args = args.split(/\s*[ ]+\s*/g); + if (args.length > 0 && args[0].length > 0) { + request.arguments = {}; + request.arguments.number = args[0]; + } + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the print command. +DebugRequest.prototype.printCommandToJSONRequest_ = function(args) { + // Build an evaluate request from the text command. + if (args.length == 0) { + throw new Error('Missing expression.'); + } + return this.makeEvaluateJSONRequest_(args); +}; + + +// Create a JSON request for the dir command. +DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) { + // Build an evaluate request from the text command. + if (args.length == 0) { + throw new Error('Missing expression.'); + } + return this.makeEvaluateJSONRequest_(args); +}; + + +// Create a JSON request for the references command. +DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) { + // Build an evaluate request from the text command. + if (args.length == 0) { + throw new Error('Missing object id.'); + } + + return this.makeReferencesJSONRequest_(args, 'referencedBy'); +}; + + +// Create a JSON request for the instances command. +DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) { + // Build an evaluate request from the text command. + if (args.length == 0) { + throw new Error('Missing object id.'); + } + + // Build a references request. + return this.makeReferencesJSONRequest_(args, 'constructedBy'); +}; + + +// Create a JSON request for the list command. +DebugRequest.prototype.listCommandToJSONRequest_ = function(args) { + + // Default is ten lines starting five lines before the current location. + if (Debug.State.displaySourceEndLine == -1) { + // If we list forwards, we will start listing after the last source end + // line. Set it to start from 5 lines before the current location. + Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5; + // If we list backwards, we will start listing backwards from the last + // source start line. Set it to start from 1 lines before the current + // location. + Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1; + } + + var from = Debug.State.displaySourceEndLine + 1; + var lines = 10; + + // Parse the arguments. + args = args.split(/\s*,\s*/g); + if (args == '') { + } else if ((args.length == 1) && (args[0] == '-')) { + from = Debug.State.displaySourceStartLine - lines; + } else if (args.length == 2) { + from = parseInt(args[0]); + lines = parseInt(args[1]) - from + 1; // inclusive of the ending line. + } else { + throw new Error('Invalid list arguments.'); + } + Debug.State.displaySourceStartLine = from; + Debug.State.displaySourceEndLine = from + lines - 1; + var sourceArgs = '' + from + ' ' + lines; + return this.sourceCommandToJSONRequest_(sourceArgs); +}; + + +// Create a JSON request for the source command. +DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) { + // Build a evaluate request from the text command. + var request = this.createRequest('source'); + + // Default is ten lines starting five lines before the current location. + var from = Debug.State.currentSourceLine - 5; + var lines = 10; + + // Parse the arguments. + args = args.split(/\s*[ ]+\s*/g); + if (args.length > 1 && args[0].length > 0 && args[1].length > 0) { + from = parseInt(args[0]) - 1; + lines = parseInt(args[1]); + } else if (args.length > 0 && args[0].length > 0) { + from = parseInt(args[0]) - 1; + } + + if (from < 0) from = 0; + if (lines < 0) lines = 10; + + // Request source arround current source location. + request.arguments = {}; + request.arguments.fromLine = from; + request.arguments.toLine = from + lines; + + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the scripts command. +DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) { + // Build a evaluate request from the text command. + var request = this.createRequest('scripts'); + + // Process arguments if any. + if (args && args.length > 0) { + args = args.split(/\s*[ ]+\s*/g); + + if (args.length > 1) { + throw new Error('Invalid scripts arguments.'); + } + + request.arguments = {}; + switch (args[0]) { + case 'natives': + request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native); + break; + + case 'extensions': + request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension); + break; + + case 'all': + request.arguments.types = + ScriptTypeFlag(Debug.ScriptType.Normal) | + ScriptTypeFlag(Debug.ScriptType.Native) | + ScriptTypeFlag(Debug.ScriptType.Extension); + break; + + default: + // If the arg is not one of the know one aboves, then it must be a + // filter used for filtering the results: + request.arguments.filter = args[0]; + break; + } + } + + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the break command. +DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) { + // Build a evaluate request from the text command. + // Process arguments if any. + if (args && args.length > 0) { + var target = args; + var type = 'function'; + var line; + var column; + var condition; + var pos; + + var request = this.createRequest('setbreakpoint'); + + // Break the args into target spec and condition if appropriate. + + // Check for breakpoint condition. + pos = args.indexOf(' '); + if (pos > 0) { + target = args.substring(0, pos); + condition = args.substring(pos + 1, args.length); + } + + // Check for script breakpoint (name:line[:column]). If no ':' in break + // specification it is considered a function break point. + pos = target.indexOf(':'); + if (pos > 0) { + type = 'script'; + var tmp = target.substring(pos + 1, target.length); + target = target.substring(0, pos); + + // Check for both line and column. + pos = tmp.indexOf(':'); + if (pos > 0) { + column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1; + line = parseInt(tmp.substring(0, pos)) - 1; + } else { + line = parseInt(tmp) - 1; + } + } else if (target[0] == '#' && target[target.length - 1] == '#') { + type = 'handle'; + target = target.substring(1, target.length - 1); + } else { + type = 'function'; + } + + request.arguments = {}; + request.arguments.type = type; + request.arguments.target = target; + request.arguments.line = line; + request.arguments.column = column; + request.arguments.condition = condition; + } else { + var request = this.createRequest('suspend'); + } + + return request.toJSONProtocol(); +}; + + +DebugRequest.prototype.breakpointsCommandToJSONRequest_ = function(args) { + if (args && args.length > 0) { + throw new Error('Unexpected arguments.'); + } + var request = this.createRequest('listbreakpoints'); + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the clear command. +DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) { + // Build a evaluate request from the text command. + var request = this.createRequest('clearbreakpoint'); + + // Process arguments if any. + if (args && args.length > 0) { + request.arguments = {}; + request.arguments.breakpoint = parseInt(args); + } else { + throw new Error('Invalid break arguments.'); + } + + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the change breakpoint command. +DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ = + function(args, command) { + + var request; + + // Check for exception breaks first: + // en[able] exc[eptions] [all|unc[aught]] + // en[able] [all|unc[aught]] exc[eptions] + // dis[able] exc[eptions] [all|unc[aught]] + // dis[able] [all|unc[aught]] exc[eptions] + if ((command == 'enable' || command == 'disable') && + args && args.length > 1) { + var nextPos = args.indexOf(' '); + var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args; + var excType = null; + + // Check for: + // en[able] exc[eptions] [all|unc[aught]] + // dis[able] exc[eptions] [all|unc[aught]] + if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') { + + var arg2 = (nextPos > 0) ? + args.substring(nextPos + 1, args.length) : 'all'; + if (!arg2) { + arg2 = 'all'; // if unspecified, set for all. + } if (arg2 == 'unc') { // check for short cut. + arg2 = 'uncaught'; + } + excType = arg2; + + // Check for: + // en[able] [all|unc[aught]] exc[eptions] + // dis[able] [all|unc[aught]] exc[eptions] + } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') { + + var arg2 = (nextPos > 0) ? + args.substring(nextPos + 1, args.length) : null; + if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') { + excType = arg1; + if (excType == 'unc') { + excType = 'uncaught'; + } + } + } + + // If we matched one of the command formats, then excType will be non-null: + if (excType) { + // Build a evaluate request from the text command. + request = this.createRequest('setexceptionbreak'); + + request.arguments = {}; + request.arguments.type = excType; + request.arguments.enabled = (command == 'enable'); + + return request.toJSONProtocol(); + } + } + + // Build a evaluate request from the text command. + request = this.createRequest('changebreakpoint'); + + // Process arguments if any. + if (args && args.length > 0) { + request.arguments = {}; + var pos = args.indexOf(' '); + var breakpointArg = args; + var otherArgs; + if (pos > 0) { + breakpointArg = args.substring(0, pos); + otherArgs = args.substring(pos + 1, args.length); + } + + request.arguments.breakpoint = parseInt(breakpointArg); + + switch(command) { + case 'cond': + request.arguments.condition = otherArgs ? otherArgs : null; + break; + case 'enable': + request.arguments.enabled = true; + break; + case 'disable': + request.arguments.enabled = false; + break; + case 'ignore': + request.arguments.ignoreCount = parseInt(otherArgs); + break; + default: + throw new Error('Invalid arguments.'); + } + } else { + throw new Error('Invalid arguments.'); + } + + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the disconnect command. +DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) { + var request; + request = this.createRequest('disconnect'); + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the info command. +DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) { + var request; + if (args && (args == 'break' || args == 'br')) { + // Build a evaluate request from the text command. + request = this.createRequest('listbreakpoints'); + last_cmd = 'info break'; + } else if (args && (args == 'locals' || args == 'lo')) { + // Build a evaluate request from the text command. + request = this.createRequest('frame'); + last_cmd = 'info locals'; + } else if (args && (args == 'args' || args == 'ar')) { + // Build a evaluate request from the text command. + request = this.createRequest('frame'); + last_cmd = 'info args'; + } else if (lol_is_enabled && + args && (args == 'liveobjectlist' || args == 'lol')) { + // Build a evaluate request from the text command. + return this.liveObjectListToJSONRequest_(null); + } else { + throw new Error('Invalid info arguments.'); + } + + return request.toJSONProtocol(); +}; + + +DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) { + var request; + request = this.createRequest('v8flags'); + request.arguments = {}; + request.arguments.flags = args; + return request.toJSONProtocol(); +}; + + +DebugRequest.prototype.gcToJSONRequest_ = function(args) { + var request; + if (!args) { + args = 'all'; + } + var args = args.split(/\s+/g); + var cmd = args[0]; + + switch(cmd) { + case 'all': + case 'quick': + case 'full': + case 'young': + case 'old': + case 'compact': + case 'sweep': + case 'scavenge': { + if (cmd == 'young') { cmd = 'quick'; } + else if (cmd == 'old') { cmd = 'full'; } + + request = this.createRequest('gc'); + request.arguments = {}; + request.arguments.type = cmd; + break; + } + // Else fall thru to the default case below to report the error. + default: + throw new Error('Missing arguments after ' + cmd + '.'); + } + return request.toJSONProtocol(); +}; + + +// Args: [v[erbose]] [<N>] [i[ndex] <i>] [t[ype] <type>] [sp[ace] <space>] +DebugRequest.prototype.lolMakeListRequest = + function(cmd, args, first_arg_index, is_repeating) { + + var request; + var start_index = 0; + var dump_limit = void 0; + var type_filter = void 0; + var space_filter = void 0; + var prop_filter = void 0; + var is_verbose = false; + var i; + + for (i = first_arg_index; i < args.length; i++) { + var arg = args[i]; + // Check for [v[erbose]]: + if (arg === 'verbose' || arg === 'v') { + // Nothing to do. This is already implied by args.length > 3. + is_verbose = true; + + // Check for [<N>]: + } else if (arg.match(/^[0-9]+$/)) { + dump_limit = arg; + is_verbose = true; + + // Check for i[ndex] <i>: + } else if (arg === 'index' || arg === 'i') { + i++; + if (args.length < i) { + throw new Error('Missing index after ' + arg + '.'); + } + start_index = parseInt(args[i]); + // The user input start index starts at 1: + if (start_index <= 0) { + throw new Error('Invalid index ' + args[i] + '.'); + } + start_index -= 1; + is_verbose = true; + + // Check for t[ype] <type>: + } else if (arg === 'type' || arg === 't') { + i++; + if (args.length < i) { + throw new Error('Missing type after ' + arg + '.'); + } + type_filter = args[i]; + + // Check for space <heap space name>: + } else if (arg === 'space' || arg === 'sp') { + i++; + if (args.length < i) { + throw new Error('Missing space name after ' + arg + '.'); + } + space_filter = args[i]; + + // Check for property <prop name>: + } else if (arg === 'property' || arg === 'prop') { + i++; + if (args.length < i) { + throw new Error('Missing property name after ' + arg + '.'); + } + prop_filter = args[i]; + + } else { + throw new Error('Unknown args at ' + arg + '.'); + } + } + + // Build the verbose request: + if (is_verbose) { + request = this.createLOLRequest('lol-'+cmd, + start_index, + dump_limit, + is_repeating); + request.arguments.verbose = true; + } else { + request = this.createRequest('lol-'+cmd); + request.arguments = {}; + } + + request.arguments.filter = {}; + if (type_filter) { + request.arguments.filter.type = type_filter; + } + if (space_filter) { + request.arguments.filter.space = space_filter; + } + if (prop_filter) { + request.arguments.filter.prop = prop_filter; + } + + return request; +} + + +function extractObjId(args) { + var id = args; + id = id.match(/^@([0-9]+)$/); + if (id) { + id = id[1]; + } else { + throw new Error('Invalid obj id ' + args + '.'); + } + return parseInt(id); +} + + +DebugRequest.prototype.lolToJSONRequest_ = function(args, is_repeating) { + var request; + // Use default command if one is not specified: + if (!args) { + args = 'info'; + } + + var orig_args = args; + var first_arg_index; + + var arg, i; + var args = args.split(/\s+/g); + var cmd = args[0]; + var id; + + // Command: <id> [v[erbose]] ... + if (cmd.match(/^[0-9]+$/)) { + // Convert to the padded list command: + // Command: l[ist] <dummy> <id> [v[erbose]] ... + + // Insert the implicit 'list' in front and process as normal: + cmd = 'list'; + args.unshift(cmd); + } + + switch(cmd) { + // Command: c[apture] + case 'capture': + case 'c': + request = this.createRequest('lol-capture'); + break; + + // Command: clear|d[elete] <id>|all + case 'clear': + case 'delete': + case 'del': { + if (args.length < 2) { + throw new Error('Missing argument after ' + cmd + '.'); + } else if (args.length > 2) { + throw new Error('Too many arguments after ' + cmd + '.'); + } + id = args[1]; + if (id.match(/^[0-9]+$/)) { + // Delete a specific lol record: + request = this.createRequest('lol-delete'); + request.arguments = {}; + request.arguments.id = parseInt(id); + } else if (id === 'all') { + // Delete all: + request = this.createRequest('lol-reset'); + } else { + throw new Error('Invalid argument after ' + cmd + '.'); + } + break; + } + + // Command: diff <id1> <id2> [<dump options>] + case 'diff': + first_arg_index = 3; + + // Command: list <dummy> <id> [<dump options>] + case 'list': + + // Command: ret[ainers] <obj id> [<dump options>] + case 'retainers': + case 'ret': + case 'retaining-paths': + case 'rp': { + if (cmd === 'ret') cmd = 'retainers'; + else if (cmd === 'rp') cmd = 'retaining-paths'; + + if (!first_arg_index) first_arg_index = 2; + + if (args.length < first_arg_index) { + throw new Error('Too few arguments after ' + cmd + '.'); + } + + var request_cmd = (cmd === 'list') ? 'diff':cmd; + request = this.lolMakeListRequest(request_cmd, + args, + first_arg_index, + is_repeating); + + if (cmd === 'diff') { + request.arguments.id1 = parseInt(args[1]); + request.arguments.id2 = parseInt(args[2]); + } else if (cmd == 'list') { + request.arguments.id1 = 0; + request.arguments.id2 = parseInt(args[1]); + } else { + request.arguments.id = extractObjId(args[1]); + } + break; + } + + // Command: getid + case 'getid': { + request = this.createRequest('lol-getid'); + request.arguments = {}; + request.arguments.address = args[1]; + break; + } + + // Command: inf[o] [<N>] + case 'info': + case 'inf': { + if (args.length > 2) { + throw new Error('Too many arguments after ' + cmd + '.'); + } + // Built the info request: + request = this.createLOLRequest('lol-info', 0, args[1], is_repeating); + break; + } + + // Command: path <obj id 1> <obj id 2> + case 'path': { + request = this.createRequest('lol-path'); + request.arguments = {}; + if (args.length > 2) { + request.arguments.id1 = extractObjId(args[1]); + request.arguments.id2 = extractObjId(args[2]); + } else { + request.arguments.id1 = 0; + request.arguments.id2 = extractObjId(args[1]); + } + break; + } + + // Command: print + case 'print': { + request = this.createRequest('lol-print'); + request.arguments = {}; + request.arguments.id = extractObjId(args[1]); + break; + } + + // Command: reset + case 'reset': { + request = this.createRequest('lol-reset'); + break; + } + + default: + throw new Error('Invalid arguments.'); + } + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the threads command. +DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) { + // Build a threads request from the text command. + var request = this.createRequest('threads'); + return request.toJSONProtocol(); +}; + + +// Handle the trace command. +DebugRequest.prototype.traceCommand_ = function(args) { + // Process arguments. + if (args && args.length > 0) { + if (args == 'compile') { + trace_compile = !trace_compile; + print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off')); + } else if (args === 'debug json' || args === 'json' || args === 'packets') { + trace_debug_json = !trace_debug_json; + print('Tracing of debug json packets ' + + (trace_debug_json ? 'on' : 'off')); + } else { + throw new Error('Invalid trace arguments.'); + } + } else { + throw new Error('Invalid trace arguments.'); + } +} + +// Handle the help command. +DebugRequest.prototype.helpCommand_ = function(args) { + // Help os quite simple. + if (args && args.length > 0) { + print('warning: arguments to \'help\' are ignored'); + } + + print('Note: <> denotes symbollic values to be replaced with real values.'); + print('Note: [] denotes optional parts of commands, or optional options / arguments.'); + print(' e.g. d[elete] - you get the same command if you type d or delete.'); + print(''); + print('[break] - break as soon as possible'); + print('b[reak] location [condition]'); + print(' - break on named function: location is a function name'); + print(' - break on function: location is #<id>#'); + print(' - break on script position: location is name:line[:column]'); + print(''); + print('clear <breakpoint #> - deletes the specified user defined breakpoint'); + print('d[elete] <breakpoint #> - deletes the specified user defined breakpoint'); + print('dis[able] <breakpoint #> - disables the specified user defined breakpoint'); + print('dis[able] exc[eptions] [[all] | unc[aught]]'); + print(' - disables breaking on exceptions'); + print('en[able] <breakpoint #> - enables the specified user defined breakpoint'); + print('en[able] exc[eptions] [[all] | unc[aught]]'); + print(' - enables breaking on exceptions'); + print(''); + print('b[ack]t[race] [n] | [-n] | [from to]'); + print(' - prints the stack back trace'); + print('f[rame] - prints info about the current frame context'); + print('f[rame] <frame #> - set context to specified frame #'); + print('scopes'); + print('scope <scope #>'); + print(''); + print('up - set context to caller of current frame'); + print('do[wn] - set context to callee of current frame'); + print('inf[o] br[eak] - prints info about breakpoints in use'); + print('inf[o] ar[gs] - prints info about arguments of the current function'); + print('inf[o] lo[cals] - prints info about locals in the current function'); + print('inf[o] liveobjectlist|lol - same as \'lol info\''); + print(''); + print('step [in | next | out| min [step count]]'); + print('c[ontinue] - continue executing after a breakpoint'); + print('s[tep] [<N>] - step into the next N callees (default N is 1)'); + print('s[tep]i [<N>] - step into the next N callees (default N is 1)'); + print('n[ext] [<N>] - step over the next N callees (default N is 1)'); + print('fin[ish] [<N>] - step out of N frames (default N is 1)'); + print(''); + print('p[rint] <expression> - prints the result of the specified expression'); + print('dir <expression> - prints the object structure of the result'); + print('set <var> = <expression> - executes the specified statement'); + print(''); + print('l[ist] - list the source code around for the current pc'); + print('l[ist] [- | <start>,<end>] - list the specified range of source code'); + print('source [from line [num lines]]'); + print('scr[ipts] [native|extensions|all]'); + print('scr[ipts] [<filter text>] - list scripts with the specified text in its description'); + print(''); + print('gc - runs the garbage collector'); + print(''); + + if (lol_is_enabled) { + print('liveobjectlist|lol <command> - live object list tracking.'); + print(' where <command> can be:'); + print(' c[apture] - captures a LOL list.'); + print(' clear|del[ete] <id>|all - clears LOL of id <id>.'); + print(' If \'all\' is unspecified instead, will clear all.'); + print(' diff <id1> <id2> [<dump options>]'); + print(' - prints the diff between LOLs id1 and id2.'); + print(' - also see <dump options> below.'); + print(' getid <address> - gets the obj id for the specified address if available.'); + print(' The address must be in hex form prefixed with 0x.'); + print(' inf[o] [<N>] - lists summary info of all LOL lists.'); + print(' If N is specified, will print N items at a time.'); + print(' [l[ist]] <id> [<dump options>]'); + print(' - prints the listing of objects in LOL id.'); + print(' - also see <dump options> below.'); + print(' reset - clears all LOL lists.'); + print(' ret[ainers] <id> [<dump options>]'); + print(' - prints the list of retainers of obj id.'); + print(' - also see <dump options> below.'); + print(' path <id1> <id2> - prints the retaining path from obj id1 to id2.'); + print(' If only one id is specified, will print the path from'); + print(' roots to the specified object if available.'); + print(' print <id> - prints the obj for the specified obj id if available.'); + print(''); + print(' <dump options> includes:'); + print(' [v[erbose]] - do verbose dump.'); + print(' [<N>] - dump N items at a time. Implies verbose dump.'); + print(' If unspecified, N will default to '+ + kDefaultLolLinesToPrintAtATime+'. Max N is '+ + kMaxLolLinesToPrintAtATime+'.'); + print(' [i[ndex] <i>] - start dump from index i. Implies verbose dump.'); + print(' [t[ype] <type>] - filter by type.'); + print(' [sp[ace] <space name>] - filter by heap space where <space name> is one of'); + print(' { cell, code, lo, map, new, old-data, old-pointer }.'); + print(''); + print(' If the verbose option, or an option that implies a verbose dump'); + print(' is specified, then a verbose dump will requested. Else, a summary dump'); + print(' will be requested.'); + print(''); + } + + print('trace compile'); + // hidden command: trace debug json - toggles tracing of debug json packets + print(''); + print('disconnect|exit|quit - disconnects and quits the debugger'); + print('help - prints this help information'); +} + + +function formatHandleReference_(value) { + if (value.handle() >= 0) { + return '#' + value.handle() + '#'; + } else { + return '#Transient#'; + } +} + + +function formatObject_(value, include_properties) { + var result = ''; + result += formatHandleReference_(value); + result += ', type: object' + result += ', constructor '; + var ctor = value.constructorFunctionValue(); + result += formatHandleReference_(ctor); + result += ', __proto__ '; + var proto = value.protoObjectValue(); + result += formatHandleReference_(proto); + result += ', '; + result += value.propertyCount(); + result += ' properties.'; + if (include_properties) { + result += '\n'; + for (var i = 0; i < value.propertyCount(); i++) { + result += ' '; + result += value.propertyName(i); + result += ': '; + var property_value = value.propertyValue(i); + if (property_value instanceof ProtocolReference) { + result += '<no type>'; + } else { + if (property_value && property_value.type()) { + result += property_value.type(); + } else { + result += '<no type>'; + } + } + result += ' '; + result += formatHandleReference_(property_value); + result += '\n'; + } + } + return result; +} + + +function formatScope_(scope) { + var result = ''; + var index = scope.index; + result += '#' + (index <= 9 ? '0' : '') + index; + result += ' '; + switch (scope.type) { + case Debug.ScopeType.Global: + result += 'Global, '; + result += '#' + scope.object.ref + '#'; + break; + case Debug.ScopeType.Local: + result += 'Local'; + break; + case Debug.ScopeType.With: + result += 'With, '; + result += '#' + scope.object.ref + '#'; + break; + case Debug.ScopeType.Catch: + result += 'Catch, '; + result += '#' + scope.object.ref + '#'; + break; + case Debug.ScopeType.Closure: + result += 'Closure'; + break; + default: + result += 'UNKNOWN'; + } + return result; +} + + +function refObjectToString_(protocolPackage, handle) { + var value = protocolPackage.lookup(handle); + var result = ''; + if (value.isString()) { + result = '"' + value.value() + '"'; + } else if (value.isPrimitive()) { + result = value.valueString(); + } else if (value.isObject()) { + result += formatObject_(value, true); + } + return result; +} + + +function decodeLolCaptureResponse(body) { + var result; + result = 'Captured live object list '+ body.id + + ': count '+ body.count + ' size ' + body.size; + return result; +} + + +function decodeLolDeleteResponse(body) { + var result; + result = 'Deleted live object list '+ body.id; + return result; +} + + +function digitsIn(value) { + var digits = 0; + if (value === 0) value = 1; + while (value >= 1) { + digits++; + value /= 10; + } + return digits; +} + + +function padding(value, max_digits) { + var padding_digits = max_digits - digitsIn(value); + var padding = ''; + while (padding_digits > 0) { + padding += ' '; + padding_digits--; + } + return padding; +} + + +function decodeLolInfoResponse(body) { + var result; + var lists = body.lists; + var length = lists.length; + var first_index = body.first_index + 1; + var has_more = ((first_index + length) <= body.count); + result = 'captured live object lists'; + if (has_more || (first_index != 1)) { + result += ' ['+ length +' of '+ body.count + + ': starting from '+ first_index +']'; + } + result += ':\n'; + var max_digits = digitsIn(body.count); + var last_count = 0; + var last_size = 0; + for (var i = 0; i < length; i++) { + var entry = lists[i]; + var count = entry.count; + var size = entry.size; + var index = first_index + i; + result += ' [' + padding(index, max_digits) + index + '] id '+ entry.id + + ': count '+ count; + if (last_count > 0) { + result += '(+' + (count - last_count) + ')'; + } + result += ' size '+ size; + if (last_size > 0) { + result += '(+' + (size - last_size) + ')'; + } + result += '\n'; + last_count = count; + last_size = size; + } + result += ' total: '+length+' lists\n'; + if (has_more) { + result += ' -- press <enter> for more --\n'; + } else { + repeat_cmd_line = ''; + } + if (length === 0) result += ' none\n'; + + return result; +} + + +function decodeLolListResponse(body, title) { + + var result; + var total_count = body.count; + var total_size = body.size; + var length; + var max_digits; + var i; + var entry; + var index; + + var max_count_digits = digitsIn(total_count); + var max_size_digits; + + var summary = body.summary; + if (summary) { + + var roots_count = 0; + var found_root = body.found_root || 0; + var found_weak_root = body.found_weak_root || 0; + + // Print the summary result: + result = 'summary of objects:\n'; + length = summary.length; + if (found_root !== 0) { + roots_count++; + } + if (found_weak_root !== 0) { + roots_count++; + } + max_digits = digitsIn(length + roots_count); + max_size_digits = digitsIn(total_size); + + index = 1; + if (found_root !== 0) { + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ 1 + padding(0, max_count_digits) + + ' '+ padding(0, max_size_digits+1) + + ' : <root>\n'; + index++; + } + if (found_weak_root !== 0) { + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ 1 + padding(0, max_count_digits) + + ' '+ padding(0, max_size_digits+1) + + ' : <weak root>\n'; + index++; + } + + for (i = 0; i < length; i++) { + entry = summary[i]; + var count = entry.count; + var size = entry.size; + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ count + padding(count, max_count_digits) + + ' size '+ size + padding(size, max_size_digits) + + ' : <' + entry.desc + '>\n'; + index++; + } + result += '\n total count: '+(total_count+roots_count)+'\n'; + if (body.size) { + result += ' total size: '+body.size+'\n'; + } + + } else { + // Print the full dump result: + var first_index = body.first_index + 1; + var elements = body.elements; + length = elements.length; + var has_more = ((first_index + length) <= total_count); + result = title; + if (has_more || (first_index != 1)) { + result += ' ['+ length +' of '+ total_count + + ': starting from '+ first_index +']'; + } + result += ':\n'; + if (length === 0) result += ' none\n'; + max_digits = digitsIn(length); + + var max_id = 0; + var max_size = 0; + for (i = 0; i < length; i++) { + entry = elements[i]; + if (entry.id > max_id) max_id = entry.id; + if (entry.size > max_size) max_size = entry.size; + } + var max_id_digits = digitsIn(max_id); + max_size_digits = digitsIn(max_size); + + for (i = 0; i < length; i++) { + entry = elements[i]; + index = first_index + i; + result += ' ['+ padding(index, max_digits) + index +']'; + if (entry.id !== 0) { + result += ' @' + entry.id + padding(entry.id, max_id_digits) + + ': size ' + entry.size + ', ' + + padding(entry.size, max_size_digits) + entry.desc + '\n'; + } else { + // Must be a root or weak root: + result += ' ' + entry.desc + '\n'; + } + } + if (has_more) { + result += ' -- press <enter> for more --\n'; + } else { + repeat_cmd_line = ''; + } + if (length === 0) result += ' none\n'; + } + + return result; +} + + +function decodeLolDiffResponse(body) { + var title = 'objects'; + return decodeLolListResponse(body, title); +} + + +function decodeLolRetainersResponse(body) { + var title = 'retainers for @' + body.id; + return decodeLolListResponse(body, title); +} + + +function decodeLolPathResponse(body) { + return body.path; +} + + +function decodeLolResetResponse(body) { + return 'Reset all live object lists.'; +} + + +function decodeLolGetIdResponse(body) { + if (body.id == 0) { + return 'Address is invalid, or object has been moved or collected'; + } + return 'obj id is @' + body.id; +} + + +function decodeLolPrintResponse(body) { + return body.dump; +} + + +// Rounds number 'num' to 'length' decimal places. +function roundNumber(num, length) { + var factor = Math.pow(10, length); + return Math.round(num * factor) / factor; +} + + +// Convert a JSON response to text for display in a text based debugger. +function DebugResponseDetails(response) { + details = {text:'', running:false} + + try { + if (!response.success()) { + details.text = response.message(); + return details; + } + + // Get the running state. + details.running = response.running(); + + var body = response.body(); + var result = ''; + switch (response.command()) { + case 'suspend': + details.text = 'stopped'; + break; + + case 'setbreakpoint': + result = 'set breakpoint #'; + result += body.breakpoint; + details.text = result; + break; + + case 'clearbreakpoint': + result = 'cleared breakpoint #'; + result += body.breakpoint; + details.text = result; + break; + + case 'changebreakpoint': + result = 'successfully changed breakpoint'; + details.text = result; + break; + + case 'listbreakpoints': + result = 'breakpoints: (' + body.breakpoints.length + ')'; + for (var i = 0; i < body.breakpoints.length; i++) { + var breakpoint = body.breakpoints[i]; + result += '\n id=' + breakpoint.number; + result += ' type=' + breakpoint.type; + if (breakpoint.script_id) { + result += ' script_id=' + breakpoint.script_id; + } + if (breakpoint.script_name) { + result += ' script_name=' + breakpoint.script_name; + } + result += ' line=' + (breakpoint.line + 1); + if (breakpoint.column != null) { + result += ' column=' + (breakpoint.column + 1); + } + if (breakpoint.groupId) { + result += ' groupId=' + breakpoint.groupId; + } + if (breakpoint.ignoreCount) { + result += ' ignoreCount=' + breakpoint.ignoreCount; + } + if (breakpoint.active === false) { + result += ' inactive'; + } + if (breakpoint.condition) { + result += ' condition=' + breakpoint.condition; + } + result += ' hit_count=' + breakpoint.hit_count; + } + if (body.breakpoints.length === 0) { + result = "No user defined breakpoints\n"; + } else { + result += '\n'; + } + if (body.breakOnExceptions) { + result += '* breaking on ALL exceptions is enabled\n'; + } else if (body.breakOnUncaughtExceptions) { + result += '* breaking on UNCAUGHT exceptions is enabled\n'; + } else { + result += '* all exception breakpoints are disabled\n'; + } + details.text = result; + break; + + case 'setexceptionbreak': + result = 'Break on ' + body.type + ' exceptions: '; + result += body.enabled ? 'enabled' : 'disabled'; + details.text = result; + break; + + case 'backtrace': + if (body.totalFrames == 0) { + result = '(empty stack)'; + } else { + var result = 'Frames #' + body.fromFrame + ' to #' + + (body.toFrame - 1) + ' of ' + body.totalFrames + '\n'; + for (i = 0; i < body.frames.length; i++) { + if (i != 0) result += '\n'; + result += body.frames[i].text; + } + } + details.text = result; + break; + + case 'frame': + if (last_cmd === 'info locals') { + var locals = body.locals; + if (locals.length === 0) { + result = 'No locals'; + } else { + for (var i = 0; i < locals.length; i++) { + var local = locals[i]; + result += local.name + ' = '; + result += refObjectToString_(response, local.value.ref); + result += '\n'; + } + } + } else if (last_cmd === 'info args') { + var args = body.arguments; + if (args.length === 0) { + result = 'No arguments'; + } else { + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + result += arg.name + ' = '; + result += refObjectToString_(response, arg.value.ref); + result += '\n'; + } + } + } else { + result = SourceUnderline(body.sourceLineText, + body.column); + Debug.State.currentSourceLine = body.line; + Debug.State.currentFrame = body.index; + Debug.State.displaySourceStartLine = -1; + Debug.State.displaySourceEndLine = -1; + } + details.text = result; + break; + + case 'scopes': + if (body.totalScopes == 0) { + result = '(no scopes)'; + } else { + result = 'Scopes #' + body.fromScope + ' to #' + + (body.toScope - 1) + ' of ' + body.totalScopes + '\n'; + for (i = 0; i < body.scopes.length; i++) { + if (i != 0) { + result += '\n'; + } + result += formatScope_(body.scopes[i]); + } + } + details.text = result; + break; + + case 'scope': + result += formatScope_(body); + result += '\n'; + var scope_object_value = response.lookup(body.object.ref); + result += formatObject_(scope_object_value, true); + details.text = result; + break; + + case 'evaluate': + case 'lookup': + case 'getobj': + if (last_cmd == 'p' || last_cmd == 'print') { + result = body.text; + } else { + var value; + if (lookup_handle) { + value = response.bodyValue(lookup_handle); + } else { + value = response.bodyValue(); + } + if (value.isObject()) { + result += formatObject_(value, true); + } else { + result += 'type: '; + result += value.type(); + if (!value.isUndefined() && !value.isNull()) { + result += ', '; + if (value.isString()) { + result += '"'; + } + result += value.value(); + if (value.isString()) { + result += '"'; + } + } + result += '\n'; + } + } + details.text = result; + break; + + case 'references': + var count = body.length; + result += 'found ' + count + ' objects'; + result += '\n'; + for (var i = 0; i < count; i++) { + var value = response.bodyValue(i); + result += formatObject_(value, false); + result += '\n'; + } + details.text = result; + break; + + case 'source': + // Get the source from the response. + var source = body.source; + var from_line = body.fromLine + 1; + var lines = source.split('\n'); + var maxdigits = 1 + Math.floor(log10(from_line + lines.length)); + if (maxdigits < 3) { + maxdigits = 3; + } + var result = ''; + for (var num = 0; num < lines.length; num++) { + // Check if there's an extra newline at the end. + if (num == (lines.length - 1) && lines[num].length == 0) { + break; + } + + var current_line = from_line + num; + spacer = maxdigits - (1 + Math.floor(log10(current_line))); + if (current_line == Debug.State.currentSourceLine + 1) { + for (var i = 0; i < maxdigits; i++) { + result += '>'; + } + result += ' '; + } else { + for (var i = 0; i < spacer; i++) { + result += ' '; + } + result += current_line + ': '; + } + result += lines[num]; + result += '\n'; + } + details.text = result; + break; + + case 'scripts': + var result = ''; + for (i = 0; i < body.length; i++) { + if (i != 0) result += '\n'; + if (body[i].id) { + result += body[i].id; + } else { + result += '[no id]'; + } + result += ', '; + if (body[i].name) { + result += body[i].name; + } else { + if (body[i].compilationType == Debug.ScriptCompilationType.Eval + && body[i].evalFromScript + ) { + result += 'eval from '; + var script_value = response.lookup(body[i].evalFromScript.ref); + result += ' ' + script_value.field('name'); + result += ':' + (body[i].evalFromLocation.line + 1); + result += ':' + body[i].evalFromLocation.column; + } else if (body[i].compilationType == + Debug.ScriptCompilationType.JSON) { + result += 'JSON '; + } else { // body[i].compilation == Debug.ScriptCompilationType.Host + result += '[unnamed] '; + } + } + result += ' (lines: '; + result += body[i].lineCount; + result += ', length: '; + result += body[i].sourceLength; + if (body[i].type == Debug.ScriptType.Native) { + result += ', native'; + } else if (body[i].type == Debug.ScriptType.Extension) { + result += ', extension'; + } + result += '), ['; + var sourceStart = body[i].sourceStart; + if (sourceStart.length > 40) { + sourceStart = sourceStart.substring(0, 37) + '...'; + } + result += sourceStart; + result += ']'; + } + if (body.length == 0) { + result = "no matching scripts found"; + } + details.text = result; + break; + + case 'threads': + var result = 'Active V8 threads: ' + body.totalThreads + '\n'; + body.threads.sort(function(a, b) { return a.id - b.id; }); + for (i = 0; i < body.threads.length; i++) { + result += body.threads[i].current ? '*' : ' '; + result += ' '; + result += body.threads[i].id; + result += '\n'; + } + details.text = result; + break; + + case 'continue': + details.text = "(running)"; + break; + + case 'v8flags': + details.text = "flags set"; + break; + + case 'gc': + details.text = "GC " + body.before + " => " + body.after; + if (body.after > (1024*1024)) { + details.text += + " (" + roundNumber(body.before/(1024*1024), 1) + "M => " + + roundNumber(body.after/(1024*1024), 1) + "M)"; + } else if (body.after > 1024) { + details.text += + " (" + roundNumber(body.before/1024, 1) + "K => " + + roundNumber(body.after/1024, 1) + "K)"; + } + break; + + case 'lol-capture': + details.text = decodeLolCaptureResponse(body); + break; + case 'lol-delete': + details.text = decodeLolDeleteResponse(body); + break; + case 'lol-diff': + details.text = decodeLolDiffResponse(body); + break; + case 'lol-getid': + details.text = decodeLolGetIdResponse(body); + break; + case 'lol-info': + details.text = decodeLolInfoResponse(body); + break; + case 'lol-print': + details.text = decodeLolPrintResponse(body); + break; + case 'lol-reset': + details.text = decodeLolResetResponse(body); + break; + case 'lol-retainers': + details.text = decodeLolRetainersResponse(body); + break; + case 'lol-path': + details.text = decodeLolPathResponse(body); + break; + + default: + details.text = + 'Response for unknown command \'' + response.command() + '\'' + + ' (' + response.raw_json() + ')'; + } + } catch (e) { + details.text = 'Error: "' + e + '" formatting response'; + } + + return details; +}; + + +/** + * Protocol packages send from the debugger. + * @param {string} json - raw protocol packet as JSON string. + * @constructor + */ +function ProtocolPackage(json) { + this.raw_json_ = json; + this.packet_ = JSON.parse(json); + this.refs_ = []; + if (this.packet_.refs) { + for (var i = 0; i < this.packet_.refs.length; i++) { + this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i]; + } + } +} + + +/** + * Get the packet type. + * @return {String} the packet type + */ +ProtocolPackage.prototype.type = function() { + return this.packet_.type; +} + + +/** + * Get the packet event. + * @return {Object} the packet event + */ +ProtocolPackage.prototype.event = function() { + return this.packet_.event; +} + + +/** + * Get the packet request sequence. + * @return {number} the packet request sequence + */ +ProtocolPackage.prototype.requestSeq = function() { + return this.packet_.request_seq; +} + + +/** + * Get the packet request sequence. + * @return {number} the packet request sequence + */ +ProtocolPackage.prototype.running = function() { + return this.packet_.running ? true : false; +} + + +ProtocolPackage.prototype.success = function() { + return this.packet_.success ? true : false; +} + + +ProtocolPackage.prototype.message = function() { + return this.packet_.message; +} + + +ProtocolPackage.prototype.command = function() { + return this.packet_.command; +} + + +ProtocolPackage.prototype.body = function() { + return this.packet_.body; +} + + +ProtocolPackage.prototype.bodyValue = function(index) { + if (index != null) { + return new ProtocolValue(this.packet_.body[index], this); + } else { + return new ProtocolValue(this.packet_.body, this); + } +} + + +ProtocolPackage.prototype.body = function() { + return this.packet_.body; +} + + +ProtocolPackage.prototype.lookup = function(handle) { + var value = this.refs_[handle]; + if (value) { + return new ProtocolValue(value, this); + } else { + return new ProtocolReference(handle); + } +} + + +ProtocolPackage.prototype.raw_json = function() { + return this.raw_json_; +} + + +function ProtocolValue(value, packet) { + this.value_ = value; + this.packet_ = packet; +} + + +/** + * Get the value type. + * @return {String} the value type + */ +ProtocolValue.prototype.type = function() { + return this.value_.type; +} + + +/** + * Get a metadata field from a protocol value. + * @return {Object} the metadata field value + */ +ProtocolValue.prototype.field = function(name) { + return this.value_[name]; +} + + +/** + * Check is the value is a primitive value. + * @return {boolean} true if the value is primitive + */ +ProtocolValue.prototype.isPrimitive = function() { + return this.isUndefined() || this.isNull() || this.isBoolean() || + this.isNumber() || this.isString(); +} + + +/** + * Get the object handle. + * @return {number} the value handle + */ +ProtocolValue.prototype.handle = function() { + return this.value_.handle; +} + + +/** + * Check is the value is undefined. + * @return {boolean} true if the value is undefined + */ +ProtocolValue.prototype.isUndefined = function() { + return this.value_.type == 'undefined'; +} + + +/** + * Check is the value is null. + * @return {boolean} true if the value is null + */ +ProtocolValue.prototype.isNull = function() { + return this.value_.type == 'null'; +} + + +/** + * Check is the value is a boolean. + * @return {boolean} true if the value is a boolean + */ +ProtocolValue.prototype.isBoolean = function() { + return this.value_.type == 'boolean'; +} + + +/** + * Check is the value is a number. + * @return {boolean} true if the value is a number + */ +ProtocolValue.prototype.isNumber = function() { + return this.value_.type == 'number'; +} + + +/** + * Check is the value is a string. + * @return {boolean} true if the value is a string + */ +ProtocolValue.prototype.isString = function() { + return this.value_.type == 'string'; +} + + +/** + * Check is the value is an object. + * @return {boolean} true if the value is an object + */ +ProtocolValue.prototype.isObject = function() { + return this.value_.type == 'object' || this.value_.type == 'function' || + this.value_.type == 'error' || this.value_.type == 'regexp'; +} + + +/** + * Get the constructor function + * @return {ProtocolValue} constructor function + */ +ProtocolValue.prototype.constructorFunctionValue = function() { + var ctor = this.value_.constructorFunction; + return this.packet_.lookup(ctor.ref); +} + + +/** + * Get the __proto__ value + * @return {ProtocolValue} __proto__ value + */ +ProtocolValue.prototype.protoObjectValue = function() { + var proto = this.value_.protoObject; + return this.packet_.lookup(proto.ref); +} + + +/** + * Get the number og properties. + * @return {number} the number of properties + */ +ProtocolValue.prototype.propertyCount = function() { + return this.value_.properties ? this.value_.properties.length : 0; +} + + +/** + * Get the specified property name. + * @return {string} property name + */ +ProtocolValue.prototype.propertyName = function(index) { + var property = this.value_.properties[index]; + return property.name; +} + + +/** + * Return index for the property name. + * @param name The property name to look for + * @return {number} index for the property name + */ +ProtocolValue.prototype.propertyIndex = function(name) { + for (var i = 0; i < this.propertyCount(); i++) { + if (this.value_.properties[i].name == name) { + return i; + } + } + return null; +} + + +/** + * Get the specified property value. + * @return {ProtocolValue} property value + */ +ProtocolValue.prototype.propertyValue = function(index) { + var property = this.value_.properties[index]; + return this.packet_.lookup(property.ref); +} + + +/** + * Check is the value is a string. + * @return {boolean} true if the value is a string + */ +ProtocolValue.prototype.value = function() { + return this.value_.value; +} + + +ProtocolValue.prototype.valueString = function() { + return this.value_.text; +} + + +function ProtocolReference(handle) { + this.handle_ = handle; +} + + +ProtocolReference.prototype.handle = function() { + return this.handle_; +} + + +function MakeJSONPair_(name, value) { + return '"' + name + '":' + value; +} + + +function ArrayToJSONObject_(content) { + return '{' + content.join(',') + '}'; +} + + +function ArrayToJSONArray_(content) { + return '[' + content.join(',') + ']'; +} + + +function BooleanToJSON_(value) { + return String(value); +} + + +function NumberToJSON_(value) { + return String(value); +} + + +// 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 testing for ", \ and control characters (0x00 - 0x1F). +const ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]'); + + +// Regular expression matching ", \ and control characters (0x00 - 0x1F) +// globally. +const ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g'); + + +/** + * Convert a String to its JSON representation (see http://www.json.org/). To + * avoid depending on the String object this method calls the functions in + * string.js directly and not through the value. + * @param {String} value The String value to format as JSON + * @return {string} JSON formatted String value + */ +function StringToJSON_(value) { + // Check for" , \ and control characters (0x00 - 0x1F). No need to call + // RegExpTest as ctrlchar is constructed using RegExp. + if (ctrlCharTest_.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. + return '\\u00' + + '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) + + '0' // TODO %NumberToRadixString(mapped % 16, 16); + }) + + '"'; + } + + // Simple string with no special characters. + return '"' + value + '"'; +} + + +/** + * Convert a Date to ISO 8601 format. To avoid depending on the Date object + * this method calls the functions in date.js directly and not through the + * value. + * @param {Date} value The Date value to format as JSON + * @return {string} JSON formatted Date value + */ +function DateToISO8601_(value) { + function f(n) { + return n < 10 ? '0' + n : n; + } + function g(n) { + return n < 10 ? '00' + n : n < 100 ? '0' + n : n; + } + return builtins.GetUTCFullYearFrom(value) + '-' + + f(builtins.GetUTCMonthFrom(value) + 1) + '-' + + f(builtins.GetUTCDateFrom(value)) + 'T' + + f(builtins.GetUTCHoursFrom(value)) + ':' + + f(builtins.GetUTCMinutesFrom(value)) + ':' + + f(builtins.GetUTCSecondsFrom(value)) + '.' + + g(builtins.GetUTCMillisecondsFrom(value)) + 'Z'; +} + + +/** + * Convert a Date to ISO 8601 format. To avoid depending on the Date object + * this method calls the functions in date.js directly and not through the + * value. + * @param {Date} value The Date value to format as JSON + * @return {string} JSON formatted Date value + */ +function DateToJSON_(value) { + return '"' + DateToISO8601_(value) + '"'; +} + + +/** + * Convert an Object to its JSON representation (see http://www.json.org/). + * This implementation simply runs through all string property names and adds + * each property to the JSON representation for some predefined types. For type + * "object" the function calls itself recursively unless the object has the + * function property "toJSONProtocol" in which case that is used. This is not + * a general implementation but sufficient for the debugger. Note that circular + * structures will cause infinite recursion. + * @param {Object} object The object to format as JSON + * @return {string} JSON formatted object value + */ +function SimpleObjectToJSON_(object) { + var content = []; + for (var key in object) { + // Only consider string keys. + if (typeof key == 'string') { + var property_value = object[key]; + + // Format the value based on its type. + var property_value_json; + switch (typeof property_value) { + case 'object': + if (property_value === null) { + property_value_json = 'null'; + } else if (typeof property_value.toJSONProtocol == 'function') { + property_value_json = property_value.toJSONProtocol(true) + } else if (property_value.constructor.name == 'Array'){ + property_value_json = SimpleArrayToJSON_(property_value); + } else { + property_value_json = SimpleObjectToJSON_(property_value); + } + break; + + case 'boolean': + property_value_json = BooleanToJSON_(property_value); + break; + + case 'number': + property_value_json = NumberToJSON_(property_value); + break; + + case 'string': + property_value_json = StringToJSON_(property_value); + break; + + default: + property_value_json = null; + } + + // Add the property if relevant. + if (property_value_json) { + content.push(StringToJSON_(key) + ':' + property_value_json); + } + } + } + + // Make JSON object representation. + return '{' + content.join(',') + '}'; +} + + +/** + * Convert an array to its JSON representation. This is a VERY simple + * implementation just to support what is needed for the debugger. + * @param {Array} arrya The array to format as JSON + * @return {string} JSON formatted array value + */ +function SimpleArrayToJSON_(array) { + // Make JSON array representation. + var json = '['; + for (var i = 0; i < array.length; i++) { + if (i != 0) { + json += ','; + } + var elem = array[i]; + if (elem.toJSONProtocol) { + json += elem.toJSONProtocol(true) + } else if (typeof(elem) === 'object') { + json += SimpleObjectToJSON_(elem); + } else if (typeof(elem) === 'boolean') { + json += BooleanToJSON_(elem); + } else if (typeof(elem) === 'number') { + json += NumberToJSON_(elem); + } else if (typeof(elem) === 'string') { + json += StringToJSON_(elem); + } else { + json += elem; + } + } + json += ']'; + return json; +} |