From 4bada8b864e445b6eebe1a341e30cad94dbcaf84 Mon Sep 17 00:00:00 2001 From: tenderlove Date: Thu, 6 May 2010 06:59:24 +0000 Subject: * 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 --- ext/dl/lib/dl.rb | 12 ++ ext/dl/lib/dl/callback.rb | 83 +++++++++----- ext/dl/lib/dl/func.rb | 114 ++++++++++++------- ext/dl/lib/dl/import.rb | 14 ++- ext/dl/lib/dl/value.rb | 2 +- ext/fiddle/closure.c | 232 ++++++++++++++++++++++++++++++++++++++ ext/fiddle/closure.h | 8 ++ ext/fiddle/conversions.c | 126 +++++++++++++++++++++ ext/fiddle/conversions.h | 41 +++++++ ext/fiddle/extconf.rb | 23 ++++ ext/fiddle/fiddle.c | 30 +++++ ext/fiddle/fiddle.h | 45 ++++++++ ext/fiddle/function.c | 155 +++++++++++++++++++++++++ ext/fiddle/function.h | 8 ++ ext/fiddle/lib/fiddle.rb | 27 +++++ ext/fiddle/lib/fiddle/closure.rb | 17 +++ ext/fiddle/lib/fiddle/function.rb | 5 + 17 files changed, 871 insertions(+), 71 deletions(-) create mode 100644 ext/dl/lib/dl.rb create mode 100644 ext/fiddle/closure.c create mode 100644 ext/fiddle/closure.h create mode 100644 ext/fiddle/conversions.c create mode 100644 ext/fiddle/conversions.h create mode 100644 ext/fiddle/extconf.rb create mode 100644 ext/fiddle/fiddle.c create mode 100644 ext/fiddle/fiddle.h create mode 100644 ext/fiddle/function.c create mode 100644 ext/fiddle/function.h create mode 100644 ext/fiddle/lib/fiddle.rb create mode 100644 ext/fiddle/lib/fiddle/closure.rb create mode 100644 ext/fiddle/lib/fiddle/function.rb (limited to 'ext') diff --git a/ext/dl/lib/dl.rb b/ext/dl/lib/dl.rb new file mode 100644 index 0000000000..168a18a55e --- /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 c8daaf6322..0863c70d4d 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 7a8b62e325..4d0db4ee7a 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 199354c18e..fd23bc9676 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 56dfcefa32..51f96b293b 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 0000000000..2531ef2b48 --- /dev/null +++ b/ext/fiddle/closure.c @@ -0,0 +1,232 @@ +#include + +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 0000000000..1e870e2285 --- /dev/null +++ b/ext/fiddle/closure.h @@ -0,0 +1,8 @@ +#ifndef FIDDLE_CLOSURE_H +#define FIDDLE_CLOSURE_H + +#include + +void Init_fiddle_closure(); + +#endif diff --git a/ext/fiddle/conversions.c b/ext/fiddle/conversions.c new file mode 100644 index 0000000000..bb5361a6c8 --- /dev/null +++ b/ext/fiddle/conversions.c @@ -0,0 +1,126 @@ +#include + +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 0000000000..166c5d9af4 --- /dev/null +++ b/ext/fiddle/conversions.h @@ -0,0 +1,41 @@ +#ifndef FIDDLE_CONVERSIONS_H +#define FIDDLE_CONVERSIONS_H + +#include + +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 0000000000..87c5c9e633 --- /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 0000000000..78e21c57cf --- /dev/null +++ b/ext/fiddle/fiddle.c @@ -0,0 +1,30 @@ +#include + +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 0000000000..0c573871dc --- /dev/null +++ b/ext/fiddle/fiddle.h @@ -0,0 +1,45 @@ +#ifndef FIDDLE_H +#define FIDDLE_H + +#include +#include + +#if defined(HAVE_WINDOWS_H) +#include +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef USE_HEADER_HACKS +#include +#else +#include +#endif + +#include +#include +#include + +/* 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 0000000000..c547d82554 --- /dev/null +++ b/ext/fiddle/function.c @@ -0,0 +1,155 @@ +#include + +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 0000000000..e5465ab64f --- /dev/null +++ b/ext/fiddle/function.h @@ -0,0 +1,8 @@ +#ifndef FIDDLE_FUNCTION_H +#define FIDDLE_FUNCTION_H + +#include + +void Init_fiddle_function(); + +#endif diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb new file mode 100644 index 0000000000..bc4017eee0 --- /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 0000000000..dc2b7a65be --- /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 0000000000..7b9e735874 --- /dev/null +++ b/ext/fiddle/lib/fiddle/function.rb @@ -0,0 +1,5 @@ +module Fiddle + class Function + attr_reader :abi + end +end -- cgit v1.2.3