Permalink
Browse files

Merge branch 'add/function-type'

  • Loading branch information...
TooTallNate committed Oct 2, 2012
2 parents 444b45a + 80b1ed7 commit d2f68ba6dafe7450c0ba16a87e06ceb0b5377e38
Showing with 167 additions and 2 deletions.
  1. +1 −2 lib/callback.js
  2. +1 −0 lib/ffi.js
  3. +110 −0 lib/function.js
  4. +11 −0 test/ffi_tests.cc
  5. +44 −0 test/function.js
View
@@ -8,7 +8,6 @@ var ref = require('ref')
, assert = require('assert')
, debug = require('debug')('ffi:Callback')
, _Callback = require('./bindings').Callback
- , POINTER_SIZE = ref.sizeof.pointer
/**
* Turns a JavaScript function into a C function pointer.
@@ -44,7 +43,7 @@ function Callback (retType, argTypes, abi, func) {
var args = []
for (var i = 0; i < argc; i++) {
var type = argTypes[i]
- var argPtr = params.readPointer(i * POINTER_SIZE, type.size)
+ var argPtr = params.readPointer(i * ref.sizeof.pointer, type.size)
argPtr.type = type
args.push(argPtr.deref())
}
View
@@ -72,6 +72,7 @@ exports.types = ref.types
// Include our other modules
exports.CIF = require('./cif')
exports.CIF_var = require('./cif_var')
+exports.Function = require('./function')
exports.ForeignFunction = require('./foreign_function')
exports.VariadicForeignFunction = require('./foreign_function_var')
exports.DynamicLibrary = require('./dynamic_library')
View
@@ -0,0 +1,110 @@
+
+/**
+ * Module dependencies.
+ */
+
+var ref = require('ref')
+ , assert = require('assert')
+ , bindings = require('./bindings')
+ , Callback = require('./callback')
+ , ForeignFunction = require('./foreign_function')
+ , debug = require('debug')('ffi:FunctionType')
+
+/**
+ * Module exports.
+ */
+
+module.exports = Function
+
+/**
+ * Creates and returns a "type" object for a C "function pointer".
+ *
+ * @api public
+ */
+
+function Function (retType, argTypes, abi) {
+ if (!(this instanceof Function)) {
+ return new Function(retType, argTypes, abi)
+ }
+
+ debug('creating new FunctionType')
+
+ // check args
+ assert(!!retType, 'expected a return "type" object as the first argument')
+ assert(Array.isArray(argTypes), 'expected Array of arg "type" objects as the second argument')
+
+ // normalize the "types" (they could be strings, so turn into real type
+ // instances)
+ this.retType = ref.coerceType(retType)
+ this.argTypes = argTypes.map(ref.coerceType)
+ this.abi = null == abi ? bindings.FFI_DEFAULT_ABI : abi
+}
+
+/**
+ * The "ffi_type" is set for node-ffi functions.
+ */
+
+Function.prototype.ffi_type = bindings.FFI_TYPES.pointer
+
+/**
+ * The "size" is always pointer-sized.
+ */
+
+Function.prototype.size = ref.sizeof.pointer
+
+/**
+ * The "alignment" is always pointer-aligned.
+ */
+
+Function.prototype.alignment = ref.alignof.pointer
+
+/**
+ * The "indirection" is always 1 to ensure that our get()/set() get called.
+ */
+
+Function.prototype.indirection = 1
+
+/**
+ * Returns a ffi.Callback pointer (Buffer) of this function type for the
+ * given `fn` Function.
+ */
+
+Function.prototype.toPointer = function toPointer (fn) {
+ return Callback(this.retType, this.argTypes, this.abi, fn)
+}
+
+/**
+ * Returns a ffi.ForeignFunction (Function) of this function type for the
+ * given `buf` Buffer.
+ */
+
+Function.prototype.toFunction = function toFunction (buf) {
+ return ForeignFunction(buf, this.retType, this.argTypes, this.abi)
+}
+
+/**
+ * get function; return a ForeignFunction instance.
+ */
+
+Function.prototype.get = function get (buffer, offset) {
+ debug('ffi FunctionType "get" function')
+ var ptr = buffer.readPointer(offset)
+ return this.toFunction(ptr)
+}
+
+/**
+ * set function; return a Callback buffer.
+ */
+
+Function.prototype.set = function set (buffer, offset, value) {
+ debug('ffi FunctionType "set" function')
+ var ptr
+ if ('function' == typeof value) {
+ ptr = this.toPointer(value)
+ } else if (Buffer.isBuffer(value)) {
+ ptr = value
+ } else {
+ throw new Error('don\'t know how to set callback function for: ' + value)
+ }
+ buffer.writePointer(ptr, offset)
+}
View
@@ -117,6 +117,16 @@ struct arst array_in_struct (struct arst input) {
return rtn;
}
+/*
+ * Tests for C function pointers.
+ */
+
+typedef int (*my_callback)(int);
+
+my_callback callback_func (my_callback cb) {
+ return cb;
+}
+
/*
* Hard-coded `strtoul` binding, for the benchmarks.
*
@@ -233,6 +243,7 @@ void Initialize(Handle<Object> target) {
target->Set(String::NewSymbol("add_boxes"), WrapPointer((char *)add_boxes));
target->Set(String::NewSymbol("int_array"), WrapPointer((char *)int_array));
target->Set(String::NewSymbol("array_in_struct"), WrapPointer((char *)array_in_struct));
+ target->Set(String::NewSymbol("callback_func"), WrapPointer((char *)callback_func));
}
} // anonymous namespace
View
@@ -0,0 +1,44 @@
+
+var assert = require('assert')
+ , ref = require('ref')
+ , ffi = require('../')
+ , bindings = require('bindings')({ module_root: __dirname, bindings: 'ffi_tests' })
+
+describe('Function "type"', function () {
+
+ afterEach(gc)
+
+ it('should be a function', function () {
+ assert.equal('function', typeof ffi.Function)
+ })
+
+ var voidFn = ffi.Function('void', [])
+
+ it('should return a "type" object when invoked with a return type and array of arguments types', function () {
+ assert(voidFn)
+ assert.equal('function', typeof voidFn.get)
+ assert.equal('function', typeof voidFn.set)
+ })
+
+ it('should be accepted as a return "type" to a ForeignFunction', function () {
+ var fn = ffi.ForeignFunction(ref.NULL, voidFn, []);
+ })
+
+ it('should be accepted as an argument "type" to a ForeignFunction', function () {
+ var fn = ffi.ForeignFunction(ref.NULL, 'void', [ voidFn ]);
+ })
+
+ it('should work as expected using the "callback_func" static bindings', function () {
+ var fn = ffi.Function('int', [ 'int' ])
+ var callback_func = ffi.ForeignFunction(bindings.callback_func, fn, [ fn ]);
+
+ var abs = callback_func(Math.abs)
+ assert.equal('function', typeof abs)
+
+ assert.equal(Math.abs(-5), abs(-5))
+ assert.equal(Math.abs(-9), abs(-9))
+ assert.equal(Math.abs(-69), abs(-69))
+ assert.equal(Math.abs(3), abs(3))
+ })
+
+})

0 comments on commit d2f68ba

Please sign in to comment.