mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ruby/fiddle] Add support for variadic arguments
GitHub: fix GH-39 Reported by kojix2. Thanks!!! https://github.com/ruby/fiddle/commit/6c4cb904dc
This commit is contained in:
parent
9f740acaf9
commit
ae18220f99
Notes:
git
2020-06-28 02:03:19 +09:00
6 changed files with 229 additions and 64 deletions
|
@ -127,6 +127,8 @@ else
|
|||
have_func('ffi_closure_alloc', ffi_header)
|
||||
end
|
||||
|
||||
have_func('ffi_prep_cif_var', ffi_header)
|
||||
|
||||
have_header 'sys/mman.h'
|
||||
|
||||
if have_header "dlfcn.h"
|
||||
|
|
|
@ -227,6 +227,12 @@ Init_fiddle(void)
|
|||
*/
|
||||
rb_define_const(mFiddle, "TYPE_DOUBLE", INT2NUM(TYPE_DOUBLE));
|
||||
|
||||
/* Document-const: TYPE_VARIADIC
|
||||
*
|
||||
* C type - ...
|
||||
*/
|
||||
rb_define_const(mFiddle, "TYPE_VARIADIC", INT2NUM(TYPE_VARIADIC));
|
||||
|
||||
/* Document-const: TYPE_SIZE_T
|
||||
*
|
||||
* C type - size_t
|
||||
|
|
|
@ -115,6 +115,7 @@
|
|||
#endif
|
||||
#define TYPE_FLOAT 7
|
||||
#define TYPE_DOUBLE 8
|
||||
#define TYPE_VARIADIC 9
|
||||
|
||||
#define ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x)
|
||||
|
||||
|
|
|
@ -87,63 +87,88 @@ parse_keyword_arg_i(VALUE key, VALUE value, VALUE self)
|
|||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
normalize_argument_types(const char *name,
|
||||
VALUE arg_types,
|
||||
bool *is_variadic)
|
||||
{
|
||||
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 = 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));
|
||||
}
|
||||
|
||||
/* freeze to prevent inconsistency at calling #to_int later */
|
||||
OBJ_FREEZE(normalized_arg_types);
|
||||
return normalized_arg_types;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
initialize(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
ffi_cif * cif;
|
||||
ffi_type **arg_types, *rtype;
|
||||
ffi_status result;
|
||||
VALUE ptr, args, ret_type, abi, kwds;
|
||||
int i, len;
|
||||
int nabi;
|
||||
VALUE ptr, arg_types, ret_type, abi, kwds;
|
||||
int c_ret_type;
|
||||
bool is_variadic = false;
|
||||
ffi_abi c_ffi_abi;
|
||||
void *cfunc;
|
||||
|
||||
rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds);
|
||||
rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwds);
|
||||
rb_iv_set(self, "@closure", ptr);
|
||||
|
||||
ptr = rb_Integer(ptr);
|
||||
cfunc = NUM2PTR(ptr);
|
||||
PTR2NUM(cfunc);
|
||||
nabi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi);
|
||||
abi = INT2FIX(nabi);
|
||||
i = NUM2INT(ret_type);
|
||||
rtype = INT2FFI_TYPE(i);
|
||||
ret_type = INT2FIX(i);
|
||||
c_ffi_abi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi);
|
||||
abi = INT2FIX(c_ffi_abi);
|
||||
c_ret_type = NUM2INT(ret_type);
|
||||
(void)INT2FFI_TYPE(c_ret_type); /* raise */
|
||||
ret_type = INT2FIX(c_ret_type);
|
||||
|
||||
Check_Type(args, T_ARRAY);
|
||||
len = RARRAY_LENINT(args);
|
||||
Check_Max_Args("args", len);
|
||||
/* freeze to prevent inconsistency at calling #to_int later */
|
||||
args = rb_ary_subseq(args, 0, len);
|
||||
for (i = 0; i < RARRAY_LEN(args); i++) {
|
||||
VALUE a = RARRAY_AREF(args, i);
|
||||
int type = NUM2INT(a);
|
||||
(void)INT2FFI_TYPE(type); /* raise */
|
||||
if (INT2FIX(type) != a) rb_ary_store(args, i, INT2FIX(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");
|
||||
}
|
||||
OBJ_FREEZE(args);
|
||||
#endif
|
||||
|
||||
rb_iv_set(self, "@ptr", ptr);
|
||||
rb_iv_set(self, "@args", args);
|
||||
rb_iv_set(self, "@argument_types", arg_types);
|
||||
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);
|
||||
|
||||
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);
|
||||
cif->arg_types = NULL;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -170,44 +195,144 @@ function_call(int argc, VALUE argv[], VALUE self)
|
|||
{
|
||||
struct nogvl_ffi_call_args args = { 0 };
|
||||
fiddle_generic *generic_args;
|
||||
VALUE cfunc, types, cPointer;
|
||||
VALUE cfunc;
|
||||
VALUE abi;
|
||||
VALUE arg_types;
|
||||
VALUE cPointer;
|
||||
VALUE is_variadic;
|
||||
int n_arg_types;
|
||||
int n_fixed_args = 0;
|
||||
int n_call_args = 0;
|
||||
int i;
|
||||
int i_call;
|
||||
VALUE alloc_buffer = 0;
|
||||
|
||||
cfunc = rb_iv_get(self, "@ptr");
|
||||
types = rb_iv_get(self, "@args");
|
||||
abi = rb_iv_get(self, "@abi");
|
||||
arg_types = rb_iv_get(self, "@argument_types");
|
||||
cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
|
||||
is_variadic = rb_iv_get(self, "@is_variadic");
|
||||
|
||||
Check_Max_Args("number of arguments", argc);
|
||||
if (argc != (i = RARRAY_LENINT(types))) {
|
||||
rb_error_arity(argc, i, i);
|
||||
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("the number of arguments", n_call_args);
|
||||
|
||||
TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif);
|
||||
|
||||
generic_args = ALLOCV(alloc_buffer,
|
||||
(size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic));
|
||||
args.values = (void **)((char *)generic_args +
|
||||
(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];
|
||||
if (is_variadic && args.cif->arg_types) {
|
||||
xfree(args.cif->arg_types);
|
||||
args.cif->arg_types = NULL;
|
||||
}
|
||||
args.values[argc] = 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 = 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);
|
||||
}
|
||||
}
|
||||
|
||||
generic_args = ALLOCV(alloc_buffer,
|
||||
sizeof(fiddle_generic) * n_call_args +
|
||||
sizeof(void *) * (n_call_args + 1));
|
||||
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 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);
|
||||
}
|
||||
src = rb_Integer(src);
|
||||
}
|
||||
|
||||
VALUE2GENERIC(c_arg_type, src, &generic_args[i_call]);
|
||||
args.values[i_call] = (void *)&generic_args[i_call];
|
||||
}
|
||||
args.values[i_call] = NULL;
|
||||
args.fn = (void(*)(void))NUM2PTR(cfunc);
|
||||
|
||||
(void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
|
||||
|
|
|
@ -79,5 +79,36 @@ module Fiddle
|
|||
EnvUtil.under_gc_stress {qsort.call(buff, buff.size, 1, cb)}
|
||||
assert_equal("1349", buff, bug4929)
|
||||
end
|
||||
|
||||
def test_snprintf
|
||||
snprintf = Function.new(@libc["snprintf"],
|
||||
[
|
||||
TYPE_VOIDP,
|
||||
TYPE_SIZE_T,
|
||||
TYPE_VOIDP,
|
||||
TYPE_VARIADIC,
|
||||
],
|
||||
TYPE_INT)
|
||||
output_buffer = " " * 1024
|
||||
output = Pointer[output_buffer]
|
||||
|
||||
written = snprintf.call(output,
|
||||
output.size,
|
||||
"int: %d, string: %.*s\n",
|
||||
TYPE_INT, -29,
|
||||
TYPE_INT, 4,
|
||||
TYPE_VOIDP, "Hello")
|
||||
assert_equal("int: -29, string: Hell\n",
|
||||
output_buffer[0, written])
|
||||
|
||||
written = snprintf.call(output,
|
||||
output.size,
|
||||
"string: %.*s, uint: %u\n",
|
||||
TYPE_INT, 2,
|
||||
TYPE_VOIDP, "Hello",
|
||||
TYPE_INT, 29)
|
||||
assert_equal("string: He, uint: 29\n",
|
||||
output_buffer[0, written])
|
||||
end
|
||||
end
|
||||
end if defined?(Fiddle)
|
||||
|
|
|
@ -110,7 +110,7 @@ module Fiddle
|
|||
assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG)
|
||||
end
|
||||
|
||||
Fiddle.constants.grep(/\ATYPE_(?!VOID\z)(.*)/) do
|
||||
Fiddle.constants.grep(/\ATYPE_(?!VOID|VARIADIC\z)(.*)/) do
|
||||
type = $&
|
||||
size = Fiddle.const_get("SIZEOF_#{$1}")
|
||||
name = $1.sub(/P\z/,"*").gsub(/_(?!T\z)/, " ").downcase
|
||||
|
|
Loading…
Add table
Reference in a new issue