@@ -87,63 +87,88 @@ parse_keyword_arg_i(VALUE key, VALUE value, VALUE self)
8787 return ST_CONTINUE ;
8888}
8989
90+ static VALUE
91+ normalize_argument_types (const char * name ,
92+ VALUE arg_types ,
93+ bool * is_variadic )
94+ {
95+ VALUE normalized_arg_types ;
96+ int i ;
97+ int n_arg_types ;
98+ * is_variadic = false;
99+
100+ Check_Type (arg_types , T_ARRAY );
101+ n_arg_types = RARRAY_LENINT (arg_types );
102+ Check_Max_Args (name , n_arg_types );
103+
104+ normalized_arg_types = rb_ary_new_capa (n_arg_types );
105+ for (i = 0 ; i < n_arg_types ; i ++ ) {
106+ VALUE arg_type = RARRAY_AREF (arg_types , i );
107+ int c_arg_type = NUM2INT (arg_type );
108+ if (c_arg_type == TYPE_VARIADIC ) {
109+ if (i != n_arg_types - 1 ) {
110+ rb_raise (rb_eArgError ,
111+ "Fiddle::TYPE_VARIADIC must be the last argument type: "
112+ "%" PRIsVALUE ,
113+ arg_types );
114+ }
115+ * is_variadic = true;
116+ break ;
117+ }
118+ else {
119+ (void )INT2FFI_TYPE (c_arg_type ); /* raise */
120+ }
121+ rb_ary_push (normalized_arg_types , INT2FIX (c_arg_type ));
122+ }
123+
124+ /* freeze to prevent inconsistency at calling #to_int later */
125+ OBJ_FREEZE (normalized_arg_types );
126+ return normalized_arg_types ;
127+ }
128+
90129static VALUE
91130initialize (int argc , VALUE argv [], VALUE self )
92131{
93132 ffi_cif * cif ;
94- ffi_type * * arg_types , * rtype ;
95- ffi_status result ;
96- VALUE ptr , args , ret_type , abi , kwds ;
97- int i , len ;
98- int nabi ;
133+ VALUE ptr , arg_types , ret_type , abi , kwds ;
134+ int c_ret_type ;
135+ bool is_variadic = false;
136+ ffi_abi c_ffi_abi ;
99137 void * cfunc ;
100138
101- rb_scan_args (argc , argv , "31:" , & ptr , & args , & ret_type , & abi , & kwds );
139+ rb_scan_args (argc , argv , "31:" , & ptr , & arg_types , & ret_type , & abi , & kwds );
102140 rb_iv_set (self , "@closure" , ptr );
103141
104142 ptr = rb_Integer (ptr );
105143 cfunc = NUM2PTR (ptr );
106144 PTR2NUM (cfunc );
107- nabi = NIL_P (abi ) ? FFI_DEFAULT_ABI : NUM2INT (abi );
108- abi = INT2FIX (nabi );
109- i = NUM2INT (ret_type );
110- rtype = INT2FFI_TYPE (i );
111- ret_type = INT2FIX (i );
112-
113- Check_Type (args , T_ARRAY );
114- len = RARRAY_LENINT (args );
115- Check_Max_Args ("args" , len );
116- /* freeze to prevent inconsistency at calling #to_int later */
117- args = rb_ary_subseq (args , 0 , len );
118- for (i = 0 ; i < RARRAY_LEN (args ); i ++ ) {
119- VALUE a = RARRAY_AREF (args , i );
120- int type = NUM2INT (a );
121- (void )INT2FFI_TYPE (type ); /* raise */
122- if (INT2FIX (type ) != a ) rb_ary_store (args , i , INT2FIX (type ));
145+ c_ffi_abi = NIL_P (abi ) ? FFI_DEFAULT_ABI : NUM2INT (abi );
146+ abi = INT2FIX (c_ffi_abi );
147+ c_ret_type = NUM2INT (ret_type );
148+ (void )INT2FFI_TYPE (c_ret_type ); /* raise */
149+ ret_type = INT2FIX (c_ret_type );
150+
151+ arg_types = normalize_argument_types ("argument types" ,
152+ arg_types ,
153+ & is_variadic );
154+ #ifndef HAVE_FFI_PREP_CIF_VAR
155+ if (is_variadic ) {
156+ rb_raise (rb_eNotImpError ,
157+ "ffi_prep_cif_var() is required in libffi "
158+ "for variadic arguments" );
123159 }
124- OBJ_FREEZE ( args );
160+ #endif
125161
126162 rb_iv_set (self , "@ptr" , ptr );
127- rb_iv_set (self , "@args " , args );
163+ rb_iv_set (self , "@argument_types " , arg_types );
128164 rb_iv_set (self , "@return_type" , ret_type );
129165 rb_iv_set (self , "@abi" , abi );
166+ rb_iv_set (self , "@is_variadic" , is_variadic ? Qtrue : Qfalse );
130167
131168 if (!NIL_P (kwds )) rb_hash_foreach (kwds , parse_keyword_arg_i , self );
132169
133170 TypedData_Get_Struct (self , ffi_cif , & function_data_type , cif );
134-
135- arg_types = xcalloc (len + 1 , sizeof (ffi_type * ));
136-
137- for (i = 0 ; i < RARRAY_LEN (args ); i ++ ) {
138- int type = NUM2INT (RARRAY_AREF (args , i ));
139- arg_types [i ] = INT2FFI_TYPE (type );
140- }
141- arg_types [len ] = NULL ;
142-
143- result = ffi_prep_cif (cif , nabi , len , rtype , arg_types );
144-
145- if (result )
146- rb_raise (rb_eRuntimeError , "error creating CIF %d" , result );
171+ cif -> arg_types = NULL ;
147172
148173 return self ;
149174}
@@ -170,44 +195,144 @@ function_call(int argc, VALUE argv[], VALUE self)
170195{
171196 struct nogvl_ffi_call_args args = { 0 };
172197 fiddle_generic * generic_args ;
173- VALUE cfunc , types , cPointer ;
198+ VALUE cfunc ;
199+ VALUE abi ;
200+ VALUE arg_types ;
201+ VALUE cPointer ;
202+ VALUE is_variadic ;
203+ int n_arg_types ;
204+ int n_fixed_args = 0 ;
205+ int n_call_args = 0 ;
174206 int i ;
207+ int i_call ;
175208 VALUE alloc_buffer = 0 ;
176209
177210 cfunc = rb_iv_get (self , "@ptr" );
178- types = rb_iv_get (self , "@args" );
211+ abi = rb_iv_get (self , "@abi" );
212+ arg_types = rb_iv_get (self , "@argument_types" );
179213 cPointer = rb_const_get (mFiddle , rb_intern ("Pointer" ));
180-
181- Check_Max_Args ("number of arguments" , argc );
182- if (argc != (i = RARRAY_LENINT (types ))) {
183- rb_error_arity (argc , i , i );
214+ is_variadic = rb_iv_get (self , "@is_variadic" );
215+
216+ n_arg_types = RARRAY_LENINT (arg_types );
217+ n_fixed_args = n_arg_types ;
218+ if (RTEST (is_variadic )) {
219+ if (argc < n_arg_types ) {
220+ rb_error_arity (argc , n_arg_types , UNLIMITED_ARGUMENTS );
221+ }
222+ if (((argc - n_arg_types ) % 2 ) != 0 ) {
223+ rb_raise (rb_eArgError ,
224+ "variadic arguments must be type and value pairs: "
225+ "%" PRIsVALUE ,
226+ rb_ary_new_from_values (argc , argv ));
227+ }
228+ n_call_args = n_arg_types + ((argc - n_arg_types ) / 2 );
229+ }
230+ else {
231+ if (argc != n_arg_types ) {
232+ rb_error_arity (argc , n_arg_types , n_arg_types );
233+ }
234+ n_call_args = n_arg_types ;
184235 }
236+ Check_Max_Args ("the number of arguments" , n_call_args );
185237
186238 TypedData_Get_Struct (self , ffi_cif , & function_data_type , args .cif );
187239
240+ if (is_variadic && args .cif -> arg_types ) {
241+ xfree (args .cif -> arg_types );
242+ args .cif -> arg_types = NULL ;
243+ }
244+
245+ if (!args .cif -> arg_types ) {
246+ VALUE fixed_arg_types = arg_types ;
247+ VALUE return_type ;
248+ int c_return_type ;
249+ ffi_type * ffi_return_type ;
250+ ffi_type * * ffi_arg_types ;
251+ ffi_status result ;
252+
253+ arg_types = rb_ary_dup (fixed_arg_types );
254+ for (i = n_fixed_args ; i < argc ; i += 2 ) {
255+ VALUE arg_type = argv [i ];
256+ int c_arg_type = NUM2INT (arg_type );
257+ (void )INT2FFI_TYPE (c_arg_type ); /* raise */
258+ rb_ary_push (arg_types , INT2FIX (c_arg_type ));
259+ }
260+
261+ return_type = rb_iv_get (self , "@return_type" );
262+ c_return_type = FIX2INT (return_type );
263+ ffi_return_type = INT2FFI_TYPE (c_return_type );
264+
265+ ffi_arg_types = xcalloc (n_call_args + 1 , sizeof (ffi_type * ));
266+ for (i_call = 0 ; i_call < n_call_args ; i_call ++ ) {
267+ VALUE arg_type ;
268+ int c_arg_type ;
269+ arg_type = RARRAY_AREF (arg_types , i_call );
270+ c_arg_type = FIX2INT (arg_type );
271+ ffi_arg_types [i_call ] = INT2FFI_TYPE (c_arg_type );
272+ }
273+ ffi_arg_types [i_call ] = NULL ;
274+
275+ if (is_variadic ) {
276+ #ifdef HAVE_FFI_PREP_CIF_VAR
277+ result = ffi_prep_cif_var (args .cif ,
278+ FIX2INT (abi ),
279+ n_fixed_args ,
280+ n_call_args ,
281+ ffi_return_type ,
282+ ffi_arg_types );
283+ #else
284+ /* This code is never used because ffi_prep_cif_var()
285+ * availability check is done in #initialize. */
286+ result = FFI_BAD_TYPEDEF ;
287+ #endif
288+ }
289+ else {
290+ result = ffi_prep_cif (args .cif ,
291+ FIX2INT (abi ),
292+ n_call_args ,
293+ ffi_return_type ,
294+ ffi_arg_types );
295+ }
296+ if (result != FFI_OK ) {
297+ xfree (ffi_arg_types );
298+ args .cif -> arg_types = NULL ;
299+ rb_raise (rb_eRuntimeError , "error creating CIF %d" , result );
300+ }
301+ }
302+
188303 generic_args = ALLOCV (alloc_buffer ,
189- (size_t )(argc + 1 ) * sizeof (void * ) + (size_t )argc * sizeof (fiddle_generic ));
304+ sizeof (fiddle_generic ) * n_call_args +
305+ sizeof (void * ) * (n_call_args + 1 ));
190306 args .values = (void * * )((char * )generic_args +
191- (size_t )argc * sizeof (fiddle_generic ));
192-
193- for (i = 0 ; i < argc ; i ++ ) {
194- VALUE type = RARRAY_AREF (types , i );
195- VALUE src = argv [i ];
196- int argtype = FIX2INT (type );
197-
198- if (argtype == TYPE_VOIDP ) {
199- if (NIL_P (src )) {
200- src = INT2FIX (0 );
201- } else if (cPointer != CLASS_OF (src )) {
202- src = rb_funcall (cPointer , rb_intern ("[]" ), 1 , src );
203- }
204- src = rb_Integer (src );
205- }
206-
207- VALUE2GENERIC (argtype , src , & generic_args [i ]);
208- args .values [i ] = (void * )& generic_args [i ];
307+ sizeof (fiddle_generic ) * n_call_args );
308+
309+ for (i = 0 , i_call = 0 ;
310+ i < argc && i_call < n_call_args ;
311+ i ++ , i_call ++ ) {
312+ VALUE arg_type ;
313+ int c_arg_type ;
314+ VALUE src ;
315+ arg_type = RARRAY_AREF (arg_types , i_call );
316+ c_arg_type = FIX2INT (arg_type );
317+ if (i >= n_fixed_args ) {
318+ i ++ ;
319+ }
320+ src = argv [i ];
321+
322+ if (c_arg_type == TYPE_VOIDP ) {
323+ if (NIL_P (src )) {
324+ src = INT2FIX (0 );
325+ }
326+ else if (cPointer != CLASS_OF (src )) {
327+ src = rb_funcall (cPointer , rb_intern ("[]" ), 1 , src );
328+ }
329+ src = rb_Integer (src );
330+ }
331+
332+ VALUE2GENERIC (c_arg_type , src , & generic_args [i_call ]);
333+ args .values [i_call ] = (void * )& generic_args [i_call ];
209334 }
210- args .values [argc ] = NULL ;
335+ args .values [i_call ] = NULL ;
211336 args .fn = (void (* )(void ))NUM2PTR (cfunc );
212337
213338 (void )rb_thread_call_without_gvl (nogvl_ffi_call , & args , 0 , 0 );
0 commit comments