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