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 if (cls->argv) xfree(cls->argv);
00027 xfree(cls);
00028 }
00029
00030 static size_t
00031 closure_memsize(const void * ptr)
00032 {
00033 fiddle_closure * cls = (fiddle_closure *)ptr;
00034 size_t size = 0;
00035
00036 if (ptr) {
00037 size += sizeof(*cls);
00038 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
00039 size += ffi_raw_size(&cls->cif);
00040 #endif
00041 size += sizeof(*cls->argv);
00042 size += sizeof(ffi_closure);
00043 }
00044 return size;
00045 }
00046
00047 const rb_data_type_t closure_data_type = {
00048 "fiddle/closure",
00049 {0, dealloc, closure_memsize,},
00050 };
00051
00052 void
00053 callback(ffi_cif *cif, void *resp, void **args, void *ctx)
00054 {
00055 VALUE self = (VALUE)ctx;
00056 VALUE rbargs = rb_iv_get(self, "@args");
00057 VALUE ctype = rb_iv_get(self, "@ctype");
00058 int argc = RARRAY_LENINT(rbargs);
00059 VALUE params = rb_ary_tmp_new(argc);
00060 VALUE ret;
00061 VALUE cPointer;
00062 int i, type;
00063
00064 cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
00065
00066 for (i = 0; i < argc; i++) {
00067 type = NUM2INT(RARRAY_PTR(rbargs)[i]);
00068 switch (type) {
00069 case TYPE_VOID:
00070 argc = 0;
00071 break;
00072 case TYPE_INT:
00073 rb_ary_push(params, INT2NUM(*(int *)args[i]));
00074 break;
00075 case -TYPE_INT:
00076 rb_ary_push(params, UINT2NUM(*(unsigned int *)args[i]));
00077 break;
00078 case TYPE_VOIDP:
00079 rb_ary_push(params,
00080 rb_funcall(cPointer, rb_intern("[]"), 1,
00081 PTR2NUM(*(void **)args[i])));
00082 break;
00083 case TYPE_LONG:
00084 rb_ary_push(params, LONG2NUM(*(long *)args[i]));
00085 break;
00086 case -TYPE_LONG:
00087 rb_ary_push(params, ULONG2NUM(*(unsigned long *)args[i]));
00088 break;
00089 case TYPE_CHAR:
00090 rb_ary_push(params, INT2NUM(*(signed char *)args[i]));
00091 break;
00092 case -TYPE_CHAR:
00093 rb_ary_push(params, UINT2NUM(*(unsigned char *)args[i]));
00094 break;
00095 case TYPE_SHORT:
00096 rb_ary_push(params, INT2NUM(*(signed short *)args[i]));
00097 break;
00098 case -TYPE_SHORT:
00099 rb_ary_push(params, UINT2NUM(*(unsigned short *)args[i]));
00100 break;
00101 case TYPE_DOUBLE:
00102 rb_ary_push(params, rb_float_new(*(double *)args[i]));
00103 break;
00104 case TYPE_FLOAT:
00105 rb_ary_push(params, rb_float_new(*(float *)args[i]));
00106 break;
00107 #if HAVE_LONG_LONG
00108 case TYPE_LONG_LONG:
00109 rb_ary_push(params, LL2NUM(*(LONG_LONG *)args[i]));
00110 break;
00111 case -TYPE_LONG_LONG:
00112 rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)args[i]));
00113 break;
00114 #endif
00115 default:
00116 rb_raise(rb_eRuntimeError, "closure args: %d", type);
00117 }
00118 }
00119
00120 ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_PTR(params));
00121 RB_GC_GUARD(params);
00122
00123 type = NUM2INT(ctype);
00124 switch (type) {
00125 case TYPE_VOID:
00126 break;
00127 case TYPE_LONG:
00128 *(long *)resp = NUM2LONG(ret);
00129 break;
00130 case -TYPE_LONG:
00131 *(unsigned long *)resp = NUM2ULONG(ret);
00132 break;
00133 case TYPE_CHAR:
00134 case TYPE_SHORT:
00135 case TYPE_INT:
00136 *(ffi_sarg *)resp = NUM2INT(ret);
00137 break;
00138 case -TYPE_CHAR:
00139 case -TYPE_SHORT:
00140 case -TYPE_INT:
00141 *(ffi_arg *)resp = NUM2UINT(ret);
00142 break;
00143 case TYPE_VOIDP:
00144 *(void **)resp = NUM2PTR(ret);
00145 break;
00146 case TYPE_DOUBLE:
00147 *(double *)resp = NUM2DBL(ret);
00148 break;
00149 case TYPE_FLOAT:
00150 *(float *)resp = (float)NUM2DBL(ret);
00151 break;
00152 #if HAVE_LONG_LONG
00153 case TYPE_LONG_LONG:
00154 *(LONG_LONG *)resp = NUM2LL(ret);
00155 break;
00156 case -TYPE_LONG_LONG:
00157 *(unsigned LONG_LONG *)resp = NUM2ULL(ret);
00158 break;
00159 #endif
00160 default:
00161 rb_raise(rb_eRuntimeError, "closure retval: %d", type);
00162 }
00163 }
00164
00165 static VALUE
00166 allocate(VALUE klass)
00167 {
00168 fiddle_closure * closure;
00169
00170 VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
00171 &closure_data_type, closure);
00172
00173 #ifndef DONT_USE_FFI_CLOSURE_ALLOC
00174 closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
00175 #else
00176 closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
00177 MAP_ANON | MAP_PRIVATE, -1, 0);
00178 #endif
00179
00180 return i;
00181 }
00182
00183 static VALUE
00184 initialize(int rbargc, VALUE argv[], VALUE self)
00185 {
00186 VALUE ret;
00187 VALUE args;
00188 VALUE abi;
00189 fiddle_closure * cl;
00190 ffi_cif * cif;
00191 ffi_closure *pcl;
00192 ffi_status result;
00193 int i, argc;
00194
00195 if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
00196 abi = INT2NUM(FFI_DEFAULT_ABI);
00197
00198 Check_Type(args, T_ARRAY);
00199
00200 argc = RARRAY_LENINT(args);
00201
00202 TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00203
00204 cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
00205
00206 for (i = 0; i < argc; i++) {
00207 int type = NUM2INT(RARRAY_PTR(args)[i]);
00208 cl->argv[i] = INT2FFI_TYPE(type);
00209 }
00210 cl->argv[argc] = NULL;
00211
00212 rb_iv_set(self, "@ctype", ret);
00213 rb_iv_set(self, "@args", args);
00214
00215 cif = &cl->cif;
00216 pcl = cl->pcl;
00217
00218 result = ffi_prep_cif(cif, NUM2INT(abi), argc,
00219 INT2FFI_TYPE(NUM2INT(ret)),
00220 cl->argv);
00221
00222 if (FFI_OK != result)
00223 rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
00224
00225 #ifndef DONT_USE_FFI_CLOSURE_ALLOC
00226 result = ffi_prep_closure_loc(pcl, cif, callback,
00227 (void *)self, cl->code);
00228 #else
00229 result = ffi_prep_closure(pcl, cif, callback, (void *)self);
00230 cl->code = (void *)pcl;
00231 i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC);
00232 if (i) {
00233 rb_sys_fail("mprotect");
00234 }
00235 #endif
00236
00237 if (FFI_OK != result)
00238 rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
00239
00240 return self;
00241 }
00242
00243 static VALUE
00244 to_i(VALUE self)
00245 {
00246 fiddle_closure * cl;
00247 void *code;
00248
00249 TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00250
00251 code = cl->code;
00252
00253 return PTR2NUM(code);
00254 }
00255
00256 void
00257 Init_fiddle_closure()
00258 {
00259 #if 0
00260 mFiddle = rb_define_module("Fiddle");
00261 #endif
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283 cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
00284
00285 rb_define_alloc_func(cFiddleClosure, allocate);
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301 rb_define_method(cFiddleClosure, "initialize", initialize, -1);
00302
00303
00304
00305
00306
00307
00308 rb_define_method(cFiddleClosure, "to_i", to_i, 0);
00309 }
00310
00311