00001 #include <fiddle.h>
00002
00003 VALUE cFiddleClosure;
00004
00005 typedef struct {
00006 void * code;
00007 ffi_closure *pcl;
00008 ffi_cif * cif;
00009 int argc;
00010 ffi_type **argv;
00011 } fiddle_closure;
00012
00013 #if defined(MACOSX) || defined(__linux) || defined(__OpenBSD__)
00014 #define DONT_USE_FFI_CLOSURE_ALLOC
00015 #endif
00016
00017 static void
00018 dealloc(void * ptr)
00019 {
00020 fiddle_closure * cls = (fiddle_closure *)ptr;
00021 #ifndef DONT_USE_FFI_CLOSURE_ALLOC
00022 ffi_closure_free(cls->pcl);
00023 #else
00024 munmap(cls->pcl, sizeof(cls->pcl));
00025 #endif
00026 xfree(cls->cif);
00027 if (cls->argv) xfree(cls->argv);
00028 xfree(cls);
00029 }
00030
00031 static size_t
00032 closure_memsize(const void * ptr)
00033 {
00034 fiddle_closure * cls = (fiddle_closure *)ptr;
00035 size_t size = 0;
00036
00037 if (ptr) {
00038 size += sizeof(*cls);
00039 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
00040 size += ffi_raw_size(cls->cif);
00041 #endif
00042 size += sizeof(*cls->argv);
00043 size += sizeof(ffi_closure);
00044 }
00045 return size;
00046 }
00047
00048 const rb_data_type_t closure_data_type = {
00049 "fiddle/closure",
00050 0, dealloc, closure_memsize,
00051 };
00052
00053 void
00054 callback(ffi_cif *cif, void *resp, void **args, void *ctx)
00055 {
00056 VALUE self = (VALUE)ctx;
00057 VALUE rbargs = rb_iv_get(self, "@args");
00058 VALUE ctype = rb_iv_get(self, "@ctype");
00059 int argc = RARRAY_LENINT(rbargs);
00060 VALUE *params = xcalloc(argc, sizeof(VALUE *));
00061 VALUE ret;
00062 VALUE cPointer;
00063 int i, type;
00064
00065 cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
00066
00067 for (i = 0; i < argc; i++) {
00068 type = NUM2INT(RARRAY_PTR(rbargs)[i]);
00069 switch (type) {
00070 case TYPE_VOID:
00071 argc = 0;
00072 break;
00073 case TYPE_INT:
00074 params[i] = INT2NUM(*(int *)args[i]);
00075 break;
00076 case TYPE_VOIDP:
00077 params[i] = rb_funcall(cPointer, rb_intern("[]"), 1,
00078 PTR2NUM(*(void **)args[i]));
00079 break;
00080 case TYPE_LONG:
00081 params[i] = LONG2NUM(*(long *)args[i]);
00082 break;
00083 case TYPE_CHAR:
00084 params[i] = INT2NUM(*(char *)args[i]);
00085 break;
00086 case TYPE_DOUBLE:
00087 params[i] = rb_float_new(*(double *)args[i]);
00088 break;
00089 case TYPE_FLOAT:
00090 params[i] = rb_float_new(*(float *)args[i]);
00091 break;
00092 #if HAVE_LONG_LONG
00093 case TYPE_LONG_LONG:
00094 params[i] = rb_ull2inum(*(unsigned LONG_LONG *)args[i]);
00095 break;
00096 #endif
00097 default:
00098 rb_raise(rb_eRuntimeError, "closure args: %d", type);
00099 }
00100 }
00101
00102 ret = rb_funcall2(self, rb_intern("call"), argc, params);
00103
00104 type = NUM2INT(ctype);
00105 switch (type) {
00106 case TYPE_VOID:
00107 break;
00108 case TYPE_LONG:
00109 *(long *)resp = NUM2LONG(ret);
00110 break;
00111 case TYPE_CHAR:
00112 *(char *)resp = NUM2INT(ret);
00113 break;
00114 case TYPE_VOIDP:
00115 *(void **)resp = NUM2PTR(ret);
00116 break;
00117 case TYPE_INT:
00118 *(int *)resp = NUM2INT(ret);
00119 break;
00120 case TYPE_DOUBLE:
00121 *(double *)resp = NUM2DBL(ret);
00122 break;
00123 case TYPE_FLOAT:
00124 *(float *)resp = (float)NUM2DBL(ret);
00125 break;
00126 #if HAVE_LONG_LONG
00127 case TYPE_LONG_LONG:
00128 *(unsigned LONG_LONG *)resp = rb_big2ull(ret);
00129 break;
00130 #endif
00131 default:
00132 rb_raise(rb_eRuntimeError, "closure retval: %d", type);
00133 }
00134 xfree(params);
00135 }
00136
00137 static VALUE
00138 allocate(VALUE klass)
00139 {
00140 fiddle_closure * closure;
00141
00142 VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
00143 &closure_data_type, closure);
00144
00145 #ifndef DONT_USE_FFI_CLOSURE_ALLOC
00146 closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
00147 #else
00148 closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
00149 MAP_ANON | MAP_PRIVATE, -1, 0);
00150 #endif
00151 closure->cif = xmalloc(sizeof(ffi_cif));
00152
00153 return i;
00154 }
00155
00156 static VALUE
00157 initialize(int rbargc, VALUE argv[], VALUE self)
00158 {
00159 VALUE ret;
00160 VALUE args;
00161 VALUE abi;
00162 fiddle_closure * cl;
00163 ffi_cif * cif;
00164 ffi_closure *pcl;
00165 ffi_status result;
00166 int i, argc;
00167
00168 if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
00169 abi = INT2NUM(FFI_DEFAULT_ABI);
00170
00171 Check_Type(args, T_ARRAY);
00172
00173 argc = RARRAY_LENINT(args);
00174
00175 TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00176
00177 cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
00178
00179 for (i = 0; i < argc; i++) {
00180 int type = NUM2INT(RARRAY_PTR(args)[i]);
00181 cl->argv[i] = INT2FFI_TYPE(type);
00182 }
00183 cl->argv[argc] = NULL;
00184
00185 rb_iv_set(self, "@ctype", ret);
00186 rb_iv_set(self, "@args", args);
00187
00188 cif = cl->cif;
00189 pcl = cl->pcl;
00190
00191 result = ffi_prep_cif(cif, NUM2INT(abi), argc,
00192 INT2FFI_TYPE(NUM2INT(ret)),
00193 cl->argv);
00194
00195 if (FFI_OK != result)
00196 rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
00197
00198 #ifndef DONT_USE_FFI_CLOSURE_ALLOC
00199 result = ffi_prep_closure_loc(pcl, cif, callback,
00200 (void *)self, cl->code);
00201 #else
00202 result = ffi_prep_closure(pcl, cif, callback, (void *)self);
00203 cl->code = (void *)pcl;
00204 mprotect(pcl, sizeof(pcl), PROT_READ | PROT_EXEC);
00205 #endif
00206
00207 if (FFI_OK != result)
00208 rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
00209
00210 return self;
00211 }
00212
00213 static VALUE
00214 to_i(VALUE self)
00215 {
00216 fiddle_closure * cl;
00217 void *code;
00218
00219 TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00220
00221 code = cl->code;
00222
00223 return PTR2NUM(code);
00224 }
00225
00226 void
00227 Init_fiddle_closure()
00228 {
00229 cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
00230
00231 rb_define_alloc_func(cFiddleClosure, allocate);
00232
00233 rb_define_method(cFiddleClosure, "initialize", initialize, -1);
00234 rb_define_method(cFiddleClosure, "to_i", to_i, 0);
00235 }
00236
00237