From c8ffbcd9f1a182bd16bbe664197b4b8d9d4d859f Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 20 Sep 2011 15:30:39 +0700 Subject: [PATCH] added comments, spawn child process automatically at debugger's start --- lib/_debugger.js | 63 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/lib/_debugger.js b/lib/_debugger.js index 69970bf3082..9e1c5844cd7 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -697,19 +697,22 @@ function Interface() { var self = this, child; - process.on('exit', function() { - self.killChild(); - }); - + // Two eval modes are available: controlEval and debugEval + // But controlEval is used by default this.repl = new repl.REPLServer('debug> ', null, this.controlEval.bind(this)); + // Kill child process when repl closed or main process is dead this.repl.rli.addListener('close', function() { self.killed = true; self.killChild(); }); - // Lift all instance methods to repl context + process.on('exit', function() { + self.killChild(); + }); + + var proto = Interface.prototype, ignored = ['pause', 'resume', 'exitRepl', 'handleBreak', 'requireConnection', 'killChild', 'trySpawn', @@ -737,6 +740,8 @@ function Interface() { } }; + // Copy all prototype methods in repl context + // Setup them as getters if possible for (var i in proto) { if (proto.hasOwnProperty(i) && ignored.indexOf(i) === -1) { defineProperty(i, i); @@ -753,6 +758,9 @@ function Interface() { control: [] }; this.breakpoints = []; + + // Run script automatically + this.run(); }; @@ -780,7 +788,7 @@ Interface.prototype.resume = function(silent) { }; -// Output +// Print text to output stream Interface.prototype.print = function(text) { if (this.killed) return; if (process.stdout.isTTY) { @@ -791,6 +799,7 @@ Interface.prototype.print = function(text) { process.stdout.write('\n'); }; +// Format and print text from child process Interface.prototype.childPrint = function(text) { this.print(text.toString().split(/\r\n|\r|\n/g).filter(function(chunk) { return chunk; @@ -800,26 +809,35 @@ Interface.prototype.childPrint = function(text) { this.repl.displayPrompt(); }; +// Errors formatting Interface.prototype.error = function(text) { this.print(text); this.resume(); }; + +// Debugger's `break` event handler Interface.prototype.handleBreak = function(r) { this.pause(); + // Save execution context's data this.client.currentSourceLine = r.sourceLine; this.client.currentSourceLineText = r.sourceLineText; this.client.currentSourceColumn = r.sourceColumn; this.client.currentFrame = 0; this.client.currentScript = r.script.name; + // Print break data this.print(SourceInfo(r)); + + // And list source this.list(2); + this.resume(); }; +// Internal method for checking connection state Interface.prototype.requireConnection = function() { if (!this.client) { this.error('App isn\'t running... Try `run` instead'); @@ -828,11 +846,20 @@ Interface.prototype.requireConnection = function() { return true; }; + +// Evals + +// Used for debugger's commands evaluation and execution Interface.prototype.controlEval = function(code, context, filename, callback) { try { var result = vm.runInContext(code, context, filename); + // Repl should not ask for next command + // if current one was asynchronous. if (this.paused === 0) return callback(null, result); + + // Add a callback for asynchronous command + // (it will be automatically invoked by .resume() method this.waiting = function() { callback(null, result); }; @@ -841,22 +868,22 @@ Interface.prototype.controlEval = function(code, context, filename, callback) { } }; +// Used for debugger's remote evaluation (`repl`) commands Interface.prototype.debugEval = function(code, context, filename, callback) { var self = this, client = this.client; + // Repl asked for scope variables if (code === '.scope') { client.reqScopes(callback); return; } - var frame; - - if (client.currentFrame === NO_FRAME) { - frame = NO_FRAME; - }; + var frame = client.currentFrame === NO_FRAME ? frame : undefined; self.pause(); + + // Request remote evaluation globally or in current frame client.reqFrameEval(code, frame, function(res) { if (!res.success) { if (res.message) { @@ -868,6 +895,7 @@ Interface.prototype.debugEval = function(code, context, filename, callback) { return; } + // Request object by handles (and it's sub-properties) client.mirrorObject(res.body, 3, function(mirror) { callback(null, mirror); self.resume(true); @@ -876,8 +904,9 @@ Interface.prototype.debugEval = function(code, context, filename, callback) { }; -// Commands +// Utils +// Returns number of digits (+1) function intChars(n) { // TODO dumb: if (n < 50) { @@ -891,7 +920,7 @@ function intChars(n) { } } - +// Adds spaces and prefix to number function leftPad(n, prefix) { var s = n.toString(); var nchars = intChars(n); @@ -903,6 +932,9 @@ function leftPad(n, prefix) { } +// Commands + + // Print help message Interface.prototype.help = function() { this.print(helpMessage); @@ -1028,7 +1060,8 @@ Interface.prototype.backtrace = function() { }; -// argument full tells if it should display internal node scripts or not +// First argument tells if it should display internal node scripts or not +// (available only for internal debugger's functions) Interface.prototype.scripts = function() { if (!this.requireConnection()) return; @@ -1281,6 +1314,7 @@ Interface.prototype.exitRepl = function() { }; +// Kills child process Interface.prototype.killChild = function() { if (this.child) { this.child.kill(); @@ -1297,6 +1331,7 @@ Interface.prototype.killChild = function() { }; +// Spawns child process (and restores breakpoints) Interface.prototype.trySpawn = function(cb) { var self = this, breakpoints = this.breakpoints || [];