From d4859a55bc8f18f1834546af12a8d8f6a8802ebd Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 30 Dec 2010 11:17:11 -0800 Subject: [PATCH] Wrap up debugger in one class just for better readablity --- lib/_debugger.js | 526 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 279 insertions(+), 247 deletions(-) diff --git a/lib/_debugger.js b/lib/_debugger.js index 07a02cc..e0bd3e1 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -6,96 +6,14 @@ var spawn = require('child_process').spawn; exports.port = 5858; exports.start = function () { - startInterface(); - - process.on('exit', function () { - if (child) child.kill(); - }); + var interface = new Interface(); }; -var child; -var c; -var term; + var args = process.argv.slice(2); args.unshift('--debug-brk'); -function tryConnect(cb) { - c = new Client(); - - process.stdout.write("connecting..."); - c.connect(exports.port); - c.once('ready', function () { - process.stdout.write("ok\r\n"); - if (cb) cb(); - }); - - c.on('close', function () { - process.exit(0); - }); - - c.on('unhandledResponse', function (res) { - console.log("\r\nunhandled res:"); - console.log(res); - term.prompt(); - }); - - c.on('break', function (res) { - var result = ''; - if (res.body.breakpoints) { - result += 'breakpoint'; - if (res.body.breakpoints.length > 1) { - result += 's'; - } - result += ' #'; - for (var i = 0; i < res.body.breakpoints.length; i++) { - if (i > 0) { - result += ', #'; - } - result += res.body.breakpoints[i]; - } - } else { - result += 'break'; - } - result += ' in '; - result += res.body.invocationText; - result += ', '; - result += SourceInfo(res.body); - result += '\n'; - result += SourceUnderline(res.body.sourceLineText, res.body.sourceColumn); - - c.currentSourceLine = res.body.sourceLine; - c.currentFrame = 0; - c.currentScript = res.body.script.name; - - console.log(result); - - term.prompt(); - }); -} - - -function killChild() { - if (c) { - c.destroy(); - } - - if (child) { - child.kill(); - child = null; - } -} - - -function trySpawn(cb) { - killChild(); - - child = spawn(process.execPath, args, { customFds: [0, 1, 2] }); - - setTimeout(function () { - tryConnect(cb); - }, 1000); -} // // Parser/Serializer for V8 debugger protocol @@ -185,7 +103,7 @@ var NO_FRAME = -1; function Client() { net.Stream.call(this); - var protocol = this.protocol = new Protocol(c); + var protocol = this.protocol = new Protocol(this); this._reqCallbacks = []; var socket = this; @@ -340,7 +258,7 @@ Client.prototype.listbreakpoints = function(cb) { }); }; -// c.next(1, cb); +// client.next(1, cb); Client.prototype.step = function(action, count, cb) { var req = { command: 'continue', @@ -394,202 +312,316 @@ function SourceInfo(body) { } -var restartQuestionPrompt = "The program being debugged has " + - "been started already.\n" + - "Start it from the beginning? (y or n): "; +// This class is the readline-enabled debugger interface which is invoked on +// "node debug" +function Interface() { + var self = this; + var term = this.term = readline.createInterface(process.stdout); + var child; + var client; + var term; -function restartQuestion (cb) { - term.question(restartQuestionPrompt, function (answer) { - if (/^y(es)?$/i.test(answer)) { - cb(true); - } else if (/^n(o)?$/i.test(answer)) { - cb(false); - } else { - console.log("Please answer y or n."); - restartQuestion(cb); - } + process.on('exit', function () { + self.killChild(); }); -} - - -function printScripts () { - var text = ''; - for (var id in c.scripts) { - var script = c.scripts[id]; - if (typeof script == 'object' && script.name) { - text += script.name == c.currentScript ? '* ' : ' '; - text += script.name + '\n'; - } - } - process.stdout.write(text); -} - - -function printNotConnected () { - console.log("Program not running. Try 'run'."); - term.prompt(); -} - - -function startInterface() { - - term = readline.createInterface(process.stdout); var stdin = process.openStdin(); stdin.addListener('data', function(chunk) { term.write(chunk); }); - var prompt = 'debug> '; - term.setPrompt('debug> '); term.prompt(); - var quitTried = false; + this.quitTried = false; + + + term.on('SIGINT', function () { + self.tryQuit(); + }); - function tryQuit() { - if (quitTried) return; - quitTried = true; - killChild(); - term.close(); - process.exit(0); - } - term.on('SIGINT', tryQuit); - term.on('close', tryQuit); + term.on('close', function () { + self.tryQuit(); + }); term.on('line', function(cmd) { + // trim whitespace cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, ''); + self.handleCommand(cmd); + }); +} - if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') { - tryQuit(); - - } else if (/^r(un)?/.test(cmd)) { - if (child) { - restartQuestion(function (yes) { - if (!yes) { - term.prompt(); - } else { - console.log("restarting..."); - trySpawn(function () { - c.reqContinue(); - }); - } - }); - } else { - trySpawn(function () { - c.reqContinue(); - }); - } - } else if (/^help/.test(cmd)) { - console.log(helpMessage); - term.prompt(); +Interface.prototype.tryQuit = function() { + if (this.quitTried) return; + this.quitTried = true; + this.killChild(); + this.term.close(); + process.exit(0); +}; - } else if ('version' == cmd) { - if (!c) { - printNotConnected(); - return; - } - c.reqVersion(function (v) { - console.log(v); - term.prompt(); - }); - } else if (/info +breakpoints/.test(cmd)) { - if (!c) { - printNotConnected(); - return; +Interface.prototype.handleBreak = function(r) { + var result = ''; + if (r.breakpoints) { + result += 'breakpoint'; + if (r.breakpoints.length > 1) { + result += 's'; + } + result += ' #'; + for (var i = 0; i < r.breakpoints.length; i++) { + if (i > 0) { + result += ', #'; } - c.listbreakpoints(function (res) { - console.log(res); - term.prompt(); - }); + result += r.breakpoints[i]; + } + } else { + result += 'break'; + } + result += ' in '; + result += r.invocationText; + result += ', '; + result += SourceInfo(r); + result += '\n'; + result += SourceUnderline(r.sourceLineText, r.sourceColumn); - } else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) { - if (!c) { - printNotConnected(); - return; - } - c.reqBacktrace(function (bt) { - if (/full/.test(cmd)) { - console.log(bt); - } else if (bt.totalFrames == 0) { - console.log('(empty stack)'); + this.client.currentSourceLine = r.sourceLine; + this.client.currentFrame = 0; + this.client.currentScript = r.script.name; + + console.log(result); + + this.term.prompt(); +}; + + +Interface.prototype.handleCommand = function(cmd) { + var self = this; + + var client = this.client; + var term = this.term; + + if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') { + self.tryQuit(); + + } else if (/^r(un)?/.test(cmd)) { + if (self.child) { + self.restartQuestion(function (yes) { + if (!yes) { + term.prompt(); } else { - var result = ''; - for (j = 0; j < bt.frames.length; j++) { - if (j != 0) result += '\n'; - result += bt.frames[j].text; - } - console.log(result); + console.log("restarting..."); + self.trySpawn(); } - term.prompt(); }); + } else { + self.trySpawn(); + } - } else if (cmd == 'scripts' || cmd == 'scripts full') { - if (!c) { - printNotConnected(); - return; - } - printScripts(); + } else if (/^help/.test(cmd)) { + console.log(helpMessage); + term.prompt(); + + } else if ('version' == cmd) { + if (!client) { + self.printNotConnected(); + return; + } + client.reqVersion(function (v) { + console.log(v); term.prompt(); + }); - } else if (/^continue/.test(cmd) || /^c/.test(cmd)) { - if (!c) { - printNotConnected(); - return; - } - c.reqContinue(function (res) { - // Wait for break point. (disable raw mode?) - }); + } else if (/info +breakpoints/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.listbreakpoints(function (res) { + console.log(res); + term.prompt(); + }); - } else if (/^next/.test(cmd) || /^n/.test(cmd)) { - if (!c) { - printNotConnected(); - return; + } else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.reqBacktrace(function (bt) { + if (/full/.test(cmd)) { + console.log(bt); + } else if (bt.totalFrames == 0) { + console.log('(empty stack)'); + } else { + var result = ''; + for (j = 0; j < bt.frames.length; j++) { + if (j != 0) result += '\n'; + result += bt.frames[j].text; + } + console.log(result); } - c.step('next', 1, function (res) { - // Wait for break point. (disable raw mode?) - }); + term.prompt(); + }); - } else if (/^step/.test(cmd) || /^s/.test(cmd)) { - if (!c) { - printNotConnected(); - return; - } - c.step('in', 1, function (res) { - // Wait for break point. (disable raw mode?) - }); + } else if (cmd == 'scripts' || cmd == 'scripts full') { + if (!client) { + self.printNotConnected(); + return; + } + self.printScripts(); + term.prompt(); - } else if (/^print/.test(cmd) || /^p/.test(cmd)) { - if (!c) { - printNotConnected(); - return; - } - var i = cmd.indexOf(' '); - if (i < 0) { - console.log("print [expression]"); + } else if (/^c(ontinue)?/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.reqContinue(function (res) { + // Wait for break point. (disable raw mode?) + }); + + } else if (/^next/.test(cmd) || /^n/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.step('next', 1, function (res) { + // Wait for break point. (disable raw mode?) + }); + + } else if (/^step/.test(cmd) || /^s/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.step('in', 1, function (res) { + // Wait for break point. (disable raw mode?) + }); + + } else if (/^print/.test(cmd) || /^p/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + var i = cmd.indexOf(' '); + if (i < 0) { + console.log("print [expression]"); + term.prompt(); + } else { + cmd = cmd.slice(i); + client.reqEval(cmd, function (res) { + if (res) { + console.log(res.text); + } else { + console.log(res); + } term.prompt(); - } else { - cmd = cmd.slice(i); - c.reqEval(cmd, function (res) { - if (res) { - console.log(res.text); - } else { - console.log(res); - } - term.prompt(); - }); - } + }); + } + + } else { + if (!/^\s*$/.test(cmd)) { + // If it's not all white-space print this error message. + console.log('Unknown command "%s". Try "help"', cmd); + } + term.prompt(); + } +}; + + +var restartQuestionPrompt = "The program being debugged has " + + "been started already.\n" + + "Start it from the beginning? (y or n): "; +Interface.prototype.restartQuestion = function(cb) { + var self = this; + this.term.question(restartQuestionPrompt, function (answer) { + if (/^y(es)?$/i.test(answer)) { + cb(true); + } else if (/^n(o)?$/i.test(answer)) { + cb(false); } else { - if (!/^\s*$/.test(cmd)) { - // If it's not all white-space print this error message. - console.log('Unknown command "%s". Try "help"', cmd); - } - term.prompt(); + console.log("Please answer y or n."); + self.restartQuestion(cb); } }); -} +}; + + +Interface.prototype.killChild = function() { + if (this.client) { + this.client.destroy(); + this.client = null; + } + + if (this.child) { + this.child.kill(); + this.child = null; + } +}; + + +Interface.prototype.trySpawn = function(cb) { + this.killChild(); + + this.child = spawn(process.execPath, args, { customFds: [0, 1, 2] }); + + var self = this; + setTimeout(function () { + process.stdout.write("connecting..."); + var client = self.client = new Client(); + client.connect(exports.port); + + client.once('ready', function () { + process.stdout.write("ok\r\n"); + + // since we did debug-brk, we're hitting a break point immediately + // continue before anything else. + client.reqContinue(); + + if (cb) cb(); + }); + + client.on('close', function () { + console.log("\nprogram terminated"); + self.client = null; + self.killChild(); + self.term.prompt(); + }); + + client.on('unhandledResponse', function (res) { + console.log("\r\nunhandled res:"); + console.log(res); + self.term.prompt(); + }); + + client.on('break', function (res) { + self.handleBreak(res.body); + }); + }, 100); +}; + + +Interface.prototype.printNotConnected = function() { + console.log("Program not running. Try 'run'."); + this.term.prompt(); +}; + + +Interface.prototype.printScripts = function() { + var client = this.client; + var text = ''; + for (var id in client.scripts) { + var script = client.scripts[id]; + if (typeof script == 'object' && script.name) { + text += script.name == client.currentScript ? '* ' : ' '; + text += script.name + '\n'; + } + } + process.stdout.write(text); +}; + + + -- 2.7.4