@@ -37,23 +37,38 @@ static ID id_win32_last_error, id_win32_last_socket_error;
3737 } \
3838 } while (0)
3939
40+ /* Call-path state, converted and validated once in #initialize.
41+ * Deliberately holds no VALUEs so the type needs no dmark/dcompact. */
42+ typedef struct fiddle_function {
43+ ffi_cif cif ;
44+ void (* fn )(void );
45+ int * argument_types ;
46+ int n_argument_types ;
47+ int return_type ;
48+ ffi_abi abi ;
49+ bool is_variadic ;
50+ bool need_gvl ;
51+ } fiddle_function_t ;
52+
4053static void
4154deallocate (void * p )
4255{
43- ffi_cif * cif = p ;
44- if (cif -> arg_types ) xfree (cif -> arg_types );
45- xfree (cif );
56+ fiddle_function_t * func = p ;
57+ if (func -> cif .arg_types ) xfree (func -> cif .arg_types );
58+ if (func -> argument_types ) xfree (func -> argument_types );
59+ xfree (func );
4660}
4761
4862static size_t
4963function_memsize (const void * p )
5064{
51- /* const */ ffi_cif * ptr = ( ffi_cif * ) p ;
65+ const fiddle_function_t * func = p ;
5266 size_t size = 0 ;
5367
54- size += sizeof (* ptr );
68+ size += sizeof (* func );
69+ size += func -> n_argument_types * sizeof (int );
5570#if !defined(FFI_NO_RAW_API ) || !FFI_NO_RAW_API
56- size += ffi_raw_size (ptr );
71+ size += ffi_raw_size (( ffi_cif * ) & func -> cif );
5772#endif
5873
5974 return size ;
@@ -72,9 +87,9 @@ const rb_data_type_t function_data_type = {
7287static VALUE
7388allocate (VALUE klass )
7489{
75- ffi_cif * cif ;
90+ fiddle_function_t * func ;
7691
77- return TypedData_Make_Struct (klass , ffi_cif , & function_data_type , cif );
92+ return TypedData_Make_Struct (klass , fiddle_function_t , & function_data_type , func );
7893}
7994
8095VALUE
@@ -133,14 +148,15 @@ normalize_argument_types(const char *name,
133148static VALUE
134149initialize (int argc , VALUE argv [], VALUE self )
135150{
136- ffi_cif * cif ;
151+ fiddle_function_t * func ;
137152 VALUE ptr , arg_types , ret_type , abi , kwargs ;
138153 VALUE name = Qnil ;
139154 VALUE need_gvl = Qfalse ;
140155 int c_ret_type ;
141156 bool is_variadic = false;
142157 ffi_abi c_ffi_abi ;
143158 void * cfunc ;
159+ int i ;
144160
145161 rb_scan_args (argc , argv , "31:" , & ptr , & arg_types , & ret_type , & abi , & kwargs );
146162 rb_ivar_set (self , id_closure , ptr );
@@ -200,8 +216,27 @@ initialize(int argc, VALUE argv[], VALUE self)
200216 rb_ivar_set (self , id_abi , abi );
201217 rb_ivar_set (self , id_is_variadic , is_variadic ? Qtrue : Qfalse );
202218
203- TypedData_Get_Struct (self , ffi_cif , & function_data_type , cif );
204- cif -> arg_types = NULL ;
219+ TypedData_Get_Struct (self , fiddle_function_t , & function_data_type , func );
220+ func -> fn = (void (* )(void ))(VALUE )cfunc ;
221+ func -> abi = c_ffi_abi ;
222+ func -> return_type = c_ret_type ;
223+ func -> is_variadic = is_variadic ;
224+ func -> need_gvl = RTEST (need_gvl );
225+
226+ /* allow re-initialization without leaking or double-freeing */
227+ if (func -> argument_types ) {
228+ xfree (func -> argument_types );
229+ func -> argument_types = NULL ;
230+ }
231+ func -> n_argument_types = RARRAY_LENINT (arg_types );
232+ func -> argument_types = ALLOC_N (int , func -> n_argument_types );
233+ for (i = 0 ; i < func -> n_argument_types ; i ++ ) {
234+ func -> argument_types [i ] = FIX2INT (RARRAY_AREF (arg_types , i ));
235+ }
236+ if (func -> cif .arg_types ) {
237+ xfree (func -> cif .arg_types );
238+ func -> cif .arg_types = NULL ;
239+ }
205240
206241 return self ;
207242}
@@ -227,95 +262,93 @@ static VALUE
227262function_call (int argc , VALUE argv [], VALUE self )
228263{
229264 struct nogvl_ffi_call_args args = { 0 };
265+ fiddle_function_t * func ;
230266 fiddle_generic * generic_args ;
231- VALUE cfunc ;
232- VALUE abi ;
233- VALUE arg_types ;
234- VALUE is_variadic ;
235- VALUE need_gvl ;
236- int n_arg_types ;
237267 int n_fixed_args = 0 ;
238268 int n_call_args = 0 ;
239269 int i ;
240270 int i_call ;
271+ int * call_arg_types ;
241272 VALUE * converted_args ;
242273 int n_converted_args = 0 ;
243274 VALUE alloc_buffer = 0 ;
244275
245- cfunc = rb_ivar_get (self , id_ptr );
246- abi = rb_ivar_get (self , id_abi );
247- arg_types = rb_ivar_get (self , id_argument_types );
248- is_variadic = rb_ivar_get (self , id_is_variadic );
249- need_gvl = rb_ivar_get (self , id_need_gvl );
276+ TypedData_Get_Struct (self , fiddle_function_t , & function_data_type , func );
277+ args .cif = & func -> cif ;
250278
251- n_arg_types = RARRAY_LENINT (arg_types );
252- n_fixed_args = n_arg_types ;
253- if (RTEST (is_variadic )) {
254- if (argc < n_arg_types ) {
255- rb_error_arity (argc , n_arg_types , UNLIMITED_ARGUMENTS );
279+ n_fixed_args = func -> n_argument_types ;
280+ if (func -> is_variadic ) {
281+ if (argc < n_fixed_args ) {
282+ rb_error_arity (argc , n_fixed_args , UNLIMITED_ARGUMENTS );
256283 }
257- if (((argc - n_arg_types ) % 2 ) != 0 ) {
284+ if (((argc - n_fixed_args ) % 2 ) != 0 ) {
258285 rb_raise (rb_eArgError ,
259286 "variadic arguments must be type and value pairs: "
260287 "%" PRIsVALUE ,
261288 rb_ary_new_from_values (argc , argv ));
262289 }
263- n_call_args = n_arg_types + ((argc - n_arg_types ) / 2 );
290+ n_call_args = n_fixed_args + ((argc - n_fixed_args ) / 2 );
264291 }
265292 else {
266- if (argc != n_arg_types ) {
267- rb_error_arity (argc , n_arg_types , n_arg_types );
293+ if (argc != n_fixed_args ) {
294+ rb_error_arity (argc , n_fixed_args , n_fixed_args );
268295 }
269- n_call_args = n_arg_types ;
296+ n_call_args = n_fixed_args ;
270297 }
271298 Check_Max_Args ("the number of arguments" , n_call_args );
272299
273- TypedData_Get_Struct (self , ffi_cif , & function_data_type , args .cif );
300+ generic_args = ALLOCV (alloc_buffer ,
301+ sizeof (fiddle_generic ) * n_call_args +
302+ sizeof (void * ) * (n_call_args + 1 ) +
303+ sizeof (VALUE ) * n_call_args +
304+ (func -> is_variadic ? sizeof (int ) * n_call_args : 0 ));
305+ args .values = (void * * )((char * )generic_args +
306+ sizeof (fiddle_generic ) * n_call_args );
307+ /* GC-scanned (conservatively) as part of the ALLOCV buffer */
308+ converted_args = (VALUE * )((char * )args .values +
309+ sizeof (void * ) * (n_call_args + 1 ));
310+ MEMZERO (converted_args , VALUE , n_call_args );
274311
275- if (is_variadic && args .cif -> arg_types ) {
276- xfree (args .cif -> arg_types );
277- args .cif -> arg_types = NULL ;
312+ if (func -> is_variadic ) {
313+ call_arg_types = (int * )(converted_args + n_call_args );
314+ MEMCPY (call_arg_types , func -> argument_types , int , n_fixed_args );
315+ for (i = n_fixed_args , i_call = n_fixed_args ;
316+ i < argc ;
317+ i += 2 , i_call ++ ) {
318+ VALUE arg_type = rb_fiddle_type_ensure (argv [i ]);
319+ int c_arg_type = NUM2INT (arg_type );
320+ (void )INT2FFI_TYPE (c_arg_type ); /* raise */
321+ call_arg_types [i_call ] = c_arg_type ;
322+ }
323+
324+ /* Variadic calls may pass different types each call, so the
325+ * cif must always be re-prepared */
326+ if (func -> cif .arg_types ) {
327+ xfree (func -> cif .arg_types );
328+ func -> cif .arg_types = NULL ;
329+ }
330+ }
331+ else {
332+ call_arg_types = func -> argument_types ;
278333 }
279334
280- if (!args .cif -> arg_types ) {
281- VALUE fixed_arg_types = arg_types ;
282- VALUE return_type ;
283- int c_return_type ;
284- ffi_type * ffi_return_type ;
335+ if (!func -> cif .arg_types ) {
285336 ffi_type * * ffi_arg_types ;
286337 ffi_status result ;
287338
288- arg_types = rb_ary_dup (fixed_arg_types );
289- for (i = n_fixed_args ; i < argc ; i += 2 ) {
290- VALUE arg_type = argv [i ];
291- int c_arg_type ;
292- arg_type = rb_fiddle_type_ensure (arg_type );
293- c_arg_type = NUM2INT (arg_type );
294- (void )INT2FFI_TYPE (c_arg_type ); /* raise */
295- rb_ary_push (arg_types , INT2FIX (c_arg_type ));
296- }
297-
298- return_type = rb_ivar_get (self , id_return_type );
299- c_return_type = FIX2INT (return_type );
300- ffi_return_type = INT2FFI_TYPE (c_return_type );
301-
302339 ffi_arg_types = xcalloc (n_call_args + 1 , sizeof (ffi_type * ));
303340 for (i_call = 0 ; i_call < n_call_args ; i_call ++ ) {
304- VALUE arg_type ;
305- int c_arg_type ;
306- arg_type = RARRAY_AREF (arg_types , i_call );
307- c_arg_type = FIX2INT (arg_type );
308- ffi_arg_types [i_call ] = INT2FFI_TYPE (c_arg_type );
341+ ffi_arg_types [i_call ] = INT2FFI_TYPE (call_arg_types [i_call ]);
309342 }
310343 ffi_arg_types [i_call ] = NULL ;
311344
312- if (is_variadic ) {
345+ if (func -> is_variadic ) {
313346#ifdef HAVE_FFI_PREP_CIF_VAR
314- result = ffi_prep_cif_var (args . cif ,
315- FIX2INT ( abi ) ,
347+ result = ffi_prep_cif_var (& func -> cif ,
348+ func -> abi ,
316349 n_fixed_args ,
317350 n_call_args ,
318- ffi_return_type ,
351+ INT2FFI_TYPE ( func -> return_type ) ,
319352 ffi_arg_types );
320353#else
321354 /* This code is never used because ffi_prep_cif_var()
@@ -324,39 +357,25 @@ function_call(int argc, VALUE argv[], VALUE self)
324357#endif
325358 }
326359 else {
327- result = ffi_prep_cif (args . cif ,
328- FIX2INT ( abi ) ,
360+ result = ffi_prep_cif (& func -> cif ,
361+ func -> abi ,
329362 n_call_args ,
330- ffi_return_type ,
363+ INT2FFI_TYPE ( func -> return_type ) ,
331364 ffi_arg_types );
332365 }
333366 if (result != FFI_OK ) {
334367 xfree (ffi_arg_types );
335- args . cif -> arg_types = NULL ;
368+ func -> cif . arg_types = NULL ;
336369 rb_raise (rb_eRuntimeError , "error creating CIF %d" , result );
337370 }
338371 }
339372
340- generic_args = ALLOCV (alloc_buffer ,
341- sizeof (fiddle_generic ) * n_call_args +
342- sizeof (void * ) * (n_call_args + 1 ) +
343- sizeof (VALUE ) * n_call_args );
344- args .values = (void * * )((char * )generic_args +
345- sizeof (fiddle_generic ) * n_call_args );
346- /* GC-scanned (conservatively) as part of the ALLOCV buffer */
347- converted_args = (VALUE * )((char * )args .values +
348- sizeof (void * ) * (n_call_args + 1 ));
349- MEMZERO (converted_args , VALUE , n_call_args );
350-
351373 for (i = 0 , i_call = 0 ;
352374 i < argc && i_call < n_call_args ;
353375 i ++ , i_call ++ ) {
354- VALUE arg_type ;
355- int c_arg_type ;
376+ int c_arg_type = call_arg_types [i_call ];
356377 VALUE original_src ;
357378 VALUE src ;
358- arg_type = RARRAY_AREF (arg_types , i_call );
359- c_arg_type = FIX2INT (arg_type );
360379 if (i >= n_fixed_args ) {
361380 i ++ ;
362381 }
@@ -389,9 +408,9 @@ function_call(int argc, VALUE argv[], VALUE self)
389408 args .values [i_call ] = (void * )& generic_args [i_call ];
390409 }
391410 args .values [i_call ] = NULL ;
392- args .fn = ( void ( * )( void ))( VALUE ) NUM2PTR ( cfunc ) ;
411+ args .fn = func -> fn ;
393412
394- if (RTEST ( need_gvl ) ) {
413+ if (func -> need_gvl ) {
395414 ffi_call (args .cif , args .fn , & (args .retval ), args .values );
396415 }
397416 else {
@@ -414,7 +433,7 @@ function_call(int argc, VALUE argv[], VALUE self)
414433
415434 ALLOCV_END (alloc_buffer );
416435
417- return GENERIC2VALUE (rb_ivar_get ( self , id_return_type ), args .retval );
436+ return GENERIC2VALUE (INT2FIX ( func -> return_type ), args .retval );
418437}
419438
420439void
0 commit comments