123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- var extend = require('util')._extend;
- var EventEmitter = require('events').EventEmitter,
- inherits = require('util').inherits,
- DebugConnection = require('./debugger.js');
- function createFailingConnection(reason) {
- return {
- connected: false,
- isRunning: false,
- request: function(command, args, callback) {
- callback({ message: new ErrorNotConnected(reason) });
- },
- close: function() {
- }
- };
- }
- /**
- * @constructor
- * @param {number} debuggerPort
- */
- function DebuggerClient(debuggerPort) {
- this._conn = createFailingConnection('node-inspector server was restarted');
- this._port = debuggerPort;
- }
- inherits(DebuggerClient, EventEmitter);
- Object.defineProperties(DebuggerClient.prototype, {
- /** @type {boolean} */
- isRunning: {
- get: function() {
- return this._conn.isRunning;
- }
- },
- isConnected: {
- get: function() {
- return this._conn.connected;
- }
- }
- });
- DebuggerClient.prototype.connect = function() {
- this._conn = DebugConnection.attachDebugger(this._port);
- this._conn.
- on('connect', this._onConnectionOpen.bind(this)).
- on('error', this.emit.bind(this, 'error')).
- on('close', this._onConnectionClose.bind(this));
- this._registerDebuggerEventHandlers('break', 'afterCompile', 'exception');
- };
- /**
- * @param {...string} eventNames
- */
- DebuggerClient.prototype._registerDebuggerEventHandlers = function(eventNames) {
- for (var i in arguments) {
- var name = arguments[i];
- this._conn.on(name, this._emitDebuggerEvent.bind(this, name));
- }
- };
- DebuggerClient.prototype._onConnectionOpen = function() {
- //We need to update isRunning state before we continue with debugging.
- //Send the dummy requestso that we can read the state from the response.
- this.request('version', {}, function(error, result) {
- this.emit('connect');
- }.bind(this));
- };
- /**
- * @param {string} reason
- */
- DebuggerClient.prototype._onConnectionClose = function(reason) {
- this._conn = createFailingConnection(reason);
- this.emit('close', reason);
- };
- /**
- * @param {string} name
- * @param {Object} message
- */
- DebuggerClient.prototype._emitDebuggerEvent = function(name, message) {
- this.emit(name, message.body);
- };
- /**
- * @param {string} command
- * @param {!Object} args
- * @param {function(error, response, refs)} callback
- */
- DebuggerClient.prototype.request = function(command, args, callback) {
- if (typeof callback !== 'function') {
- callback = function(error) {
- if (!error) return;
- console.log('Warning: ignored V8 debugger error. %s', error);
- };
- }
- // Note: we must not add args object if it was not sent.
- // E.g. resume (V8 request 'continue') does no work
- // correctly when args are empty instead of undefined
- if (args && args.maxStringLength == null)
- args.maxStringLength = 10000;
- this._conn.request(command, { arguments: args }, function(response) {
- var refsLookup;
- if (!response.success)
- callback(response.message);
- else {
- refsLookup = {};
- if (response.refs)
- response.refs.forEach(function(r) { refsLookup[r.handle] = r; });
- callback(null, response.body, refsLookup);
- }
- });
- };
- /**
- */
- DebuggerClient.prototype.close = function() {
- this._conn.close();
- };
- /**
- * @param {number} breakpointId
- * @param {function(error, response, refs)} done
- */
- DebuggerClient.prototype.clearBreakpoint = function(breakpointId, done) {
- this.request(
- 'clearbreakpoint',
- {
- breakpoint: breakpointId
- },
- done
- );
- };
- /**
- * @param {string} expression
- * @param {function(error, response)} done
- */
- DebuggerClient.prototype.evaluateGlobal = function(expression, done) {
- // Note: we can't simply evaluate JSON.stringify(`expression`)
- // because V8 debugger protocol truncates returned value to 80 characters
- // The workaround is to split the serialized value into multiple pieces,
- // each piece 80 characters long, send an array over the wire,
- // and reconstruct the value back here
- var code = 'JSON.stringify(' + expression + ').match(/.{1,80}/g).slice()';
- this.request(
- 'evaluate',
- {
- expression: code,
- global: true
- },
- function _handleEvaluateResponse(err, result, refs) {
- if (err) return done(err);
- if (result.type != 'object' && result.className != 'Array') {
- return done(
- new Error(
- 'Evaluate returned unexpected result:' +
- ' type: ' + result.type +
- ' className: ' + result.className
- )
- );
- }
- var fullJsonString = result.properties
- .filter(function isArrayIndex(p) { return /^\d+$/.test(p.name);})
- .map(function resolvePropertyValue(p) { return refs[p.ref].value; })
- .join('');
- try {
- done(null, JSON.parse(fullJsonString));
- } catch (e) {
- console.error('evaluateGlobal "%s" failed', expression);
- console.error(e.stack);
- console.error('--json-begin--\n%s--json-end--', fullJsonString);
- done(e);
- }
- }
- );
- };
- /**
- * @param {number} id
- * @param {function(Object, string?)} callback
- */
- DebuggerClient.prototype.getScriptSourceById = function(id, callback) {
- this.request(
- 'scripts',
- {
- includeSource: true,
- types: 4,
- ids: [id]
- },
- function handleScriptSourceResponse(err, result) {
- if (err) return callback(err);
- // Some modules gets unloaded (?) after they are parsed,
- // e.g. node_modules/express/node_modules/methods/index.js
- // V8 request 'scripts' returns an empty result in such case
- var source = result.length > 0 ? result[0].source : undefined;
- callback(null, source);
- }
- );
- };
- /**
- * @param {string} message
- * @constructor
- */
- function ErrorNotConnected(message) {
- Error.call(this);
- this.name = ErrorNotConnected.name;
- this.message = message;
- }
- inherits(ErrorNotConnected, Error);
- exports.DebuggerClient = DebuggerClient;
- exports.ErrorNotConnected = ErrorNotConnected;
|