/* -*- C -*- * $Id$ */ #include #include #include "dl.h" #include VALUE rb_cDLFunction; typedef union { unsigned char uchar; /* ffi_type_uchar */ signed char schar; /* ffi_type_schar */ unsigned short ushort; /* ffi_type_sshort */ signed short sshort; /* ffi_type_ushort */ unsigned int uint; /* ffi_type_uint */ signed int sint; /* ffi_type_sint */ unsigned long ulong; /* ffi_type_ulong */ signed long slong; /* ffi_type_slong */ float ffloat; /* ffi_type_float */ double ddouble; /* ffi_type_double */ #if HAVE_LONG_LONG unsigned LONG_LONG long_long; /* ffi_type_uint64 */ #endif void * pointer; /* ffi_type_pointer */ } dl_generic; static void dlfunction_free(void *p) { ffi_cif *ptr = p; if (ptr->arg_types) xfree(ptr->arg_types); xfree(ptr); } static size_t dlfunction_memsize(const void *p) { /* const */ffi_cif *ptr = (ffi_cif *)p; size_t size = 0; if (ptr) { size += sizeof(*ptr); size += ffi_raw_size(ptr); } return size; } const rb_data_type_t dlfunction_data_type = { "dl/function", 0, dlfunction_free, dlfunction_memsize, }; static VALUE rb_dlfunc_allocate(VALUE klass) { ffi_cif * cif; return TypedData_Make_Struct(klass, ffi_cif, &dlfunction_data_type, cif); } static VALUE rb_dlfunction_native_init(VALUE self, VALUE args, VALUE ret_type, VALUE abi) { ffi_cif * cif; ffi_type **arg_types; ffi_status result; int i; TypedData_Get_Struct(self, ffi_cif, &dlfunction_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] = DL2FFI_TYPE(type); } arg_types[RARRAY_LEN(args)] = NULL; result = ffi_prep_cif ( cif, NUM2INT(abi), RARRAY_LEN(args), DL2FFI_TYPE(NUM2INT(ret_type)), arg_types); if (result) rb_raise(rb_eRuntimeError, "error creating CIF %d", result); return self; } static void dl2generic(int dl_type, VALUE src, dl_generic * dst) { int signed_p = 1; if (dl_type < 0) { dl_type = -1 * dl_type; signed_p = 0; } switch (dl_type) { case DLTYPE_VOID: break; case DLTYPE_VOIDP: dst->pointer = NUM2PTR(rb_Integer(src)); break; case DLTYPE_CHAR: case DLTYPE_SHORT: case DLTYPE_INT: dst->sint = NUM2INT(src); break; case DLTYPE_LONG: if (signed_p) dst->slong = NUM2LONG(src); else dst->ulong = NUM2LONG(src); break; #if HAVE_LONG_LONG case DLTYPE_LONG_LONG: dst->long_long = rb_big2ull(src); break; #endif case DLTYPE_FLOAT: dst->ffloat = (float)NUM2DBL(src); break; case DLTYPE_DOUBLE: dst->ddouble = NUM2DBL(src); break; default: rb_raise(rb_eRuntimeError, "unknown type %d", dl_type); } } static VALUE unwrap_ffi(VALUE rettype, dl_generic retval) { int signed_p = 1; int dl_type = NUM2INT(rettype); if (dl_type < 0) { dl_type = -1 * dl_type; signed_p = 0; } switch (dl_type) { case DLTYPE_VOID: return Qnil; case DLTYPE_VOIDP: return rb_dlptr_new((void *)retval.pointer, 0, NULL); case DLTYPE_CHAR: case DLTYPE_SHORT: case DLTYPE_INT: return INT2NUM(retval.sint); case DLTYPE_LONG: if (signed_p) return LONG2NUM(retval.slong); return ULONG2NUM(retval.ulong); #if HAVE_LONG_LONG case DLTYPE_LONG_LONG: return rb_ll2inum(retval.long_long); break; #endif case DLTYPE_FLOAT: return rb_float_new(retval.ffloat); case DLTYPE_DOUBLE: return rb_float_new(retval.ddouble); default: rb_raise(rb_eRuntimeError, "unknown type %d", dl_type); } } static VALUE rb_dlfunction_call(int argc, VALUE argv[], VALUE self) { ffi_cif * cif; dl_generic retval; dl_generic *generic_args; void **values; void * fun_ptr; VALUE cfunc, types; int i; TypedData_Get_Struct(self, ffi_cif, &dlfunction_data_type, cif); values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *)); generic_args = xcalloc((size_t)argc, (size_t)sizeof(dl_generic)); cfunc = rb_iv_get(self, "@cfunc"); types = rb_iv_get(self, "@args"); for (i = 0; i < argc; i++) { VALUE dl_type = RARRAY_PTR(types)[i]; VALUE src = rb_funcall(self, rb_intern("ruby2ffi"), 2, argv[i], dl_type ); dl2generic(NUM2INT(dl_type), src, &generic_args[i]); values[i] = (void *)&generic_args[i]; } values[argc] = NULL; ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values); rb_dl_set_last_error(self, INT2NUM(errno)); #if defined(HAVE_WINDOWS_H) rb_dl_set_win32_last_error(self, INT2NUM(GetLastError())); #endif xfree(values); xfree(generic_args); return unwrap_ffi(rb_funcall(cfunc, rb_intern("ctype"), 0), retval); } void Init_dlfunction(void) { rb_cDLFunction = rb_define_class_under(rb_mDL, "Function", rb_cObject); rb_define_const(rb_cDLFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI)); #ifdef FFI_STDCALL rb_define_const(rb_cDLFunction, "STDCALL", INT2NUM(FFI_STDCALL)); #endif rb_define_alloc_func(rb_cDLFunction, rb_dlfunc_allocate); rb_define_private_method(rb_cDLFunction, "native_call", rb_dlfunction_call, -1); rb_define_private_method(rb_cDLFunction, "native_init", rb_dlfunction_native_init, 3); } /* vim: set noet sw=4 sts=4 */