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/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 + 12 files changed, 717 insertions(+) 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/fiddle') 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