added comments, spawn child process automatically at debugger's start

pull/22966/head
Fedor Indutny 2011-09-20 15:30:39 +07:00 committed by Ryan Dahl
parent 4ff15512f5
commit c8ffbcd9f1
1 changed files with 49 additions and 14 deletions

View File

@ -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 || [];