diff options
author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 |
---|---|---|
committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 |
commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/ext/fiddle/closure.c |
Fresh start
Diffstat (limited to 'jni/ruby/ext/fiddle/closure.c')
-rw-r--r-- | jni/ruby/ext/fiddle/closure.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/jni/ruby/ext/fiddle/closure.c b/jni/ruby/ext/fiddle/closure.c new file mode 100644 index 0000000..d857bfe --- /dev/null +++ b/jni/ruby/ext/fiddle/closure.c @@ -0,0 +1,316 @@ +#include <fiddle.h> + +VALUE cFiddleClosure; + +typedef struct { + void * code; + ffi_closure *pcl; + ffi_cif cif; + int argc; + ffi_type **argv; +} fiddle_closure; + +#if defined(USE_FFI_CLOSURE_ALLOC) +#elif defined(__OpenBSD__) || defined(__APPLE__) || defined(__linux__) +# define USE_FFI_CLOSURE_ALLOC 0 +#elif defined(RUBY_LIBFFI_MODVERSION) && RUBY_LIBFFI_MODVERSION < 3000005 && \ + (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64)) +# define USE_FFI_CLOSURE_ALLOC 0 +#else +# define USE_FFI_CLOSURE_ALLOC 1 +#endif + +static void +dealloc(void * ptr) +{ + fiddle_closure * cls = (fiddle_closure *)ptr; +#if USE_FFI_CLOSURE_ALLOC + ffi_closure_free(cls->pcl); +#else + munmap(cls->pcl, sizeof(*cls->pcl)); +#endif + 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 = rb_ary_tmp_new(argc); + 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: + rb_ary_push(params, INT2NUM(*(int *)args[i])); + break; + case -TYPE_INT: + rb_ary_push(params, UINT2NUM(*(unsigned int *)args[i])); + break; + case TYPE_VOIDP: + rb_ary_push(params, + rb_funcall(cPointer, rb_intern("[]"), 1, + PTR2NUM(*(void **)args[i]))); + break; + case TYPE_LONG: + rb_ary_push(params, LONG2NUM(*(long *)args[i])); + break; + case -TYPE_LONG: + rb_ary_push(params, ULONG2NUM(*(unsigned long *)args[i])); + break; + case TYPE_CHAR: + rb_ary_push(params, INT2NUM(*(signed char *)args[i])); + break; + case -TYPE_CHAR: + rb_ary_push(params, UINT2NUM(*(unsigned char *)args[i])); + break; + case TYPE_SHORT: + rb_ary_push(params, INT2NUM(*(signed short *)args[i])); + break; + case -TYPE_SHORT: + rb_ary_push(params, UINT2NUM(*(unsigned short *)args[i])); + break; + case TYPE_DOUBLE: + rb_ary_push(params, rb_float_new(*(double *)args[i])); + break; + case TYPE_FLOAT: + rb_ary_push(params, rb_float_new(*(float *)args[i])); + break; +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + rb_ary_push(params, LL2NUM(*(LONG_LONG *)args[i])); + break; + case -TYPE_LONG_LONG: + rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)args[i])); + break; +#endif + default: + rb_raise(rb_eRuntimeError, "closure args: %d", type); + } + } + + ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_PTR(params)); + RB_GC_GUARD(params); + + type = NUM2INT(ctype); + switch (type) { + case TYPE_VOID: + break; + case TYPE_LONG: + *(long *)resp = NUM2LONG(ret); + break; + case -TYPE_LONG: + *(unsigned long *)resp = NUM2ULONG(ret); + break; + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: + *(ffi_sarg *)resp = NUM2INT(ret); + break; + case -TYPE_CHAR: + case -TYPE_SHORT: + case -TYPE_INT: + *(ffi_arg *)resp = NUM2UINT(ret); + break; + case TYPE_VOIDP: + *(void **)resp = NUM2PTR(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: + *(LONG_LONG *)resp = NUM2LL(ret); + break; + case -TYPE_LONG_LONG: + *(unsigned LONG_LONG *)resp = NUM2ULL(ret); + break; +#endif + default: + rb_raise(rb_eRuntimeError, "closure retval: %d", type); + } +} + +static VALUE +allocate(VALUE klass) +{ + fiddle_closure * closure; + + VALUE i = TypedData_Make_Struct(klass, fiddle_closure, + &closure_data_type, closure); + +#if USE_FFI_CLOSURE_ALLOC + 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 + + 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); + +#if USE_FFI_CLOSURE_ALLOC + 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; + i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC); + if (i) { + rb_sys_fail("mprotect"); + } +#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(void) +{ +#if 0 + mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */ +#endif + + /* + * Document-class: Fiddle::Closure + * + * == Description + * + * An FFI closure wrapper, for handling callbacks. + * + * == Example + * + * closure = Class.new(Fiddle::Closure) { + * def call + * 10 + * end + * }.new(Fiddle::TYPE_INT, []) + * #=> #<#<Class:0x0000000150d308>:0x0000000150d240> + * func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT) + * #=> #<Fiddle::Function:0x00000001516e58> + * func.call + * #=> 10 + */ + cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject); + + rb_define_alloc_func(cFiddleClosure, allocate); + + /* + * Document-method: new + * + * call-seq: new(ret, args, abi = Fiddle::DEFAULT) + * + * Construct a new Closure object. + * + * * +ret+ is the C type to be returned + * * +args+ is an Array of arguments, passed to the callback function + * * +abi+ is the abi of the closure + * + * If there is an error in preparing the ffi_cif or ffi_prep_closure, + * then a RuntimeError will be raised. + */ + rb_define_method(cFiddleClosure, "initialize", initialize, -1); + + /* + * Document-method: to_i + * + * Returns the memory address for this closure + */ + rb_define_method(cFiddleClosure, "to_i", to_i, 0); +} +/* vim: set noet sw=4 sts=4 */ |