atom-nuclide
Version:
A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.
171 lines (144 loc) • 4.23 kB
JavaScript
var EventEmitter = require('events').EventEmitter,
inherits = require('util').inherits,
async = require('async'),
debug = require('debug')('node-inspector:injector');
// NM is NativeModule
var FN_WITH_SCOPED_NM = 'Object.getOwnPropertyDescriptor(global, "console").get';
/**
* @param {{inject}} config
* @param {DebuggerClient} debuggerClient
* @constructor
*/
function InjectorClient(config, session) {
this._noInject = config.inject === false;
this._injected = false;
this._appPausedByInjector = false;
this._debuggerClient = session.debuggerClient;
this._frontendClient = session.frontendClient;
this._debuggerClient.on('close', this.close.bind(this));
}
inherits(InjectorClient, EventEmitter);
Object.defineProperties(InjectorClient.prototype, {
/** @type {boolean} */
needsInject: {
get: function() {
return !this._noInject && !this._injected;
}
}
});
/**
* @param {string} sourceLine
* @type {boolean}
*/
InjectorClient.prototype.tryHandleDebuggerBreak = function(sourceLine, done) {
return done(this._appPausedByInjector);
};
/**
*/
InjectorClient.prototype.inject = function(cb) {
if (typeof cb !== 'function')
cb = function(error, result) {};
var _water = [];
if (this.needsInject) {
_water.unshift(
this._getFuncWithNMInScope.bind(this),
this._findNMInScope.bind(this),
this._inject.bind(this),
this._onInjection.bind(this)
);
}
if (this.needsInject && this._debuggerClient.isRunning) {
_water.unshift(this._pause.bind(this));
_water.push(this._resume.bind(this));
}
async.waterfall(_water, function(error) {
if (error)
this._frontendClient.sendLogToConsole('error', error.toString());
cb(error);
}.bind(this));
};
InjectorClient.prototype._pause = function(cb) {
this._appPausedByInjector = true;
this._debuggerClient.request('suspend', {}, function() {
cb();
});
};
InjectorClient.prototype._resume = function(cb) {
this._debuggerClient.request('continue', undefined, function() {
this._appPausedByInjector = false;
cb();
}.bind(this));
};
InjectorClient.prototype._getFuncWithNMInScope = function(cb) {
this._debuggerClient.request('evaluate', {
global: true,
expression: FN_WITH_SCOPED_NM
}, function(error, result) {
if (error) return cb(error);
cb(null, result.handle);
}.bind(this));
};
InjectorClient.prototype._findNMInScope = function(funcHandle, cb) {
this._debuggerClient.request('scope', {
functionHandle: funcHandle
}, function(error, result, refs) {
if (error) return cb(error);
var NM = refs[result.object.ref].properties.filter(function(prop) {
return prop.name == 'NativeModule';
});
if (!NM.length)
error = new Error('No NativeModule in target scope');
cb(error, NM[0].ref);
}.bind(this));
};
/**
* @param {Number} NM - handle of NativeModule object
*/
InjectorClient.prototype._inject = function(NM, cb) {
var injectorServerPath = JSON.stringify(require.resolve('./InjectorServer'));
var options = {
'v8-debug': require.resolve('v8-debug'),
'convert': require.resolve('./convert')
};
var injection = '(function (NM) {' +
'NM.require("module")._load(' + injectorServerPath + ')' +
'(' + JSON.stringify(options) + ')' +
'})(NM)';
this._debuggerClient.request(
'evaluate',
{
expression: injection,
global: true,
additional_context: [
{ name: 'NM', handle: NM }
]
},
function(error) {
cb(error);
}
);
};
/**
*/
InjectorClient.prototype._onInjection = function(cb) {
this._injected = true;
this.emit('inject');
cb();
};
InjectorClient.prototype.close = function() {
this._injected = false;
this._appPausedByInjector = false;
this.emit('close');
};
InjectorClient.prototype.injection = function(injection, options, callback) {
this._debuggerClient.request(
'evaluate',
{
expression: '(' + injection.toString() + ')' +
'(process._require, process._debugObject, ' + JSON.stringify(options) + ')',
global: true
},
callback
);
};
module.exports.InjectorClient = InjectorClient;