From 3b2577b4fef924fde7b24f45ff98aff51183c9b7 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 7 Sep 2011 21:29:01 +0700 Subject: [PATCH] [debugger] restructurize code, eval control repl asynchronously Move commands closer to each other, use .debugEval and .controlEval for controlling repl output (no more incorrect 'debug>' prints). --- lib/_debugger.js | 163 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 58 deletions(-) diff --git a/lib/_debugger.js b/lib/_debugger.js index f79f42b..c7ba428 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -589,6 +589,12 @@ var helpMessage = 'Commands: ' + commands.join(', '); function SourceUnderline(sourceText, position) { if (!sourceText) return; + var wrapper = require('module').wrapper[0]; + if (sourceText.indexOf(wrapper) === 0) { + sourceText = sourceText.slice(wrapper.length); + position -= wrapper.length; + } + // 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. @@ -623,7 +629,6 @@ function SourceInfo(body) { return result; } - // This class is the repl-enabled debugger interface which is invoked on // "node debug" function Interface() { @@ -634,14 +639,14 @@ function Interface() { self.killChild(); }); - this.stdin = process.openStdin(); - this.repl = new repl.REPLServer('debug> '); + this.repl.eval = this.controlEval.bind(this); // Lift all instance methods to repl context var proto = Interface.prototype, ignored = ['pause', 'resume', 'exitRepl', 'handleBreak', - 'requireConnection', 'killChild', 'trySpawn']; + 'requireConnection', 'killChild', 'trySpawn', + 'controlEval', 'debugEval']; for (var i in proto) { if (proto.hasOwnProperty(i) && ignored.indexOf(i) === -1) { @@ -649,29 +654,37 @@ function Interface() { } } - this.quitting = false; - this.debugRepl = false; + this.waiting = null; this.paused = 0; this.context = this.repl.context; }; + +// Stream control + + Interface.prototype.pause = function() { if (this.paused++ > 0) return false; - this.stdin.pause(); this.repl.rli.pause(); + process.stdin.pause(); }; Interface.prototype.resume = function() { if (this.paused === 0 || --this.paused !== 0) return false; - this.stdin.resume(); this.repl.rli.resume(); - process.stdout.write('\n'); this.repl.displayPrompt(); + process.stdin.resume(); + + if (this.waiting) { + this.waiting(); + this.waiting = null; + } }; + Interface.prototype.handleBreak = function(r) { this.pause(); - var result = ''; + var result = '\n'; if (r.breakpoints) { result += 'breakpoint'; if (r.breakpoints.length > 1) { @@ -698,11 +711,58 @@ Interface.prototype.handleBreak = function(r) { this.client.currentFrame = 0; this.client.currentScript = r.script.name; - process.stdout.write(result); + console.log(result); this.resume(); }; +Interface.prototype.requireConnection = function() { + if (!this.client) throw Error('App isn\'t running... Try `run()` instead'); +}; + +Interface.prototype.controlEval = function(code, context, filename, callback) { + try { + var result = vm.runInContext(code, context, filename); + if (this.paused === 0) return callback(null, result); + this.waiting = function() { + callback(null, result); + }; + } catch (e) { + callback(e); + } +}; + +Interface.prototype.debugEval = function(code, context, filename, callback) { + var client = this.client; + + if (code === '.scope') { + client.reqScopes(callback); + return; + } + + client.reqEval(code, function(res) { + if (!res.success) { + if (res.message) { + if (/SyntaxError/.test(res.message)) { + callback(new SyntaxError(res.message)); + } else { + callback(new Error(res.message)); + } + } else { + callback(null); + } + return; + } + + client.mirrorObject(res.body, function(mirror) { + callback(null, mirror); + }); + }); +}; + + +// Commands + function intChars(n) { // TODO dumb: if (n < 50) { @@ -727,12 +787,16 @@ function leftPad(n) { return s; } + +// Print help message Interface.prototype.help = function() { this.pause(); process.stdout.write(helpMessage); this.resume(); }; + +// Run script Interface.prototype.run = function() { if (this.child) { throw Error('App is already running... Try `restart()` instead'); @@ -741,6 +805,8 @@ Interface.prototype.run = function() { } }; + +// Restart script Interface.prototype.restart = function() { if (!this.child) throw Error('App isn\'t running... Try `run()` instead'); @@ -754,11 +820,8 @@ Interface.prototype.restart = function() { }, 1000); }; -Interface.prototype.requireConnection = function() { - if (!this.client) throw Error('App isn\'t running... Try `run()` instead'); -}; - +// Print version Interface.prototype.version = function() { this.requireConnection(); var self = this; @@ -770,6 +833,7 @@ Interface.prototype.version = function() { }); }; +// List source code Interface.prototype.list = function() { this.requireConnection(); @@ -808,6 +872,7 @@ Interface.prototype.list = function() { }); }; +// Print backtrace Interface.prototype.backtrace = function() { this.requireConnection(); @@ -834,12 +899,13 @@ Interface.prototype.backtrace = function() { text += '\n'; } - process.stdout.write(text); + console.log(text); } self.resume(); }); }; + // argument full tells if it should display internal node scripts or not Interface.prototype.scripts = function(displayNatives) { this.requireConnection(); @@ -859,10 +925,12 @@ Interface.prototype.scripts = function(displayNatives) { } } } - process.stdout.write(text); + console.log(text); this.resume(); }; + +// Continue execution of script Interface.prototype.cont = function() { this.requireConnection(); this.pause(); @@ -873,6 +941,8 @@ Interface.prototype.cont = function() { }); }; + +// Jump to next command Interface.prototype.next = function() { this.requireConnection(); @@ -884,6 +954,8 @@ Interface.prototype.next = function() { }); }; + +// Step in Interface.prototype.step = function() { this.requireConnection(); @@ -895,6 +967,8 @@ Interface.prototype.step = function() { }); }; + +// Step out Interface.prototype.out = function() { this.requireConnection(); @@ -906,6 +980,8 @@ Interface.prototype.out = function() { }); }; + +// Show breakpoints Interface.prototype.breakpoints = function() { this.requireConnection(); this.pause(); @@ -920,53 +996,27 @@ Interface.prototype.breakpoints = function() { }); }; + +// Kill child process Interface.prototype.kill = function() { if (!this.child) return; this.killChild(); }; + +// Activate debug repl Interface.prototype.repl = function() { - if (!this.child) throw Error('App isn\'t running... Try `run()` instead'); + this.requireConnection(); var self = this; - this.debugRepl = true; - + // Exit debug repl on Ctrl + C this.repl.rli.once('SIGINT', function() { self.exitRepl(); }); - var client = this.client; - - // Save old eval - this.repl._eval = this.repl.eval; - // Set new - this.repl.eval = function(code, context, filename, callback) { - if (code === '.scope') { - client.reqScopes(callback); - return; - } - - client.reqEval(code, function(res) { - if (!res.success) { - if (res.message) { - if (/SyntaxError/.test(res.message)) { - callback(new SyntaxError(res.message)); - } else { - callback(new Error(res.message)); - } - } else { - callback(null); - } - return; - } - - client.mirrorObject(res.body, function(mirror) { - callback(null, mirror); - }); - }); - }; + this.repl.eval = this.debugEval.bind(this); this.repl.context = {}; this.repl.prompt = '> '; @@ -974,9 +1024,11 @@ Interface.prototype.repl = function() { this.repl.displayPrompt(); }; + +// Exit debug repl Interface.prototype.exitRepl = function() { - this.debugRepl = false; - this.repl.eval = this.repl._eval; + this.repl.eval = this.controlEval.bind(this); + this.repl.context = this.context; this.repl.prompt = 'debug> '; this.repl.rli.setPrompt('debug> '); @@ -1013,7 +1065,7 @@ Interface.prototype.trySpawn = function(cb) { var connectionAttempts = 0; client.once('ready', function() { - process.stdout.write(' ok'); + process.stdout.write(' ok\n'); // since we did debug-brk, we're hitting a break point immediately // continue before anything else. @@ -1062,8 +1114,3 @@ Interface.prototype.trySpawn = function(cb) { attemptConnect(); }, 50); }; - - - - - -- 2.7.4