| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| /** |
| * @fileoverview Generator of typeless service interface from JavaScript object. |
| * @private |
| */ |
| |
| module.exports = ReflectInterface; |
| |
| var ArgInspector = require('../lib/arg-inspector'); |
| var isPublicMethod = require('../lib/service-reflection').isPublicMethod; |
| var vdlUtil = require('./util'); |
| var format = require('format'); |
| |
| /** |
| * Create an interface for a service by inspecting the service object. |
| * @private |
| * @param {Service} service The service. |
| * @constructor |
| */ |
| function ReflectInterface(service) { |
| if (!(this instanceof ReflectInterface)) { |
| return new ReflectInterface(service); |
| } |
| |
| var ifc = this; |
| |
| ifc.methods = []; |
| |
| // NOTE: service.hasOwnProperty(key) is intentionally omitted so that |
| // methods defined on the prototype chain are mapped into the interface |
| // correctly. This supports services defined using constructors: |
| // |
| // function Service() { |
| // |
| // } |
| // |
| // Service.prototype.method = function() { |
| // |
| // } |
| // |
| // TODO(jasoncampbell): At some point we should try to avoid inherited |
| // properties so we don't unintentionally publish a service's internal |
| // implementation where inheritance has been used (event emitters etc.). |
| // |
| // SEE: http://git.io/mi6jDg |
| // SEE: veyron/release-issues#657 |
| for (var key in service) { // jshint ignore:line |
| if (!isPublicMethod(key, service)) { |
| continue; |
| } |
| |
| var method = service[key]; |
| var methodSignature = { |
| name: vdlUtil.capitalize(key), |
| streaming: false |
| }; |
| |
| var argInspector = new ArgInspector(method); |
| // Check whether the number of args reported by javascript (method.length) |
| // and the number of args retrieved from fn.toString() are the same. |
| // This usually differs if the method is a native method. |
| if (argInspector.names.length !== method.length) { |
| throw new Error('Function "' + key + '" can not be inspected. ' + |
| 'This is usually because it is a native method or bind is used.'); |
| } |
| var message; |
| if (!argInspector.hasContext()) { |
| message = format('Service method "%s" is missing the required ' + |
| '`context` object as the first argument in its definition. ' + |
| 'Args were: %s', |
| key, argInspector.names); |
| throw new Error(message); |
| } |
| |
| if (!argInspector.hasCall()) { |
| message = format('Service method "%s" is missing the required ' + |
| '`serverCall` object as the second argument in its definition. ' + |
| 'Args were: %s', |
| key, argInspector.names); |
| var e = new Error(message); |
| throw e; |
| } |
| |
| methodSignature.inArgs = argInspector.filteredNames.map(function(name) { |
| return { name: name }; |
| }); |
| |
| methodSignature.streaming = argInspector.contains('$stream'); |
| |
| // Add this method's signature to its service interface. |
| ifc.methods.push(methodSignature); |
| } |
| |
| // Sort all the method signatures by method name. |
| ifc.methods.sort(function(methodSig, methodSig2) { |
| if (methodSig.name === methodSig2.name) { |
| return 0; |
| } |
| return methodSig.name < methodSig2.name ? -1 : 1; |
| }); |
| } |