[前][次][番号順一覧][スレッド一覧]

ruby-changes:61963

From: Sutou <ko1@a...>
Date: Sun, 28 Jun 2020 02:03:09 +0900 (JST)
Subject: [ruby-changes:61963] ae18220f99 (master): [ruby/fiddle] Add support for variadic arguments

https://git.ruby-lang.org/ruby.git/commit/?id=ae18220f99

From ae18220f9904c70304bd1672eecadbb3618fc7e5 Mon Sep 17 00:00:00 2001
From: Sutou Kouhei <kou@c...>
Date: Sat, 27 Jun 2020 07:25:47 +0900
Subject: [ruby/fiddle] Add support for variadic arguments

GitHub: fix GH-39

Reported by kojix2. Thanks!!!

https://github.com/ruby/fiddle/commit/6c4cb904dc

diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index f8a94d4..1ec9c6b 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -127,6 +127,8 @@ else https://github.com/ruby/ruby/blob/trunk/ext/fiddle/extconf.rb#L127
   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"
diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
index bb6b107..9635631 100644
--- a/ext/fiddle/fiddle.c
+++ b/ext/fiddle/fiddle.c
@@ -227,6 +227,12 @@ Init_fiddle(void) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/fiddle.c#L227
      */
     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
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
index d2583c1..2dddc0b 100644
--- a/ext/fiddle/fiddle.h
+++ b/ext/fiddle/fiddle.h
@@ -115,6 +115,7 @@ https://github.com/ruby/ruby/blob/trunk/ext/fiddle/fiddle.h#L115
 #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)
 
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c
index bd6c9bd..ddb49de 100644
--- a/ext/fiddle/function.c
+++ b/ext/fiddle/function.c
@@ -88,62 +88,87 @@ parse_keyword_arg_i(VALUE key, VALUE value, VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/function.c#L88
 }
 
 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);
-
-    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));
+    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);
+
+    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) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/function.c#L195
 {
     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"));
-
-    Check_Max_Args("number of arguments", argc);
-    if (argc != (i = RARRAY_LENINT(types))) {
-	rb_error_arity(argc, i, i);
+    is_variadic = rb_iv_get(self, "@is_variadic");
+
+    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);
 
+    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 = 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,
-	(size_t)(argc + 1) * sizeof(void *) + (size_t)argc * (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]