2010-05-06 10:59:24 +04:00
|
|
|
#include <fiddle.h>
|
|
|
|
|
|
|
|
VALUE cFiddleClosure;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
void * code;
|
|
|
|
ffi_closure *pcl;
|
2010-10-30 04:08:30 +04:00
|
|
|
ffi_cif cif;
|
2010-05-06 10:59:24 +04:00
|
|
|
int argc;
|
|
|
|
ffi_type **argv;
|
|
|
|
} fiddle_closure;
|
|
|
|
|
2010-09-14 21:15:30 +04:00
|
|
|
#if defined(MACOSX) || defined(__linux) || defined(__OpenBSD__)
|
2010-06-13 23:06:25 +04:00
|
|
|
#define DONT_USE_FFI_CLOSURE_ALLOC
|
|
|
|
#endif
|
|
|
|
|
2010-05-06 10:59:24 +04:00
|
|
|
static void
|
|
|
|
dealloc(void * ptr)
|
|
|
|
{
|
|
|
|
fiddle_closure * cls = (fiddle_closure *)ptr;
|
2010-06-13 23:06:25 +04:00
|
|
|
#ifndef DONT_USE_FFI_CLOSURE_ALLOC
|
2010-05-06 10:59:24 +04:00
|
|
|
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
|
2010-10-30 04:08:30 +04:00
|
|
|
size += ffi_raw_size(&cls->cif);
|
2010-05-06 10:59:24 +04:00
|
|
|
#endif
|
|
|
|
size += sizeof(*cls->argv);
|
|
|
|
size += sizeof(ffi_closure);
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
const rb_data_type_t closure_data_type = {
|
|
|
|
"fiddle/closure",
|
2010-07-20 13:23:08 +04:00
|
|
|
{0, dealloc, closure_memsize,},
|
2010-05-06 10:59:24 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
2011-07-28 18:47:51 +04:00
|
|
|
VALUE params = rb_ary_tmp_new(argc);
|
2010-05-06 10:59:24 +04:00
|
|
|
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:
|
2011-07-28 18:47:51 +04:00
|
|
|
rb_ary_push(params, INT2NUM(*(int *)args[i]));
|
2010-05-06 10:59:24 +04:00
|
|
|
break;
|
|
|
|
case TYPE_VOIDP:
|
2011-07-28 18:47:51 +04:00
|
|
|
rb_ary_push(params,
|
|
|
|
rb_funcall(cPointer, rb_intern("[]"), 1,
|
|
|
|
PTR2NUM(*(void **)args[i])));
|
2010-05-06 10:59:24 +04:00
|
|
|
break;
|
|
|
|
case TYPE_LONG:
|
2011-07-28 18:47:51 +04:00
|
|
|
rb_ary_push(params, LONG2NUM(*(long *)args[i]));
|
2010-05-06 10:59:24 +04:00
|
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
2011-07-28 18:47:51 +04:00
|
|
|
rb_ary_push(params, INT2NUM(*(char *)args[i]));
|
2010-05-06 10:59:24 +04:00
|
|
|
break;
|
|
|
|
case TYPE_DOUBLE:
|
2011-07-28 18:47:51 +04:00
|
|
|
rb_ary_push(params, rb_float_new(*(double *)args[i]));
|
2010-05-06 10:59:24 +04:00
|
|
|
break;
|
|
|
|
case TYPE_FLOAT:
|
2011-07-28 18:47:51 +04:00
|
|
|
rb_ary_push(params, rb_float_new(*(float *)args[i]));
|
2010-05-06 10:59:24 +04:00
|
|
|
break;
|
|
|
|
#if HAVE_LONG_LONG
|
|
|
|
case TYPE_LONG_LONG:
|
2011-07-28 18:47:51 +04:00
|
|
|
rb_ary_push(params, rb_ull2inum(*(unsigned LONG_LONG *)args[i]));
|
2010-05-06 10:59:24 +04:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
rb_raise(rb_eRuntimeError, "closure args: %d", type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-28 18:47:51 +04:00
|
|
|
ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_PTR(params));
|
|
|
|
RB_GC_GUARD(params);
|
2010-05-06 10:59:24 +04:00
|
|
|
|
|
|
|
type = NUM2INT(ctype);
|
|
|
|
switch (type) {
|
|
|
|
case TYPE_VOID:
|
|
|
|
break;
|
|
|
|
case TYPE_LONG:
|
|
|
|
*(long *)resp = NUM2LONG(ret);
|
|
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
2011-08-09 09:31:47 +04:00
|
|
|
case TYPE_SHORT:
|
|
|
|
case TYPE_INT:
|
|
|
|
*(ffi_sarg *)resp = NUM2INT(ret);
|
2010-05-06 10:59:24 +04:00
|
|
|
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:
|
|
|
|
*(unsigned LONG_LONG *)resp = rb_big2ull(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);
|
|
|
|
|
2010-06-13 23:06:25 +04:00
|
|
|
#ifndef DONT_USE_FFI_CLOSURE_ALLOC
|
2010-05-06 10:59:24 +04:00
|
|
|
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);
|
|
|
|
|
2010-10-30 04:08:30 +04:00
|
|
|
cif = &cl->cif;
|
2010-05-06 10:59:24 +04:00
|
|
|
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);
|
|
|
|
|
2010-06-13 23:06:25 +04:00
|
|
|
#ifndef DONT_USE_FFI_CLOSURE_ALLOC
|
2010-05-06 10:59:24 +04:00
|
|
|
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 */
|