summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authortenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-05-06 06:59:24 +0000
committertenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-05-06 06:59:24 +0000
commit4bada8b864e445b6eebe1a341e30cad94dbcaf84 (patch)
treebb7e4ed991aa16eda953d536265c6f9e5dc03a5d /ext
parentca3c007f058f17b2f0bf3c5ccc6701f3f6d49ca5 (diff)
* ext/fiddle/*: Adding fiddle library to wrap libffi
* test/fiddle/*: testing fiddle extension * ext/dl/lib/dl.rb: Requiring fiddle if it is available * ext/dl/lib/dl/callback.rb: using Fiddle if it is available * ext/dl/lib/dl/func.rb: ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27640 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext')
-rw-r--r--ext/dl/lib/dl.rb12
-rw-r--r--ext/dl/lib/dl/callback.rb83
-rw-r--r--ext/dl/lib/dl/func.rb114
-rw-r--r--ext/dl/lib/dl/import.rb14
-rw-r--r--ext/dl/lib/dl/value.rb2
-rw-r--r--ext/fiddle/closure.c232
-rw-r--r--ext/fiddle/closure.h8
-rw-r--r--ext/fiddle/conversions.c126
-rw-r--r--ext/fiddle/conversions.h41
-rw-r--r--ext/fiddle/extconf.rb23
-rw-r--r--ext/fiddle/fiddle.c30
-rw-r--r--ext/fiddle/fiddle.h45
-rw-r--r--ext/fiddle/function.c155
-rw-r--r--ext/fiddle/function.h8
-rw-r--r--ext/fiddle/lib/fiddle.rb27
-rw-r--r--ext/fiddle/lib/fiddle/closure.rb17
-rw-r--r--ext/fiddle/lib/fiddle/function.rb5
17 files changed, 871 insertions, 71 deletions
diff --git a/ext/dl/lib/dl.rb b/ext/dl/lib/dl.rb
new file mode 100644
index 00000000000..168a18a55ee
--- /dev/null
+++ b/ext/dl/lib/dl.rb
@@ -0,0 +1,12 @@
+require 'dl.so'
+
+begin
+ require 'fiddle'
+rescue LoadError
+end
+
+module DL
+ def self.fiddle?
+ Object.const_defined?(:Fiddle)
+ end
+end
diff --git a/ext/dl/lib/dl/callback.rb b/ext/dl/lib/dl/callback.rb
index c8daaf63224..0863c70d4d2 100644
--- a/ext/dl/lib/dl/callback.rb
+++ b/ext/dl/lib/dl/callback.rb
@@ -4,22 +4,38 @@ require 'thread'
module DL
SEM = Mutex.new
- def set_callback_internal(proc_entry, addr_entry, argc, ty, &cbp)
+ if DL.fiddle?
+ CdeclCallbackProcs = {}
+ CdeclCallbackAddrs = {}
+ StdcallCallbackProcs = {}
+ StdcallCallbackAddrs = {}
+ end
+
+ def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = nil, &cbp)
if( argc < 0 )
raise(ArgumentError, "arity should not be less than 0.")
end
addr = nil
- SEM.synchronize{
- ary = proc_entry[ty]
- (0...MAX_CALLBACK).each{|n|
- idx = (n * DLSTACK_SIZE) + argc
- if( ary[idx].nil? )
- ary[idx] = cbp
- addr = addr_entry[ty][idx]
- break
- end
+
+ if DL.fiddle?
+ abi ||= Fiddle::Function::DEFAULT
+ closure = Fiddle::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp)
+ proc_entry[closure.to_i] = closure
+ addr = closure.to_i
+ else
+ SEM.synchronize{
+ ary = proc_entry[ty]
+ (0...MAX_CALLBACK).each{|n|
+ idx = (n * DLSTACK_SIZE) + argc
+ if( ary[idx].nil? )
+ ary[idx] = cbp
+ addr = addr_entry[ty][idx]
+ break
+ end
+ }
}
- }
+ end
+
addr
end
@@ -28,31 +44,42 @@ module DL
end
def set_stdcall_callback(ty, argc, &cbp)
- set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
+ if DL.fiddle?
+ set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, Fiddle::Function::STDCALL, &cbp)
+ else
+ set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
+ end
end
def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
- index = nil
- if( ctype )
- addr_entry[ctype].each_with_index{|xaddr, idx|
- if( xaddr == addr )
- index = idx
- end
- }
+ if DL.fiddle?
+ addr = addr.to_i
+ return false unless proc_entry.key?(addr)
+ proc_entry.delete(addr)
+ true
else
- addr_entry.each{|ty,entry|
- entry.each_with_index{|xaddr, idx|
+ index = nil
+ if( ctype )
+ addr_entry[ctype].each_with_index{|xaddr, idx|
if( xaddr == addr )
index = idx
end
}
- }
- end
- if( index and proc_entry[ctype][index] )
- proc_entry[ctype][index] = nil
- return true
- else
- return false
+ else
+ addr_entry.each{|ty,entry|
+ entry.each_with_index{|xaddr, idx|
+ if( xaddr == addr )
+ index = idx
+ end
+ }
+ }
+ end
+ if( index and proc_entry[ctype][index] )
+ proc_entry[ctype][index] = nil
+ return true
+ else
+ return false
+ end
end
end
diff --git a/ext/dl/lib/dl/func.rb b/ext/dl/lib/dl/func.rb
index 7a8b62e3253..4d0db4ee7a0 100644
--- a/ext/dl/lib/dl/func.rb
+++ b/ext/dl/lib/dl/func.rb
@@ -5,21 +5,37 @@ require 'dl/value'
require 'thread'
module DL
- class Function
+ parent = DL.fiddle? ? Fiddle::Function : Object
+
+ class Function < parent
include DL
include ValueUtil
- def initialize(cfunc, argtypes, &proc)
- @cfunc = cfunc
- @stack = Stack.new(argtypes.collect{|ty| ty.abs})
- if( @cfunc.ctype < 0 )
- @cfunc.ctype = @cfunc.ctype.abs
- @unsigned = true
+ def initialize cfunc, argtypes, abi = nil, &block
+ if DL.fiddle?
+ abi ||= Fiddle::Function::DEFAULT
+ if block_given?
+ @cfunc = Class.new(Fiddle::Closure) {
+ define_method(:call, block)
+ }.new(cfunc.ctype, argtypes)
+ else
+ @cfunc = cfunc
+ end
+
+ @args = argtypes
+ super(@cfunc, @args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi)
else
- @unsigned = false
- end
- if( proc )
- bind(&proc)
+ @cfunc = cfunc
+ @stack = Stack.new(argtypes.collect{|ty| ty.abs})
+ if( @cfunc.ctype < 0 )
+ @cfunc.ctype = @cfunc.ctype.abs
+ @unsigned = true
+ else
+ @unsigned = false
+ end
+ if block_given?
+ bind(&block)
+ end
end
end
@@ -32,11 +48,18 @@ module DL
end
def call(*args, &block)
- funcs = []
- args = wrap_args(args, @stack.types, funcs, &block)
- r = @cfunc.call(@stack.pack(args))
- funcs.each{|f| f.unbind_at_call()}
- return wrap_result(r)
+ if DL.fiddle?
+ if block_given?
+ args.find { |a| DL::Function === a }.bind_at_call(&block)
+ end
+ super
+ else
+ funcs = []
+ args = wrap_args(args, @stack.types, funcs, &block)
+ r = @cfunc.call(@stack.pack(args))
+ funcs.each{|f| f.unbind_at_call()}
+ return wrap_result(r)
+ end
end
def wrap_result(r)
@@ -52,31 +75,44 @@ module DL
end
def bind(&block)
- if( !block )
- raise(RuntimeError, "block must be given.")
- end
- if( @cfunc.ptr == 0 )
- cb = Proc.new{|*args|
- ary = @stack.unpack(args)
- @stack.types.each_with_index{|ty, idx|
- case ty
- when TYPE_VOIDP
- ary[idx] = CPtr.new(ary[idx])
- end
- }
- r = block.call(*ary)
- wrap_arg(r, @cfunc.ctype, [])
- }
- case @cfunc.calltype
- when :cdecl
- @cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb)
- when :stdcall
- @cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb)
- else
- raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}")
+ if DL.fiddle?
+ @cfunc = Class.new(Fiddle::Closure) {
+ def initialize ctype, args, block
+ super(ctype, args)
+ @block = block
+ end
+
+ def call *args
+ @block.call(*args)
+ end
+ }.new(@cfunc.ctype, @args, block)
+ else
+ if( !block )
+ raise(RuntimeError, "block must be given.")
end
if( @cfunc.ptr == 0 )
- raise(RuntimeException, "can't bind C function.")
+ cb = Proc.new{|*args|
+ ary = @stack.unpack(args)
+ @stack.types.each_with_index{|ty, idx|
+ case ty
+ when TYPE_VOIDP
+ ary[idx] = CPtr.new(ary[idx])
+ end
+ }
+ r = block.call(*ary)
+ wrap_arg(r, @cfunc.ctype, [])
+ }
+ case @cfunc.calltype
+ when :cdecl
+ @cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb)
+ when :stdcall
+ @cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb)
+ else
+ raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}")
+ end
+ if( @cfunc.ptr == 0 )
+ raise(RuntimeException, "can't bind C function.")
+ end
end
end
end
diff --git a/ext/dl/lib/dl/import.rb b/ext/dl/lib/dl/import.rb
index 199354c18ec..fd23bc96766 100644
--- a/ext/dl/lib/dl/import.rb
+++ b/ext/dl/lib/dl/import.rb
@@ -211,9 +211,17 @@ module DL
end
def bind_function(name, ctype, argtype, call_type = nil, &block)
- f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
- f.bind(&block)
- f
+ if DL.fiddle?
+ closure = Class.new(Fiddle::Closure) {
+ define_method(:call, block)
+ }.new(ctype, argtype)
+
+ Function.new(closure, argtype)
+ else
+ f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
+ f.bind(&block)
+ f
+ end
end
def create_temp_function(name, ctype, argtype, call_type = nil)
diff --git a/ext/dl/lib/dl/value.rb b/ext/dl/lib/dl/value.rb
index 56dfcefa323..51f96b293b4 100644
--- a/ext/dl/lib/dl/value.rb
+++ b/ext/dl/lib/dl/value.rb
@@ -45,7 +45,7 @@ module DL
result
end
- def wrap_arg(arg, ty, funcs, &block)
+ def wrap_arg(arg, ty, funcs = [], &block)
funcs ||= []
case arg
when nil
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c
new file mode 100644
index 00000000000..2531ef2b482
--- /dev/null
+++ b/ext/fiddle/closure.c
@@ -0,0 +1,232 @@
+#include <fiddle.h>
+
+VALUE cFiddleClosure;
+
+typedef struct {
+ void * code;
+ ffi_closure *pcl;
+ ffi_cif * cif;
+ int argc;
+ ffi_type **argv;
+} fiddle_closure;
+
+static void
+dealloc(void * ptr)
+{
+ fiddle_closure * cls = (fiddle_closure *)ptr;
+#ifndef MACOSX
+ ffi_closure_free(cls->pcl);
+#else
+ munmap(cls->pcl, sizeof(cls->pcl));
+#endif
+ xfree(cls->cif);
+ if (cls->argv) xfree(cls->argv);
+ xfree(cls);
+}
+
+static size_t
+closure_memsize(const void * ptr)
+{
+ fiddle_closure * cls = (fiddle_closure *)ptr;
+ size_t size = 0;
+
+ if (ptr) {
+ size += sizeof(*cls);
+#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
+ size += ffi_raw_size(cls->cif);
+#endif
+ size += sizeof(*cls->argv);
+ size += sizeof(ffi_closure);
+ }
+ return size;
+}
+
+const rb_data_type_t closure_data_type = {
+ "fiddle/closure",
+ 0, dealloc, closure_memsize,
+};
+
+void
+callback(ffi_cif *cif, void *resp, void **args, void *ctx)
+{
+ VALUE self = (VALUE)ctx;
+ VALUE rbargs = rb_iv_get(self, "@args");
+ VALUE ctype = rb_iv_get(self, "@ctype");
+ int argc = RARRAY_LENINT(rbargs);
+ VALUE *params = xcalloc(argc, sizeof(VALUE *));
+ VALUE ret;
+ VALUE cPointer;
+ int i, type;
+
+ cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
+
+ for (i = 0; i < argc; i++) {
+ type = NUM2INT(RARRAY_PTR(rbargs)[i]);
+ switch (type) {
+ case TYPE_VOID:
+ argc = 0;
+ break;
+ case TYPE_INT:
+ params[i] = INT2NUM(*(int *)args[i]);
+ break;
+ case TYPE_VOIDP:
+ params[i] = rb_funcall(cPointer, rb_intern("[]"), 1,
+ PTR2NUM(*(void **)args[i]));
+ break;
+ case TYPE_LONG:
+ params[i] = LONG2NUM(*(long *)args[i]);
+ break;
+ case TYPE_CHAR:
+ params[i] = INT2NUM(*(char *)args[i]);
+ break;
+ case TYPE_DOUBLE:
+ params[i] = rb_float_new(*(double *)args[i]);
+ break;
+ case TYPE_FLOAT:
+ params[i] = rb_float_new(*(float *)args[i]);
+ break;
+#if HAVE_LONG_LONG
+ case TYPE_LONG_LONG:
+ params[i] = rb_ull2inum(*(unsigned LONG_LONG *)args[i]);
+ break;
+#endif
+ default:
+ rb_raise(rb_eRuntimeError, "closure args: %d", type);
+ }
+ }
+
+ ret = rb_funcall2(self, rb_intern("call"), argc, params);
+
+ type = NUM2INT(ctype);
+ switch (type) {
+ case TYPE_VOID:
+ break;
+ case TYPE_LONG:
+ *(long *)resp = NUM2LONG(ret);
+ break;
+ case TYPE_CHAR:
+ *(char *)resp = NUM2INT(ret);
+ break;
+ case TYPE_VOIDP:
+ *(void **)resp = NUM2PTR(ret);
+ break;
+ case TYPE_INT:
+ *(int *)resp = NUM2INT(ret);
+ break;
+ case TYPE_DOUBLE:
+ *(double *)resp = NUM2DBL(ret);
+ break;
+ case TYPE_FLOAT:
+ *(float *)resp = (float)NUM2DBL(ret);
+ break;
+#if HAVE_LONG_LONG
+ case TYPE_LONG_LONG:
+ *(unsigned LONG_LONG *)resp = rb_big2ull(ret);
+ break;
+#endif
+ default:
+ rb_raise(rb_eRuntimeError, "closure retval: %d", type);
+ }
+ xfree(params);
+}
+
+static VALUE
+allocate(VALUE klass)
+{
+ fiddle_closure * closure;
+
+ VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
+ &closure_data_type, closure);
+
+#ifndef MACOSX
+ closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
+#else
+ closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+#endif
+ closure->cif = xmalloc(sizeof(ffi_cif));
+
+ return i;
+}
+
+static VALUE
+initialize(int rbargc, VALUE argv[], VALUE self)
+{
+ VALUE ret;
+ VALUE args;
+ VALUE abi;
+ fiddle_closure * cl;
+ ffi_cif * cif;
+ ffi_closure *pcl;
+ ffi_status result;
+ int i, argc;
+
+ if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
+ abi = INT2NUM(FFI_DEFAULT_ABI);
+
+ Check_Type(args, T_ARRAY);
+
+ argc = RARRAY_LENINT(args);
+
+ TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
+
+ cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
+
+ for (i = 0; i < argc; i++) {
+ int type = NUM2INT(RARRAY_PTR(args)[i]);
+ cl->argv[i] = INT2FFI_TYPE(type);
+ }
+ cl->argv[argc] = NULL;
+
+ rb_iv_set(self, "@ctype", ret);
+ rb_iv_set(self, "@args", args);
+
+ cif = cl->cif;
+ pcl = cl->pcl;
+
+ result = ffi_prep_cif(cif, NUM2INT(abi), argc,
+ INT2FFI_TYPE(NUM2INT(ret)),
+ cl->argv);
+
+ if (FFI_OK != result)
+ rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
+
+#ifndef MACOSX
+ result = ffi_prep_closure_loc(pcl, cif, callback,
+ (void *)self, cl->code);
+#else
+ result = ffi_prep_closure(pcl, cif, callback, (void *)self);
+ cl->code = (void *)pcl;
+ mprotect(pcl, sizeof(pcl), PROT_READ | PROT_EXEC);
+#endif
+
+ if (FFI_OK != result)
+ rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
+
+ return self;
+}
+
+static VALUE
+to_i(VALUE self)
+{
+ fiddle_closure * cl;
+ void *code;
+
+ TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
+
+ code = cl->code;
+
+ return PTR2NUM(code);
+}
+
+void
+Init_fiddle_closure()
+{
+ cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
+
+ rb_define_alloc_func(cFiddleClosure, allocate);
+
+ rb_define_method(cFiddleClosure, "initialize", initialize, -1);
+ rb_define_method(cFiddleClosure, "to_i", to_i, 0);
+}
+/* vim: set noet sw=4 sts=4 */
diff --git a/ext/fiddle/closure.h b/ext/fiddle/closure.h
new file mode 100644
index 00000000000..1e870e22853
--- /dev/null
+++ b/ext/fiddle/closure.h
@@ -0,0 +1,8 @@
+#ifndef FIDDLE_CLOSURE_H
+#define FIDDLE_CLOSURE_H
+
+#include <fiddle.h>
+
+void Init_fiddle_closure();
+
+#endif
diff --git a/ext/fiddle/conversions.c b/ext/fiddle/conversions.c
new file mode 100644
index 00000000000..bb5361a6c85
--- /dev/null
+++ b/ext/fiddle/conversions.c
@@ -0,0 +1,126 @@
+#include <fiddle.h>
+
+ffi_type *
+int_to_ffi_type(int type)
+{
+ int signed_p = 1;
+
+ if (type < 0) {
+ type = -1 * type;
+ signed_p = 0;
+ }
+
+#define rb_ffi_type_of(t) (signed_p ? &ffi_type_s##t : &ffi_type_u##t)
+
+ switch (type) {
+ case TYPE_VOID:
+ return &ffi_type_void;
+ case TYPE_VOIDP:
+ return &ffi_type_pointer;
+ case TYPE_CHAR:
+ return rb_ffi_type_of(char);
+ case TYPE_SHORT:
+ return rb_ffi_type_of(short);
+ case TYPE_INT:
+ return rb_ffi_type_of(int);
+ case TYPE_LONG:
+ return rb_ffi_type_of(long);
+#if HAVE_LONG_LONG
+ case TYPE_LONG_LONG:
+ return rb_ffi_type_of(int64);
+#endif
+ case TYPE_FLOAT:
+ return &ffi_type_float;
+ case TYPE_DOUBLE:
+ return &ffi_type_double;
+ default:
+ rb_raise(rb_eRuntimeError, "unknown type %d", type);
+ }
+ return &ffi_type_pointer;
+}
+
+void
+value_to_generic(int type, VALUE src, fiddle_generic * dst)
+{
+ int signed_p = 1;
+
+ if (type < 0) {
+ type = -1 * type;
+ signed_p = 0;
+ }
+
+ switch (type) {
+ case TYPE_VOID:
+ break;
+ case TYPE_VOIDP:
+ dst->pointer = NUM2PTR(rb_Integer(src));
+ break;
+ case TYPE_CHAR:
+ case TYPE_SHORT:
+ case TYPE_INT:
+ dst->sint = NUM2INT(src);
+ break;
+ case TYPE_LONG:
+ if (signed_p)
+ dst->slong = NUM2LONG(src);
+ else
+ dst->ulong = NUM2LONG(src);
+ break;
+#if HAVE_LONG_LONG
+ case TYPE_LONG_LONG:
+ dst->long_long = rb_big2ull(src);
+ break;
+#endif
+ case TYPE_FLOAT:
+ dst->ffloat = (float)NUM2DBL(src);
+ break;
+ case TYPE_DOUBLE:
+ dst->ddouble = NUM2DBL(src);
+ break;
+ default:
+ rb_raise(rb_eRuntimeError, "unknown type %d", type);
+ }
+}
+
+VALUE
+generic_to_value(VALUE rettype, fiddle_generic retval)
+{
+ int signed_p = 1;
+ int type = NUM2INT(rettype);
+ VALUE cPointer;
+
+ cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
+
+ if (type < 0) {
+ type = -1 * type;
+ signed_p = 0;
+ }
+
+ switch (type) {
+ case TYPE_VOID:
+ return Qnil;
+ case TYPE_VOIDP:
+ return rb_funcall(cPointer, rb_intern("[]"), 1,
+ PTR2NUM((void *)retval.pointer));
+ case TYPE_CHAR:
+ case TYPE_SHORT:
+ case TYPE_INT:
+ return INT2NUM(retval.sint);
+ case TYPE_LONG:
+ if (signed_p) return LONG2NUM(retval.slong);
+ return ULONG2NUM(retval.ulong);
+#if HAVE_LONG_LONG
+ case TYPE_LONG_LONG:
+ return rb_ll2inum(retval.long_long);
+ break;
+#endif
+ case TYPE_FLOAT:
+ return rb_float_new(retval.ffloat);
+ case TYPE_DOUBLE:
+ return rb_float_new(retval.ddouble);
+ default:
+ rb_raise(rb_eRuntimeError, "unknown type %d", type);
+ }
+}
+
+/* vim: set noet sw=4 sts=4 */
diff --git a/ext/fiddle/conversions.h b/ext/fiddle/conversions.h
new file mode 100644
index 00000000000..166c5d9af42
--- /dev/null
+++ b/ext/fiddle/conversions.h
@@ -0,0 +1,41 @@
+#ifndef FIDDLE_CONVERSIONS_H
+#define FIDDLE_CONVERSIONS_H
+
+#include <fiddle.h>
+
+typedef union
+{
+ unsigned char uchar; /* ffi_type_uchar */
+ signed char schar; /* ffi_type_schar */
+ unsigned short ushort; /* ffi_type_sshort */
+ signed short sshort; /* ffi_type_ushort */
+ unsigned int uint; /* ffi_type_uint */
+ signed int sint; /* ffi_type_sint */
+ unsigned long ulong; /* ffi_type_ulong */
+ signed long slong; /* ffi_type_slong */
+ float ffloat; /* ffi_type_float */
+ double ddouble; /* ffi_type_double */
+#if HAVE_LONG_LONG
+ unsigned LONG_LONG long_long; /* ffi_type_uint64 */
+#endif
+ void * pointer; /* ffi_type_pointer */
+} fiddle_generic;
+
+ffi_type * int_to_ffi_type(int type);
+void value_to_generic(int type, VALUE src, fiddle_generic * dst);
+VALUE generic_to_value(VALUE rettype, fiddle_generic retval);
+
+#define VALUE2GENERIC(_type, _src, _dst) value_to_generic(_type, _src, _dst)
+#define INT2FFI_TYPE(_type) int_to_ffi_type(_type)
+#define GENERIC2VALUE(_type, _retval) generic_to_value(_type, _retval)
+
+#if SIZEOF_VOIDP == SIZEOF_LONG
+# define PTR2NUM(x) (ULONG2NUM((unsigned long)(x)))
+# define NUM2PTR(x) ((void*)(NUM2ULONG(x)))
+#else
+/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */
+# define PTR2NUM(x) (ULL2NUM((unsigned long long)(x)))
+# define NUM2PTR(x) ((void*)(NUM2ULL(x)))
+#endif
+
+#endif
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
new file mode 100644
index 00000000000..87c5c9e6333
--- /dev/null
+++ b/ext/fiddle/extconf.rb
@@ -0,0 +1,23 @@
+require 'mkmf'
+
+# :stopdoc:
+
+dir_config 'libffi'
+
+unless have_header('ffi.h')
+ if have_header('ffi/ffi.h')
+ $defs.push(format('-DUSE_HEADER_HACKS'))
+ else
+ abort "ffi.h is missing. Please install libffi."
+ end
+end
+
+unless have_library('ffi')
+ abort "libffi is missing. Please install libffi."
+end
+
+have_header 'sys/mman.h'
+
+create_makefile 'fiddle'
+
+# :startdoc:
diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
new file mode 100644
index 00000000000..78e21c57cfb
--- /dev/null
+++ b/ext/fiddle/fiddle.c
@@ -0,0 +1,30 @@
+#include <fiddle.h>
+
+VALUE mFiddle;
+
+void Init_fiddle()
+{
+ mFiddle = rb_define_module("Fiddle");
+
+ rb_define_const(mFiddle, "TYPE_VOID", INT2NUM(TYPE_VOID));
+ rb_define_const(mFiddle, "TYPE_VOIDP", INT2NUM(TYPE_VOIDP));
+ rb_define_const(mFiddle, "TYPE_CHAR", INT2NUM(TYPE_CHAR));
+ rb_define_const(mFiddle, "TYPE_SHORT", INT2NUM(TYPE_SHORT));
+ rb_define_const(mFiddle, "TYPE_INT", INT2NUM(TYPE_INT));
+ rb_define_const(mFiddle, "TYPE_LONG", INT2NUM(TYPE_LONG));
+#if HAVE_LONG_LONG
+ rb_define_const(mFiddle, "TYPE_LONG_LONG", INT2NUM(TYPE_LONG_LONG));
+#endif
+ rb_define_const(mFiddle, "TYPE_FLOAT", INT2NUM(TYPE_FLOAT));
+ rb_define_const(mFiddle, "TYPE_DOUBLE", INT2NUM(TYPE_DOUBLE));
+
+#if defined(HAVE_WINDOWS_H)
+ rb_define_const(mFiddle, "WINDOWS", Qtrue);
+#else
+ rb_define_const(mFiddle, "WINDOWS", Qfalse);
+#endif
+
+ Init_fiddle_function();
+ Init_fiddle_closure();
+}
+/* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
new file mode 100644
index 00000000000..0c573871dc7
--- /dev/null
+++ b/ext/fiddle/fiddle.h
@@ -0,0 +1,45 @@
+#ifndef FIDDLE_H
+#define FIDDLE_H
+
+#include <ruby.h>
+#include <errno.h>
+
+#if defined(HAVE_WINDOWS_H)
+#include <windows.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef USE_HEADER_HACKS
+#include <ffi/ffi.h>
+#else
+#include <ffi.h>
+#endif
+
+#include <closure.h>
+#include <conversions.h>
+#include <function.h>
+
+/* FIXME
+ * These constants need to match up with DL. We need to refactor this to use
+ * the DL header files or vice versa.
+ */
+
+#define TYPE_VOID 0
+#define TYPE_VOIDP 1
+#define TYPE_CHAR 2
+#define TYPE_SHORT 3
+#define TYPE_INT 4
+#define TYPE_LONG 5
+#if HAVE_LONG_LONG
+#define TYPE_LONG_LONG 6
+#endif
+#define TYPE_FLOAT 7
+#define TYPE_DOUBLE 8
+
+extern VALUE mFiddle;
+
+#endif
+/* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c
new file mode 100644
index 00000000000..c547d82554c
--- /dev/null
+++ b/ext/fiddle/function.c
@@ -0,0 +1,155 @@
+#include <fiddle.h>
+
+VALUE cFiddleFunction;
+
+static void
+deallocate(void *p)
+{
+ ffi_cif *ptr = p;
+ if (ptr->arg_types) xfree(ptr->arg_types);
+ xfree(ptr);
+}
+
+static size_t
+function_memsize(const void *p)
+{
+ /* const */ffi_cif *ptr = (ffi_cif *)p;
+ size_t size = 0;
+
+ if (ptr) {
+ size += sizeof(*ptr);
+#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
+ size += ffi_raw_size(ptr);
+#endif
+ }
+ return size;
+}
+
+const rb_data_type_t function_data_type = {
+ "fiddle/function",
+ 0, deallocate, function_memsize,
+};
+
+static VALUE
+allocate(VALUE klass)
+{
+ ffi_cif * cif;
+
+ return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
+}
+
+static VALUE
+initialize(int argc, VALUE argv[], VALUE self)
+{
+ ffi_cif * cif;
+ ffi_type **arg_types;
+ ffi_status result;
+ VALUE ptr, args, ret_type, abi;
+ int i;
+
+ rb_scan_args(argc, argv, "31", &ptr, &args, &ret_type, &abi);
+ if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
+
+ Check_Type(args, T_ARRAY);
+
+ rb_iv_set(self, "@ptr", ptr);
+ rb_iv_set(self, "@args", args);
+ rb_iv_set(self, "@return_type", ret_type);
+ rb_iv_set(self, "@abi", abi);
+
+ TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
+
+ arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
+
+ for (i = 0; i < RARRAY_LEN(args); i++) {
+ int type = NUM2INT(RARRAY_PTR(args)[i]);
+ arg_types[i] = INT2FFI_TYPE(type);
+ }
+ arg_types[RARRAY_LEN(args)] = NULL;
+
+ result = ffi_prep_cif (
+ cif,
+ NUM2INT(abi),
+ RARRAY_LENINT(args),
+ INT2FFI_TYPE(NUM2INT(ret_type)),
+ arg_types);
+
+ if (result)
+ rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
+
+ return self;
+}
+
+static VALUE
+function_call(int argc, VALUE argv[], VALUE self)
+{
+ ffi_cif * cif;
+ fiddle_generic retval;
+ fiddle_generic *generic_args;
+ void **values;
+ void * fun_ptr;
+ VALUE cfunc, types, cPointer;
+ int i;
+
+ cfunc = rb_iv_get(self, "@ptr");
+ types = rb_iv_get(self, "@args");
+ cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
+
+ if(argc != RARRAY_LENINT(types)) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+ argc, RARRAY_LENINT(types));
+ }
+
+ TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
+
+ values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
+ generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
+
+ for (i = 0; i < argc; i++) {
+ VALUE type = RARRAY_PTR(types)[i];
+ VALUE src = argv[i];
+
+ if(NUM2INT(type) == TYPE_VOIDP) {
+ if(NIL_P(src)) {
+ src = INT2NUM(0);
+ } else if(cPointer != CLASS_OF(src)) {
+ src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
+ }
+ src = rb_Integer(src);
+ }
+
+ VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
+ values[i] = (void *)&generic_args[i];
+ }
+ values[argc] = NULL;
+
+ ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
+
+ rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
+#if defined(HAVE_WINDOWS_H)
+ rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
+#endif
+
+ xfree(values);
+ xfree(generic_args);
+
+ return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
+}
+
+void
+Init_fiddle_function(void)
+{
+ cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
+
+ rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
+
+#ifdef FFI_STDCALL
+ rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
+#endif
+
+ rb_define_alloc_func(cFiddleFunction, allocate);
+
+ rb_define_method(cFiddleFunction, "call", function_call, -1);
+ rb_define_method(cFiddleFunction, "initialize", initialize, -1);
+}
+/* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/function.h b/ext/fiddle/function.h
new file mode 100644
index 00000000000..e5465ab64fb
--- /dev/null
+++ b/ext/fiddle/function.h
@@ -0,0 +1,8 @@
+#ifndef FIDDLE_FUNCTION_H
+#define FIDDLE_FUNCTION_H
+
+#include <fiddle.h>
+
+void Init_fiddle_function();
+
+#endif
diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb
new file mode 100644
index 00000000000..bc4017eee0a
--- /dev/null
+++ b/ext/fiddle/lib/fiddle.rb
@@ -0,0 +1,27 @@
+require 'fiddle.so'
+require 'fiddle/function'
+require 'fiddle/closure'
+require 'dl'
+
+module Fiddle
+ Pointer = DL::CPtr
+
+ if WINDOWS
+ def self.win32_last_error
+ Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
+ end
+
+ def self.win32_last_error= error
+ Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
+ end
+ end
+
+ def self.last_error
+ Thread.current[:__FIDDLE_LAST_ERROR__]
+ end
+
+ def self.last_error= error
+ Thread.current[:__DL2_LAST_ERROR__] = error
+ Thread.current[:__FIDDLE_LAST_ERROR__] = error
+ end
+end
diff --git a/ext/fiddle/lib/fiddle/closure.rb b/ext/fiddle/lib/fiddle/closure.rb
new file mode 100644
index 00000000000..dc2b7a65bec
--- /dev/null
+++ b/ext/fiddle/lib/fiddle/closure.rb
@@ -0,0 +1,17 @@
+module Fiddle
+ class Closure
+ attr_reader :ctype
+ attr_reader :args
+
+ class BlockCaller < Fiddle::Closure
+ def initialize ctype, args, abi = Fiddle::Function::DEFAULT, &block
+ super(ctype, args, abi)
+ @block = block
+ end
+
+ def call *args
+ @block.call(*args)
+ end
+ end
+ end
+end
diff --git a/ext/fiddle/lib/fiddle/function.rb b/ext/fiddle/lib/fiddle/function.rb
new file mode 100644
index 00000000000..7b9e7358748
--- /dev/null
+++ b/ext/fiddle/lib/fiddle/function.rb
@@ -0,0 +1,5 @@
+module Fiddle
+ class Function
+ attr_reader :abi
+ end
+end