ruby/ext/fiddle/function.c

155 строки
3.6 KiB
C

#include <fiddle.h>
VALUE cFiddleFunction;
static void
deallocate(void *p)
{
ffi_cif *ptr = p;
if (ptr->arg_types) xfree(ptr->arg_types);
xfree(ptr);
}
static size_t
function_memsize(const void *p)
{
/* const */ffi_cif *ptr = (ffi_cif *)p;
size_t size = 0;
if (ptr) {
size += sizeof(*ptr);
#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
size += ffi_raw_size(ptr);
#endif
}
return size;
}
const rb_data_type_t function_data_type = {
"fiddle/function",
{0, deallocate, function_memsize,},
};
static VALUE
allocate(VALUE klass)
{
ffi_cif * cif;
return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
}
static VALUE
initialize(int argc, VALUE argv[], VALUE self)
{
ffi_cif * cif;
ffi_type **arg_types;
ffi_status result;
VALUE ptr, args, ret_type, abi;
int i;
rb_scan_args(argc, argv, "31", &ptr, &args, &ret_type, &abi);
if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
Check_Type(args, T_ARRAY);
rb_iv_set(self, "@ptr", ptr);
rb_iv_set(self, "@args", args);
rb_iv_set(self, "@return_type", ret_type);
rb_iv_set(self, "@abi", abi);
TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
for (i = 0; i < RARRAY_LEN(args); i++) {
int type = NUM2INT(RARRAY_PTR(args)[i]);
arg_types[i] = INT2FFI_TYPE(type);
}
arg_types[RARRAY_LEN(args)] = NULL;
result = ffi_prep_cif (
cif,
NUM2INT(abi),
RARRAY_LENINT(args),
INT2FFI_TYPE(NUM2INT(ret_type)),
arg_types);
if (result)
rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
return self;
}
static VALUE
function_call(int argc, VALUE argv[], VALUE self)
{
ffi_cif * cif;
fiddle_generic retval;
fiddle_generic *generic_args;
void **values;
VALUE cfunc, types, cPointer;
int i;
cfunc = rb_iv_get(self, "@ptr");
types = rb_iv_get(self, "@args");
cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
if(argc != RARRAY_LENINT(types)) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
argc, RARRAY_LENINT(types));
}
TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
for (i = 0; i < argc; i++) {
VALUE type = RARRAY_PTR(types)[i];
VALUE src = argv[i];
if(NUM2INT(type) == TYPE_VOIDP) {
if(NIL_P(src)) {
src = INT2NUM(0);
} else if(cPointer != CLASS_OF(src)) {
src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
}
src = rb_Integer(src);
}
VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
values[i] = (void *)&generic_args[i];
}
values[argc] = NULL;
ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
#if defined(_WIN32)
rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
#endif
xfree(values);
xfree(generic_args);
return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
}
void
Init_fiddle_function(void)
{
cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
#ifdef FFI_STDCALL
rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
#endif
rb_define_alloc_func(cFiddleFunction, allocate);
rb_define_method(cFiddleFunction, "call", function_call, -1);
rb_define_method(cFiddleFunction, "initialize", initialize, -1);
}
/* vim: set noet sws=4 sw=4: */