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 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 = INT2FIX(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
00177
00178
00179
00180
00181
00182
00183
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 cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
00210
00211
00212
00213
00214
00215
00216
00217 rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
00218
00219 #ifdef HAVE_CONST_FFI_STDCALL
00220
00221
00222
00223
00224
00225
00226 rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
00227 #endif
00228
00229 rb_define_alloc_func(cFiddleFunction, allocate);
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239 rb_define_method(cFiddleFunction, "call", function_call, -1);
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252 rb_define_method(cFiddleFunction, "initialize", initialize, -1);
00253 }
00254
00255