|
|
@ -20,7 +20,7 @@ |
|
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
var net = require('net'); |
|
|
|
var readline = require('readline'); |
|
|
|
var repl = require('repl'); |
|
|
|
var inherits = require('util').inherits; |
|
|
|
var spawn = require('child_process').spawn; |
|
|
|
|
|
|
@ -597,19 +597,11 @@ function SourceInfo(body) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// This class is the readline-enabled debugger interface which is invoked on
|
|
|
|
// This class is the repl-enabled debugger interface which is invoked on
|
|
|
|
// "node debug"
|
|
|
|
function Interface() { |
|
|
|
var self = this; |
|
|
|
var child; |
|
|
|
var client; |
|
|
|
|
|
|
|
function complete(line) { |
|
|
|
return self.complete(line); |
|
|
|
} |
|
|
|
|
|
|
|
var term = readline.createInterface(process.stdin, process.stdout, complete); |
|
|
|
this.term = term; |
|
|
|
var self = this, |
|
|
|
child; |
|
|
|
|
|
|
|
process.on('exit', function() { |
|
|
|
self.killChild(); |
|
|
@ -617,104 +609,45 @@ function Interface() { |
|
|
|
|
|
|
|
this.stdin = process.openStdin(); |
|
|
|
|
|
|
|
term.setPrompt('debug> '); |
|
|
|
term.prompt(); |
|
|
|
|
|
|
|
this.quitting = false; |
|
|
|
|
|
|
|
process.on('SIGINT', function() { |
|
|
|
self.handleSIGINT(); |
|
|
|
}); |
|
|
|
|
|
|
|
term.on('SIGINT', function() { |
|
|
|
self.handleSIGINT(); |
|
|
|
}); |
|
|
|
|
|
|
|
term.on('attemptClose', function() { |
|
|
|
self.tryQuit(); |
|
|
|
}); |
|
|
|
|
|
|
|
term.on('line', function(cmd) { |
|
|
|
// trim whitespace
|
|
|
|
cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, ''); |
|
|
|
|
|
|
|
if (cmd.length) { |
|
|
|
self._lastCommand = cmd; |
|
|
|
self.handleCommand(cmd); |
|
|
|
} else { |
|
|
|
self.handleCommand(self._lastCommand); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
this.repl = repl.start('debug> '); |
|
|
|
|
|
|
|
Interface.prototype.complete = function(line) { |
|
|
|
// Match me with a command.
|
|
|
|
var matches = []; |
|
|
|
// Remove leading whitespace
|
|
|
|
line = line.replace(/^\s*/, ''); |
|
|
|
// Lift all instance methods to repl context
|
|
|
|
var proto = Interface.prototype, |
|
|
|
ignored = ['pause', 'resume', 'handleSIGINT']; |
|
|
|
|
|
|
|
for (var i = 0; i < commands.length; i++) { |
|
|
|
if (commands[i].indexOf(line) === 0) { |
|
|
|
matches.push(commands[i]); |
|
|
|
for (var i in proto) { |
|
|
|
if (proto.hasOwnProperty(i) && ignored.indexOf(i) === -1) { |
|
|
|
this.repl.context[i] = proto[i].bind(this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return [matches, line]; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.handleSIGINT = function() { |
|
|
|
if (this.paused) { |
|
|
|
this.child.kill('SIGINT'); |
|
|
|
} else { |
|
|
|
this.tryQuit(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.quit = function() { |
|
|
|
if (this.quitting) return; |
|
|
|
this.quitting = true; |
|
|
|
this.killChild(); |
|
|
|
this.term.close(); |
|
|
|
process.exit(0); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.tryQuit = function() { |
|
|
|
var self = this; |
|
|
|
this.quitting = false; |
|
|
|
this.paused = 0; |
|
|
|
|
|
|
|
if (self.child) { |
|
|
|
self.quitQuestion(function(yes) { |
|
|
|
if (yes) { |
|
|
|
self.quit(); |
|
|
|
} else { |
|
|
|
self.term.prompt(); |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
self.quit(); |
|
|
|
} |
|
|
|
process.on('SIGINT', function() { |
|
|
|
self.handleSIGINT(); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.pause = function() { |
|
|
|
this.paused = true; |
|
|
|
if (this.paused++ > 0) return false; |
|
|
|
this.stdin.pause(); |
|
|
|
this.term.pause(); |
|
|
|
this.repl.rli.pause(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.resume = function() { |
|
|
|
if (!this.paused) return false; |
|
|
|
this.paused = false; |
|
|
|
if (this.paused === 0 || --this.paused !== 0) return false; |
|
|
|
this.stdin.resume(); |
|
|
|
this.term.resume(); |
|
|
|
this.term.prompt(); |
|
|
|
return true; |
|
|
|
this.repl.rli.resume(); |
|
|
|
process.stdout.write('\n'); |
|
|
|
this.repl.displayPrompt(); |
|
|
|
}; |
|
|
|
|
|
|
|
Interface.prototype.handleSIGINT = function() { |
|
|
|
this.child.kill('SIGINT'); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.handleBreak = function(r) { |
|
|
|
var result = ''; |
|
|
@ -745,8 +678,6 @@ Interface.prototype.handleBreak = function(r) { |
|
|
|
this.client.currentScript = r.script.name; |
|
|
|
|
|
|
|
console.log(result); |
|
|
|
|
|
|
|
if (!this.resume()) this.term.prompt(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -774,6 +705,46 @@ function leftPad(n) { |
|
|
|
return s; |
|
|
|
} |
|
|
|
|
|
|
|
Interface.prototype.help = function() { |
|
|
|
this.pause(); |
|
|
|
process.stdout.write(helpMessage); |
|
|
|
this.resume(); |
|
|
|
}; |
|
|
|
|
|
|
|
Interface.prototype.run = function() { |
|
|
|
if (this.child) { |
|
|
|
throw Error('App is already running... Try `restart()` instead'); |
|
|
|
} else { |
|
|
|
this.trySpawn(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Interface.prototype.restart = function() { |
|
|
|
if (!this.child) throw Error('App isn\'t running... Try `run()` instead'); |
|
|
|
|
|
|
|
var self = this; |
|
|
|
|
|
|
|
this.killChild(); |
|
|
|
|
|
|
|
// XXX need to wait a little bit for the restart to work?
|
|
|
|
setTimeout(function() { |
|
|
|
self.trySpawn(); |
|
|
|
}, 1000); |
|
|
|
}; |
|
|
|
|
|
|
|
Interface.prototype.version = function() { |
|
|
|
if (!this.child) throw Error('App isn\'t running... Try `run()` instead'); |
|
|
|
|
|
|
|
var self = this; |
|
|
|
|
|
|
|
this.pause(); |
|
|
|
this.client.reqVersion(function(v) { |
|
|
|
process.stdout.write(v); |
|
|
|
self.resume(); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.handleCommand = function(cmd) { |
|
|
|
var self = this; |
|
|
@ -786,29 +757,9 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
self.tryQuit(); |
|
|
|
|
|
|
|
} else if (/^r(un)?/.test(cmd)) { |
|
|
|
self._lastCommand = null; |
|
|
|
if (self.child) { |
|
|
|
self.restartQuestion(function(yes) { |
|
|
|
if (!yes) { |
|
|
|
self._lastCommand = null; |
|
|
|
term.prompt(); |
|
|
|
} else { |
|
|
|
console.log('restarting...'); |
|
|
|
self.killChild(); |
|
|
|
// XXX need to wait a little bit for the restart to work?
|
|
|
|
setTimeout(function() { |
|
|
|
self.trySpawn(); |
|
|
|
}, 1000); |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
self.trySpawn(); |
|
|
|
} |
|
|
|
|
|
|
|
// DONE
|
|
|
|
} else if (/^help/.test(cmd)) { |
|
|
|
console.log(helpMessage); |
|
|
|
term.prompt(); |
|
|
|
|
|
|
|
// DONE
|
|
|
|
} else if ('version' == cmd) { |
|
|
|
if (!client) { |
|
|
|
self.printNotConnected(); |
|
|
@ -816,7 +767,6 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
} |
|
|
|
client.reqVersion(function(v) { |
|
|
|
console.log(v); |
|
|
|
term.prompt(); |
|
|
|
}); |
|
|
|
|
|
|
|
} else if (/info +breakpoints/.test(cmd)) { |
|
|
@ -826,7 +776,6 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
} |
|
|
|
client.listbreakpoints(function(res) { |
|
|
|
console.log(res); |
|
|
|
term.prompt(); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
@ -864,7 +813,6 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
console.log(leftPad(lineno) + ' ' + lines[i]); |
|
|
|
} |
|
|
|
} |
|
|
|
term.prompt(); |
|
|
|
}); |
|
|
|
|
|
|
|
} else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) { |
|
|
@ -894,7 +842,6 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
|
|
|
|
console.log(text); |
|
|
|
} |
|
|
|
term.prompt(); |
|
|
|
}); |
|
|
|
|
|
|
|
} else if (cmd == 'scripts' || cmd == 'scripts full') { |
|
|
@ -903,7 +850,6 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
return; |
|
|
|
} |
|
|
|
self.printScripts(cmd.indexOf('full') > 0); |
|
|
|
term.prompt(); |
|
|
|
|
|
|
|
} else if (/^c(ontinue)?/.test(cmd)) { |
|
|
|
if (!client) { |
|
|
@ -931,7 +877,6 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
self.term.prompt(); |
|
|
|
} |
|
|
|
|
|
|
|
} else if (/^next/.test(cmd) || /^n/.test(cmd)) { |
|
|
@ -960,19 +905,16 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
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.success) { |
|
|
|
console.log(res.message); |
|
|
|
term.prompt(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
client.mirrorObject(res.body, function(mirror) { |
|
|
|
console.log(mirror); |
|
|
|
term.prompt(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
@ -982,45 +924,11 @@ Interface.prototype.handleCommand = function(cmd) { |
|
|
|
// If it's not all white-space print this error message.
|
|
|
|
console.log('Unknown command "%s". Try "help"', cmd); |
|
|
|
} |
|
|
|
term.prompt(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.yesNoQuestion = function(prompt, cb) { |
|
|
|
var self = this; |
|
|
|
self.resume(); |
|
|
|
this.term.question(prompt, 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.'); |
|
|
|
self.yesNoQuestion(prompt, cb); |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.restartQuestion = function(cb) { |
|
|
|
this.yesNoQuestion('The program being debugged has been started already.\n' + |
|
|
|
'Start it from the beginning? (y or n) ', cb); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.killQuestion = function(cb) { |
|
|
|
this.yesNoQuestion('Kill the program being debugged? (y or n) ', cb); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.quitQuestion = function(cb) { |
|
|
|
this.yesNoQuestion('A debugging session is active. Quit anyway? (y or n) ', |
|
|
|
cb); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.killChild = function() { |
|
|
|
if (this.child) { |
|
|
|
this.child.kill(); |
|
|
@ -1050,26 +958,25 @@ Interface.prototype.trySpawn = function(cb) { |
|
|
|
var connectionAttempts = 0; |
|
|
|
|
|
|
|
client.once('ready', function() { |
|
|
|
process.stdout.write(' ok\r\n'); |
|
|
|
process.stdout.write(' ok'); |
|
|
|
|
|
|
|
// since we did debug-brk, we're hitting a break point immediately
|
|
|
|
// continue before anything else.
|
|
|
|
client.reqContinue(function() { |
|
|
|
self.resume(); |
|
|
|
if (cb) cb(); |
|
|
|
}); |
|
|
|
|
|
|
|
client.on('close', function() { |
|
|
|
console.log('\nprogram terminated'); |
|
|
|
console.log('program terminated'); |
|
|
|
self.client = null; |
|
|
|
self.killChild(); |
|
|
|
if (!self.quitting) self.term.prompt(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
client.on('unhandledResponse', function(res) { |
|
|
|
console.log('\r\nunhandled res:'); |
|
|
|
console.log(res); |
|
|
|
self.term.prompt(); |
|
|
|
}); |
|
|
|
|
|
|
|
client.on('break', function(res) { |
|
|
@ -1098,12 +1005,6 @@ Interface.prototype.trySpawn = function(cb) { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.printNotConnected = function() { |
|
|
|
console.log("Program not running. Try 'run'."); |
|
|
|
this.term.prompt(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// argument full tells if it should display internal node scripts or not
|
|
|
|
Interface.prototype.printScripts = function(displayNatives) { |
|
|
|
var client = this.client; |
|
|
|