diff options
Diffstat (limited to 'ext/fiddle/function.c')
| -rw-r--r-- | ext/fiddle/function.c | 375 |
1 files changed, 97 insertions, 278 deletions
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c index 5469e09e37..8e280567db 100644 --- a/ext/fiddle/function.c +++ b/ext/fiddle/function.c @@ -1,8 +1,6 @@ #include <fiddle.h> #include <ruby/thread.h> -#include <stdbool.h> - #ifdef PRIsVALUE # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj) # define RB_OBJ_STRING(obj) (obj) @@ -21,21 +19,21 @@ VALUE cFiddleFunction; #define Check_Max_Args_Long(name, len) \ Check_Max_Args_(name, len, "l") #define Check_Max_Args_(name, len, fmt) \ - do { \ - if ((size_t)(len) >= MAX_ARGS) { \ - rb_raise(rb_eTypeError, \ - "%s is so large " \ - "that it can cause integer overflow (%"fmt"d)", \ - (name), (len)); \ - } \ - } while (0) + if ((size_t)(len) < MAX_ARGS) { \ + /* OK */ \ + } \ + else { \ + rb_raise(rb_eTypeError, \ + name" is so large that it can cause integer overflow (%"fmt"d)", \ + (len)); \ + } static void deallocate(void *p) { - ffi_cif *cif = p; - if (cif->arg_types) xfree(cif->arg_types); - xfree(cif); + ffi_cif *ptr = p; + if (ptr->arg_types) xfree(ptr->arg_types); + xfree(ptr); } static size_t @@ -53,13 +51,8 @@ function_memsize(const void *p) } const rb_data_type_t function_data_type = { - .wrap_struct_name = "fiddle/function", - .function = { - .dmark = 0, - .dfree = deallocate, - .dsize = function_memsize - }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, + "fiddle/function", + {0, deallocate, function_memsize,}, }; static VALUE @@ -82,114 +75,72 @@ rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type) return rb_class_new_instance(3, argv, cFiddleFunction); } -static VALUE -normalize_argument_types(const char *name, - VALUE arg_types, - bool *is_variadic) +static int +parse_keyword_arg_i(VALUE key, VALUE value, VALUE self) { - VALUE normalized_arg_types; - int i; - int n_arg_types; - *is_variadic = false; - - Check_Type(arg_types, T_ARRAY); - n_arg_types = RARRAY_LENINT(arg_types); - Check_Max_Args(name, n_arg_types); - - normalized_arg_types = rb_ary_new_capa(n_arg_types); - for (i = 0; i < n_arg_types; i++) { - VALUE arg_type = RARRAY_AREF(arg_types, i); - int c_arg_type; - arg_type = rb_fiddle_type_ensure(arg_type); - c_arg_type = NUM2INT(arg_type); - if (c_arg_type == TYPE_VARIADIC) { - if (i != n_arg_types - 1) { - rb_raise(rb_eArgError, - "Fiddle::TYPE_VARIADIC must be the last argument type: " - "%"PRIsVALUE, - arg_types); - } - *is_variadic = true; - break; - } - else { - (void)INT2FFI_TYPE(c_arg_type); /* raise */ - } - rb_ary_push(normalized_arg_types, INT2FIX(c_arg_type)); + if (key == ID2SYM(rb_intern("name"))) { + rb_iv_set(self, "@name", value); + } else { + rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, + RB_OBJ_STRING(key)); } - - /* freeze to prevent inconsistency at calling #to_int later */ - OBJ_FREEZE(normalized_arg_types); - return normalized_arg_types; + return ST_CONTINUE; } static VALUE initialize(int argc, VALUE argv[], VALUE self) { ffi_cif * cif; - VALUE ptr, arg_types, ret_type, abi, kwargs; - VALUE name = Qnil; - VALUE need_gvl = Qfalse; - int c_ret_type; - bool is_variadic = false; - ffi_abi c_ffi_abi; + ffi_type **arg_types, *rtype; + ffi_status result; + VALUE ptr, args, ret_type, abi, kwds, ary; + int i, len; + int nabi; void *cfunc; - rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwargs); - rb_iv_set(self, "@closure", ptr); - - if (!NIL_P(kwargs)) { - enum { - kw_name, - kw_need_gvl, - kw_max_, - }; - static ID kw[kw_max_]; - VALUE args[kw_max_]; - if (!kw[0]) { - kw[kw_name] = rb_intern_const("name"); - kw[kw_need_gvl] = rb_intern_const("need_gvl"); - } - rb_get_kwargs(kwargs, kw, 0, kw_max_, args); - if (args[kw_name] != Qundef) { - name = args[kw_name]; - } - if (args[kw_need_gvl] != Qundef) { - need_gvl = args[kw_need_gvl]; - } - } - rb_iv_set(self, "@name", name); - rb_iv_set(self, "@need_gvl", need_gvl); - + rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds); ptr = rb_Integer(ptr); cfunc = NUM2PTR(ptr); PTR2NUM(cfunc); - c_ffi_abi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi); - abi = INT2FIX(c_ffi_abi); - ret_type = rb_fiddle_type_ensure(ret_type); - c_ret_type = NUM2INT(ret_type); - (void)INT2FFI_TYPE(c_ret_type); /* raise */ - ret_type = INT2FIX(c_ret_type); - - arg_types = normalize_argument_types("argument types", - arg_types, - &is_variadic); -#ifndef HAVE_FFI_PREP_CIF_VAR - if (is_variadic) { - rb_raise(rb_eNotImpError, - "ffi_prep_cif_var() is required in libffi " - "for variadic arguments"); + nabi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi); + abi = INT2FIX(nabi); + i = NUM2INT(ret_type); + rtype = INT2FFI_TYPE(i); + ret_type = INT2FIX(i); + + Check_Type(args, T_ARRAY); + len = RARRAY_LENINT(args); + Check_Max_Args("args", len); + ary = rb_ary_subseq(args, 0, len); + for (i = 0; i < RARRAY_LEN(args); i++) { + VALUE a = RARRAY_PTR(args)[i]; + int type = NUM2INT(a); + (void)INT2FFI_TYPE(type); /* raise */ + if (INT2FIX(type) != a) rb_ary_store(ary, i, INT2FIX(type)); } -#endif + OBJ_FREEZE(ary); rb_iv_set(self, "@ptr", ptr); - rb_iv_set(self, "@argument_types", arg_types); + rb_iv_set(self, "@args", args); rb_iv_set(self, "@return_type", ret_type); rb_iv_set(self, "@abi", abi); - rb_iv_set(self, "@is_variadic", is_variadic ? Qtrue : Qfalse); + + if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self); TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); - cif->arg_types = NULL; + + arg_types = xcalloc(len + 1, sizeof(ffi_type *)); + + for (i = 0; i < RARRAY_LEN(args); i++) { + int type = NUM2INT(RARRAY_AREF(args, i)); + arg_types[i] = INT2FFI_TYPE(type); + } + arg_types[len] = NULL; + + result = ffi_prep_cif(cif, nabi, len, rtype, arg_types); + + if (result) + rb_raise(rb_eRuntimeError, "error creating CIF %d", result); return self; } @@ -216,182 +167,61 @@ function_call(int argc, VALUE argv[], VALUE self) { struct nogvl_ffi_call_args args = { 0 }; fiddle_generic *generic_args; - VALUE cfunc; - VALUE abi; - VALUE arg_types; - VALUE cPointer; - VALUE is_variadic; - VALUE need_gvl; - int n_arg_types; - int n_fixed_args = 0; - int n_call_args = 0; + VALUE cfunc, types, cPointer; int i; - int i_call; - VALUE converted_args = Qnil; VALUE alloc_buffer = 0; cfunc = rb_iv_get(self, "@ptr"); - abi = rb_iv_get(self, "@abi"); - arg_types = rb_iv_get(self, "@argument_types"); + types = rb_iv_get(self, "@args"); cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); - is_variadic = rb_iv_get(self, "@is_variadic"); - need_gvl = rb_iv_get(self, "@need_gvl"); - - n_arg_types = RARRAY_LENINT(arg_types); - n_fixed_args = n_arg_types; - if (RTEST(is_variadic)) { - if (argc < n_arg_types) { - rb_error_arity(argc, n_arg_types, UNLIMITED_ARGUMENTS); - } - if (((argc - n_arg_types) % 2) != 0) { - rb_raise(rb_eArgError, - "variadic arguments must be type and value pairs: " - "%"PRIsVALUE, - rb_ary_new_from_values(argc, argv)); - } - n_call_args = n_arg_types + ((argc - n_arg_types) / 2); - } - else { - if (argc != n_arg_types) { - rb_error_arity(argc, n_arg_types, n_arg_types); - } - n_call_args = n_arg_types; + + Check_Max_Args("number of arguments", argc); + if (argc != (i = RARRAY_LENINT(types))) { + rb_error_arity(argc, i, i); } - Check_Max_Args("the number of arguments", n_call_args); TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif); - if (is_variadic && args.cif->arg_types) { - xfree(args.cif->arg_types); - args.cif->arg_types = NULL; - } - - if (!args.cif->arg_types) { - VALUE fixed_arg_types = arg_types; - VALUE return_type; - int c_return_type; - ffi_type *ffi_return_type; - ffi_type **ffi_arg_types; - ffi_status result; - - arg_types = rb_ary_dup(fixed_arg_types); - for (i = n_fixed_args; i < argc; i += 2) { - VALUE arg_type = argv[i]; - int c_arg_type; - arg_type = rb_fiddle_type_ensure(arg_type); - c_arg_type = NUM2INT(arg_type); - (void)INT2FFI_TYPE(c_arg_type); /* raise */ - rb_ary_push(arg_types, INT2FIX(c_arg_type)); - } - - return_type = rb_iv_get(self, "@return_type"); - c_return_type = FIX2INT(return_type); - ffi_return_type = INT2FFI_TYPE(c_return_type); - - ffi_arg_types = xcalloc(n_call_args + 1, sizeof(ffi_type *)); - for (i_call = 0; i_call < n_call_args; i_call++) { - VALUE arg_type; - int c_arg_type; - arg_type = RARRAY_AREF(arg_types, i_call); - c_arg_type = FIX2INT(arg_type); - ffi_arg_types[i_call] = INT2FFI_TYPE(c_arg_type); - } - ffi_arg_types[i_call] = NULL; - - if (is_variadic) { -#ifdef HAVE_FFI_PREP_CIF_VAR - result = ffi_prep_cif_var(args.cif, - FIX2INT(abi), - n_fixed_args, - n_call_args, - ffi_return_type, - ffi_arg_types); -#else - /* This code is never used because ffi_prep_cif_var() - * availability check is done in #initialize. */ - result = FFI_BAD_TYPEDEF; -#endif - } - else { - result = ffi_prep_cif(args.cif, - FIX2INT(abi), - n_call_args, - ffi_return_type, - ffi_arg_types); - } - if (result != FFI_OK) { - xfree(ffi_arg_types); - args.cif->arg_types = NULL; - rb_raise(rb_eRuntimeError, "error creating CIF %d", result); - } + if (rb_safe_level() >= 1) { + for (i = 0; i < argc; i++) { + VALUE src = argv[i]; + if (OBJ_TAINTED(src)) { + rb_raise(rb_eSecurityError, "tainted parameter not allowed"); + } + } } generic_args = ALLOCV(alloc_buffer, - sizeof(fiddle_generic) * n_call_args + - sizeof(void *) * (n_call_args + 1)); + (size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic)); args.values = (void **)((char *)generic_args + - sizeof(fiddle_generic) * n_call_args); - - for (i = 0, i_call = 0; - i < argc && i_call < n_call_args; - i++, i_call++) { - VALUE arg_type; - int c_arg_type; - VALUE original_src; - VALUE src; - arg_type = RARRAY_AREF(arg_types, i_call); - c_arg_type = FIX2INT(arg_type); - if (i >= n_fixed_args) { - i++; - } - src = argv[i]; - - if (c_arg_type == TYPE_VOIDP) { - if (NIL_P(src)) { - src = INT2FIX(0); - } - else if (cPointer != CLASS_OF(src)) { - src = rb_funcall(cPointer, rb_intern("[]"), 1, src); - if (NIL_P(converted_args)) { - converted_args = rb_ary_new(); - } - rb_ary_push(converted_args, src); - } - src = rb_Integer(src); - } - - original_src = src; - VALUE2GENERIC(c_arg_type, src, &generic_args[i_call]); - if (src != original_src) { - if (NIL_P(converted_args)) { - converted_args = rb_ary_new(); - } - rb_ary_push(converted_args, src); - } - args.values[i_call] = (void *)&generic_args[i_call]; + (size_t)argc * sizeof(fiddle_generic)); + + for (i = 0; i < argc; i++) { + VALUE type = RARRAY_AREF(types, i); + VALUE src = argv[i]; + int argtype = FIX2INT(type); + + if (argtype == TYPE_VOIDP) { + if(NIL_P(src)) { + src = INT2FIX(0); + } else if(cPointer != CLASS_OF(src)) { + src = rb_funcall(cPointer, rb_intern("[]"), 1, src); + } + src = rb_Integer(src); + } + + VALUE2GENERIC(argtype, src, &generic_args[i]); + args.values[i] = (void *)&generic_args[i]; } - args.values[i_call] = NULL; - args.fn = (void(*)(void))NUM2PTR(cfunc); + args.values[argc] = NULL; + args.fn = NUM2PTR(cfunc); - if (RTEST(need_gvl)) { - ffi_call(args.cif, args.fn, &(args.retval), args.values); - } - else { - (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0); - } + (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0); - { - int errno_keep = errno; + rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno)); #if defined(_WIN32) - DWORD error = WSAGetLastError(); - int socket_error = WSAGetLastError(); - rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, - ULONG2NUM(error)); - rb_funcall(mFiddle, rb_intern("win32_last_socket_error="), 1, - INT2NUM(socket_error)); + rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno)); #endif - rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno_keep)); - } ALLOCV_END(alloc_buffer); @@ -464,10 +294,6 @@ Init_fiddle_function(void) * Caller must ensure the underlying function is called in a * thread-safe manner if running in a multi-threaded process. * - * Note that it is not thread-safe to use this method to - * directly or indirectly call many Ruby C-extension APIs unless - * you don't pass +need_gvl: true+ to Fiddle::Function#new. - * * For an example see Fiddle::Function * */ @@ -475,20 +301,13 @@ Init_fiddle_function(void) /* * Document-method: new - * call-seq: new(ptr, - * args, - * ret_type, - * abi = DEFAULT, - * name: nil, - * need_gvl: false) + * call-seq: new(ptr, args, ret_type, abi = DEFAULT) * * Constructs a Function object. * * +ptr+ is a referenced function, of a Fiddle::Handle * * +args+ is an Array of arguments, passed to the +ptr+ function * * +ret_type+ is the return type of the function * * +abi+ is the ABI of the function - * * +name+ is the name of the function - * * +need_gvl+ is whether GVL is needed to call the function * */ rb_define_method(cFiddleFunction, "initialize", initialize, -1); |
