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