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 static VALUE
00042 initialize(int argc, VALUE argv[], VALUE self)
00043 {
00044 ffi_cif * cif;
00045 ffi_type **arg_types;
00046 ffi_status result;
00047 VALUE ptr, args, ret_type, abi;
00048 int i;
00049
00050 rb_scan_args(argc, argv, "31", &ptr, &args, &ret_type, &abi);
00051 if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
00052
00053 Check_Type(args, T_ARRAY);
00054
00055 rb_iv_set(self, "@ptr", ptr);
00056 rb_iv_set(self, "@args", args);
00057 rb_iv_set(self, "@return_type", ret_type);
00058 rb_iv_set(self, "@abi", abi);
00059
00060 TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
00061
00062 arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
00063
00064 for (i = 0; i < RARRAY_LEN(args); i++) {
00065 int type = NUM2INT(RARRAY_PTR(args)[i]);
00066 arg_types[i] = INT2FFI_TYPE(type);
00067 }
00068 arg_types[RARRAY_LEN(args)] = NULL;
00069
00070 result = ffi_prep_cif (
00071 cif,
00072 NUM2INT(abi),
00073 RARRAY_LENINT(args),
00074 INT2FFI_TYPE(NUM2INT(ret_type)),
00075 arg_types);
00076
00077 if (result)
00078 rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
00079
00080 return self;
00081 }
00082
00083 static VALUE
00084 function_call(int argc, VALUE argv[], VALUE self)
00085 {
00086 ffi_cif * cif;
00087 fiddle_generic retval;
00088 fiddle_generic *generic_args;
00089 void **values;
00090 VALUE cfunc, types, cPointer;
00091 int i;
00092
00093 cfunc = rb_iv_get(self, "@ptr");
00094 types = rb_iv_get(self, "@args");
00095 cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
00096
00097 if(argc != RARRAY_LENINT(types)) {
00098 rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
00099 argc, RARRAY_LENINT(types));
00100 }
00101
00102 TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
00103
00104 values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
00105 generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
00106
00107 for (i = 0; i < argc; i++) {
00108 VALUE type = RARRAY_PTR(types)[i];
00109 VALUE src = argv[i];
00110
00111 if(NUM2INT(type) == TYPE_VOIDP) {
00112 if(NIL_P(src)) {
00113 src = INT2NUM(0);
00114 } else if(cPointer != CLASS_OF(src)) {
00115 src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
00116 }
00117 src = rb_Integer(src);
00118 }
00119
00120 VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
00121 values[i] = (void *)&generic_args[i];
00122 }
00123 values[argc] = NULL;
00124
00125 ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
00126
00127 rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
00128 #if defined(_WIN32)
00129 rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
00130 #endif
00131
00132 xfree(values);
00133 xfree(generic_args);
00134
00135 return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
00136 }
00137
00138 void
00139 Init_fiddle_function(void)
00140 {
00141 cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
00142
00143 rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
00144
00145 #ifdef FFI_STDCALL
00146 rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
00147 #endif
00148
00149 rb_define_alloc_func(cFiddleFunction, allocate);
00150
00151 rb_define_method(cFiddleFunction, "call", function_call, -1);
00152 rb_define_method(cFiddleFunction, "initialize", initialize, -1);
00153 }
00154
00155