Ruby
2.0.0p247(2013-06-27revision41674)
|
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"); /* let rdoc know about mFiddle */ 00261 #endif 00262 00263 /* 00264 * Document-class: Fiddle::Closure 00265 * 00266 * == Description 00267 * 00268 * An FFI closure wrapper, for handling callbacks. 00269 * 00270 * == Example 00271 * 00272 * closure = Class.new(Fiddle::Closure) { 00273 * def call 00274 * 10 00275 * end 00276 * }.new(Fiddle::TYPE_INT, []) 00277 * #=> #<#<Class:0x0000000150d308>:0x0000000150d240> 00278 * func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT) 00279 * #=> #<Fiddle::Function:0x00000001516e58> 00280 * func.call 00281 * #=> 10 00282 */ 00283 cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject); 00284 00285 rb_define_alloc_func(cFiddleClosure, allocate); 00286 00287 /* 00288 * Document-method: new 00289 * 00290 * call-seq: new(ret, args, abi = Fiddle::DEFAULT) 00291 * 00292 * Construct a new Closure object. 00293 * 00294 * * +ret+ is the C type to be returned 00295 * * +args+ is an Array of arguments, passed to the callback function 00296 * * +abi+ is the abi of the closure 00297 * 00298 * If there is an error in preparing the ffi_cif or ffi_prep_closure, 00299 * then a RuntimeError will be raised. 00300 */ 00301 rb_define_method(cFiddleClosure, "initialize", initialize, -1); 00302 00303 /* 00304 * Document-method: to_i 00305 * 00306 * Returns the memory address for this closure 00307 */ 00308 rb_define_method(cFiddleClosure, "to_i", to_i, 0); 00309 } 00310 /* vim: set noet sw=4 sts=4 */ 00311