Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 #include <fiddle.h> 00002 00003 VALUE cFiddleFunction; 00004 00005 static void 00006 deallocate(void *p) 00007 { 00008 ffi_cif *ptr = p; 00009 if (ptr->arg_types) xfree(ptr->arg_types); 00010 xfree(ptr); 00011 } 00012 00013 static size_t 00014 function_memsize(const void *p) 00015 { 00016 /* const */ffi_cif *ptr = (ffi_cif *)p; 00017 size_t size = 0; 00018 00019 if (ptr) { 00020 size += sizeof(*ptr); 00021 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API 00022 size += ffi_raw_size(ptr); 00023 #endif 00024 } 00025 return size; 00026 } 00027 00028 const rb_data_type_t function_data_type = { 00029 "fiddle/function", 00030 {0, deallocate, function_memsize,}, 00031 }; 00032 00033 static VALUE 00034 allocate(VALUE klass) 00035 { 00036 ffi_cif * cif; 00037 00038 return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif); 00039 } 00040 00041 VALUE 00042 rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type) 00043 { 00044 VALUE argv[3]; 00045 00046 argv[0] = address; 00047 argv[1] = arg_types; 00048 argv[2] = ret_type; 00049 00050 return rb_class_new_instance(3, argv, cFiddleFunction); 00051 } 00052 00053 static int 00054 parse_keyword_arg_i(VALUE key, VALUE value, VALUE self) 00055 { 00056 if (key == ID2SYM(rb_intern("name"))) { 00057 rb_iv_set(self, "@name", value); 00058 } else { 00059 rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, key); 00060 } 00061 return ST_CONTINUE; 00062 } 00063 00064 static VALUE 00065 initialize(int argc, VALUE argv[], VALUE self) 00066 { 00067 ffi_cif * cif; 00068 ffi_type **arg_types; 00069 ffi_status result; 00070 VALUE ptr, args, ret_type, abi, kwds; 00071 int i; 00072 00073 rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds); 00074 if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI); 00075 00076 Check_Type(args, T_ARRAY); 00077 00078 rb_iv_set(self, "@ptr", ptr); 00079 rb_iv_set(self, "@args", args); 00080 rb_iv_set(self, "@return_type", ret_type); 00081 rb_iv_set(self, "@abi", abi); 00082 00083 if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self); 00084 00085 TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); 00086 00087 arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *)); 00088 00089 for (i = 0; i < RARRAY_LEN(args); i++) { 00090 int type = NUM2INT(RARRAY_PTR(args)[i]); 00091 arg_types[i] = INT2FFI_TYPE(type); 00092 } 00093 arg_types[RARRAY_LEN(args)] = NULL; 00094 00095 result = ffi_prep_cif ( 00096 cif, 00097 NUM2INT(abi), 00098 RARRAY_LENINT(args), 00099 INT2FFI_TYPE(NUM2INT(ret_type)), 00100 arg_types); 00101 00102 if (result) 00103 rb_raise(rb_eRuntimeError, "error creating CIF %d", result); 00104 00105 return self; 00106 } 00107 00108 static VALUE 00109 function_call(int argc, VALUE argv[], VALUE self) 00110 { 00111 ffi_cif * cif; 00112 fiddle_generic retval; 00113 fiddle_generic *generic_args; 00114 void **values; 00115 VALUE cfunc, types, cPointer; 00116 int i; 00117 00118 cfunc = rb_iv_get(self, "@ptr"); 00119 types = rb_iv_get(self, "@args"); 00120 cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); 00121 00122 if(argc != RARRAY_LENINT(types)) { 00123 rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", 00124 argc, RARRAY_LENINT(types)); 00125 } 00126 00127 TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); 00128 00129 if (rb_safe_level() >= 1) { 00130 for (i = 0; i < argc; i++) { 00131 VALUE src = argv[i]; 00132 if (OBJ_TAINTED(src)) { 00133 rb_raise(rb_eSecurityError, "tainted parameter not allowed"); 00134 } 00135 } 00136 } 00137 00138 values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *)); 00139 generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic)); 00140 00141 for (i = 0; i < argc; i++) { 00142 VALUE type = RARRAY_PTR(types)[i]; 00143 VALUE src = argv[i]; 00144 00145 if(NUM2INT(type) == TYPE_VOIDP) { 00146 if(NIL_P(src)) { 00147 src = INT2NUM(0); 00148 } else if(cPointer != CLASS_OF(src)) { 00149 src = rb_funcall(cPointer, rb_intern("[]"), 1, src); 00150 } 00151 src = rb_Integer(src); 00152 } 00153 00154 VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]); 00155 values[i] = (void *)&generic_args[i]; 00156 } 00157 values[argc] = NULL; 00158 00159 ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values); 00160 00161 rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno)); 00162 #if defined(_WIN32) 00163 rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno)); 00164 #endif 00165 00166 xfree(values); 00167 xfree(generic_args); 00168 00169 return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval); 00170 } 00171 00172 void 00173 Init_fiddle_function(void) 00174 { 00175 /* 00176 * Document-class: Fiddle::Function 00177 * 00178 * == Description 00179 * 00180 * A representation of a C function 00181 * 00182 * == Examples 00183 * 00184 * === 'strcpy' 00185 * 00186 * @libc = Fiddle.dlopen "/lib/libc.so.6" 00187 * #=> #<Fiddle::Handle:0x00000001d7a8d8> 00188 * f = Fiddle::Function.new( 00189 * @libc['strcpy'], 00190 * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], 00191 * Fiddle::TYPE_VOIDP) 00192 * #=> #<Fiddle::Function:0x00000001d8ee00> 00193 * buff = "000" 00194 * #=> "000" 00195 * str = f.call(buff, "123") 00196 * #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000> 00197 * str.to_s 00198 * => "123" 00199 * 00200 * === ABI check 00201 * 00202 * @libc = DL.dlopen "/lib/libc.so.6" 00203 * #=> #<Fiddle::Handle:0x00000001d7a8d8> 00204 * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) 00205 * #=> #<Fiddle::Function:0x00000001d8ee00> 00206 * f.abi == Fiddle::Function::DEFAULT 00207 * #=> true 00208 */ 00209 cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject); 00210 00211 /* 00212 * Document-const: DEFAULT 00213 * 00214 * Default ABI 00215 * 00216 */ 00217 rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI)); 00218 00219 #ifdef HAVE_CONST_FFI_STDCALL 00220 /* 00221 * Document-const: STDCALL 00222 * 00223 * FFI implementation of WIN32 stdcall convention 00224 * 00225 */ 00226 rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL)); 00227 #endif 00228 00229 rb_define_alloc_func(cFiddleFunction, allocate); 00230 00231 /* 00232 * Document-method: call 00233 * 00234 * Calls the constructed Function, with +args+ 00235 * 00236 * For an example see Fiddle::Function 00237 * 00238 */ 00239 rb_define_method(cFiddleFunction, "call", function_call, -1); 00240 00241 /* 00242 * Document-method: new 00243 * call-seq: new(ptr, args, ret_type, abi = DEFAULT) 00244 * 00245 * Constructs a Function object. 00246 * * +ptr+ is a referenced function, of a Fiddle::Handle 00247 * * +args+ is an Array of arguments, passed to the +ptr+ function 00248 * * +ret_type+ is the return type of the function 00249 * * +abi+ is the ABI of the function 00250 * 00251 */ 00252 rb_define_method(cFiddleFunction, "initialize", initialize, -1); 00253 } 00254 /* vim: set noet sws=4 sw=4: */ 00255