Browse Source

[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).
Fedor Indutny 13 years ago
parent
commit
3b2577b4fe
  1. 163
      lib/_debugger.js

163
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);
};

Loading…
Cancel
Save