// node-inspector version of on webkit-inspector/InspectorRuntimeAgent.cpp var convert = require('./convert.js'), util = require('util'), CallFramesProvider = require('./CallFramesProvider.js').CallFramesProvider; /** * @param {Object} config * @param {DebuggerClient} debuggerClient * @constructor */ function RuntimeAgent(config, debuggerClient) { this._debuggerClient = debuggerClient; this._callFramesProvider = new CallFramesProvider(config, debuggerClient); } RuntimeAgent.prototype = { enable: function(params, done) { done(); }, evaluate: function(params, done) { var self = this; self._debuggerClient.request( 'evaluate', { expression: params.expression, global: true }, function(err, result) { // Errors from V8 are actually just messages, so we need to fill them out a bit. if (err) { err = convert.v8ErrorToInspectorError(err); } done(null, { result: err || convert.v8ResultToInspectorResult(result), wasThrown: !!err }); } ); }, callFunctionOn: function(params, done) { function callFunctionWithParams(err, evaluateParams) { if (err) { done(err); return; } var callback = this._handleCallFunctionOnObjectResponse .bind(this, done, params.returnByValue); this._debuggerClient.request( 'evaluate', evaluateParams, callback ); } this._createEvaluateParamsForFunctionCall( params.objectId, params.functionDeclaration, params.arguments, callFunctionWithParams.bind(this) ); }, _createEvaluateParamsForFunctionCall: function(selfId, declaration, args, done) { args = args || []; try { var argsData = args.map(this._getFunctionCallArgsData.bind(this)); var params = this._buildEvaluateParamsFromArgsData( selfId, declaration, argsData); done(null, params); } catch (err) { done(err); } }, _buildEvaluateParamsFromArgsData: function(selfId, declaration, argsData) { argsData.unshift(this._getSelfArgData(selfId)); var argNames = argsData.map(function(a) { return a.code; }); var argContexts = argsData .map(function(a) { return a.context; }) // filter out empty contexts (value types are context-less) .filter(function(c) { return !!c; }); var expression = util.format( '(%s).call(%s)', declaration, argNames.join(', ')); return { expression: expression, global: true, additional_context: argContexts }; }, _getSelfArgData: function(selfId) { var SELF_CONTEXT_NAME = '__node_inspector_self__'; return { code: SELF_CONTEXT_NAME, context: { name: SELF_CONTEXT_NAME, handle: Number(selfId) } }; }, _getFunctionCallArgsData: function(arg, index) { var uniqueId = '__node_inspector_arg' + index; switch (arg.type) { case undefined: case 'string': return { code: util.format('"%s"', arg.value) }; case 'number': return { code: arg.value }; case 'null': case 'undefined': return { code: arg.type }; case 'object': case 'function': return { code: uniqueId, context: { name: uniqueId, handle: Number(arg.objectId) } }; default: throw new Error(util.format( 'Function arguments of type "%s" are not supported', arg.type )); } }, _handleCallFunctionOnObjectResponse: function(done, returnByValue, err, response) { if (err) { done(null, { err: err, wasThrown: true }); return; } var value = {}; if (returnByValue && response.properties) { for (var i = 0; i < response.properties.length; i++) { value[response.properties[i].name] = true; } } done(null, { result: { value: value }, wasThrown: false }); }, getProperties: function(params, done) { // TODO implement the new way of getting object properties // // Front-end sends the following two requests for Object properties: // "params": {"objectId":"78","ownProperties":false,"accessorPropertiesOnly":true} // "params":{"objectId":"78","ownProperties":true,"accessorPropertiesOnly":false} // // Or the following request for Scope properties: // "params":{"objectId":"scope:0:2","ownProperties":false,"accessorPropertiesOnly":false} // See getProperties() and getInjectedProperties() in // http://src.chromium.org/blink/branches/chromium/1625/Source/core/ // inspector/InjectedScriptSource.js // for more details. var options = { ownProperties: params.ownProperties, accessorPropertiesOnly: params.accessorPropertiesOnly }; if (this._callFramesProvider.isScopeId(params.objectId)) { this._getPropertiesOfScopeId(params.objectId, options, done); } else { this._getPropertiesOfObjectId(params.objectId, options, done); } }, _getPropertiesOfScopeId: function(scopeId, options, done) { this._callFramesProvider.resolveScopeId( scopeId, function(err, result) { if (err) { done(err); } else { this._getPropertiesOfObjectId(result, options, done); } }.bind(this) ); }, _getPropertiesOfObjectId: function(objectId, options, done) { var handle = parseInt(objectId, 10); var request = { handles: [handle], includeSource: false }; this._debuggerClient.request( 'lookup', request, function(error, responseBody, responseRefs) { if (error) { done(error); return; } var obj = responseBody[handle], props = convert.v8ObjectToInspectorProperties(obj, responseRefs, options); done(null, { result: props }); } ); }, releaseObjectGroup: function(params, done) { // V8 debugger protocol does not support object groups done(); }, releaseObject: function(params, done) { // V8 debugger protocol does not support manual release done(); } }; exports.RuntimeAgent = RuntimeAgent;