diff options
author | tenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2010-05-06 06:59:24 +0000 |
---|---|---|
committer | tenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2010-05-06 06:59:24 +0000 |
commit | 4bada8b864e445b6eebe1a341e30cad94dbcaf84 (patch) | |
tree | bb7e4ed991aa16eda953d536265c6f9e5dc03a5d /ext/fiddle/function.c | |
parent | ca3c007f058f17b2f0bf3c5ccc6701f3f6d49ca5 (diff) |
* ext/fiddle/*: Adding fiddle library to wrap libffi
* test/fiddle/*: testing fiddle extension
* ext/dl/lib/dl.rb: Requiring fiddle if it is available
* ext/dl/lib/dl/callback.rb: using Fiddle if it is available
* ext/dl/lib/dl/func.rb: ditto
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27640 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/fiddle/function.c')
-rw-r--r-- | ext/fiddle/function.c | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c new file mode 100644 index 0000000000..c547d82554 --- /dev/null +++ b/ext/fiddle/function.c @@ -0,0 +1,155 @@ +#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; + void * fun_ptr; + 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(HAVE_WINDOWS_H) + 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: */ |