mirror of https://github.com/nodejs/node.git
[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).pull/22966/head
parent
e01635eb9b
commit
3b2577b4fe
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…
Reference in New Issue