node-inspector-sans-ws
Version:
Web Inspector based nodeJS debugger
113 lines (99 loc) • 3.56 kB
JavaScript
var convert = require('./convert'),
CallFramesProvider = require('./CallFramesProvider').CallFramesProvider;
/**
* @param {Object} config
* @param {FrontendClient} frontendClient
* @param {DebuggerClient} debuggerClient
* @param {ScriptManager} scriptManager
* @constructor
*/
function BreakEventHandler(config, frontendClient, debuggerClient, scriptManager) {
this._config = config;
this._frontendClient = frontendClient;
this._debuggerClient = debuggerClient;
this._scriptManager = scriptManager;
this._callFramesProvider = new CallFramesProvider(config, debuggerClient);
this._registerDebuggerEventHandlers();
}
var callbackForNextBreak;
Object.defineProperties(BreakEventHandler.prototype, {
/** @type {Number} */
continueToLocationBreakpointId: {
writable: true,
value: null
},
/** @type {function(eventData)} */
callbackForNextBreak: {
get: function() { return callbackForNextBreak; },
set: function(value) {
if (value && callbackForNextBreak)
throw new Error('Cannot set multiple callbacks for the next break.');
callbackForNextBreak = value;
}
}
});
BreakEventHandler.prototype._registerDebuggerEventHandlers = function() {
this._debuggerClient.on('break', this._onBreak.bind(this));
this._debuggerClient.on('exception', this._onBreak.bind(this));
};
BreakEventHandler.prototype._onBreak = function(obj) {
var scriptId = obj.script.id,
hitBreakpoints = obj.breakpoints,
source = this._scriptManager.findScriptByID(scriptId);
// Source is undefined when the breakpoint was in code eval()-ed via
// console or eval()-ed internally by node inspector.
// We could send backtrace in such case, but that isn't working well now.
// V8 is reusing the same scriptId for multiple eval() calls and DevTools
// front-end does not update the displayed source code when a content
// of a script changes.
// The following solution - ignore the breakpoint and resume the
// execution - should be good enough in most cases.
if (!source || source.hidden) {
this._debuggerClient.request('continue', { stepaction: 'out' });
return;
}
if (this.callbackForNextBreak) {
var callback = this.callbackForNextBreak;
this.callbackForNextBreak = null;
callback(obj);
return;
}
if (this.continueToLocationBreakpointId !== null) {
this._debuggerClient.clearBreakpoint(
this.continueToLocationBreakpointId,
function(err, result) {
if (err)
this._frontendClient.sendLogToConsole('warning', err);
else
this.continueToLocationBreakpointId = null;
}.bind(this)
);
}
this.sendBacktraceToFrontend(obj.exception, hitBreakpoints);
};
/**
* @param {function(error, response)} callback
*/
BreakEventHandler.prototype.fetchCallFrames = function(callback) {
this._callFramesProvider.fetchCallFrames(callback);
};
/**
* @param {Object} exception
* @param {Array.<number>} hitBreakpoints
*/
BreakEventHandler.prototype.sendBacktraceToFrontend = function(exception, hitBreakpoints) {
this.fetchCallFrames(function(error, result) {
if (error)
this._frontendClient.sendLogToConsole('error', error);
else
this._frontendClient.sendEvent(
'Debugger.paused',
{
callFrames: result,
reason: exception ? 'exception' : 'other',
data: exception ? convert.v8RefToInspectorObject(exception) : null,
hitBreakpoints: hitBreakpoints
});
}.bind(this));
};
exports.BreakEventHandler = BreakEventHandler;