Ruby  3.1.0dev(2021-09-10revisionb76ad15ed0da636161de0243c547ee1e6fc95681)
function.c
Go to the documentation of this file.
1 #include <fiddle.h>
2 #include <ruby/thread.h>
3 
4 #include <stdbool.h>
5 
6 #ifdef PRIsVALUE
7 # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
8 # define RB_OBJ_STRING(obj) (obj)
9 #else
10 # define PRIsVALUE "s"
11 # define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
12 # define RB_OBJ_STRING(obj) StringValueCStr(obj)
13 #endif
14 
16 
17 #define MAX_ARGS (SIZE_MAX / (sizeof(void *) + sizeof(fiddle_generic)) - 1)
18 
19 #define Check_Max_Args(name, len) \
20  Check_Max_Args_(name, len, "")
21 #define Check_Max_Args_Long(name, len) \
22  Check_Max_Args_(name, len, "l")
23 #define Check_Max_Args_(name, len, fmt) \
24  do { \
25  if ((size_t)(len) >= MAX_ARGS) { \
26  rb_raise(rb_eTypeError, \
27  "%s is so large " \
28  "that it can cause integer overflow (%"fmt"d)", \
29  (name), (len)); \
30  } \
31  } while (0)
32 
33 static void
34 deallocate(void *p)
35 {
36  ffi_cif *cif = p;
37  if (cif->arg_types) xfree(cif->arg_types);
38  xfree(cif);
39 }
40 
41 static size_t
42 function_memsize(const void *p)
43 {
44  /* const */ffi_cif *ptr = (ffi_cif *)p;
45  size_t size = 0;
46 
47  size += sizeof(*ptr);
48 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
49  size += ffi_raw_size(ptr);
50 #endif
51 
52  return size;
53 }
54 
56  "fiddle/function",
57  {0, deallocate, function_memsize,},
58 };
59 
60 static VALUE
61 allocate(VALUE klass)
62 {
63  ffi_cif * cif;
64 
65  return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
66 }
67 
68 VALUE
69 rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
70 {
71  VALUE argv[3];
72 
73  argv[0] = address;
74  argv[1] = arg_types;
75  argv[2] = ret_type;
76 
78 }
79 
80 static VALUE
81 normalize_argument_types(const char *name,
82  VALUE arg_types,
83  bool *is_variadic)
84 {
85  VALUE normalized_arg_types;
86  int i;
87  int n_arg_types;
88  *is_variadic = false;
89 
90  Check_Type(arg_types, T_ARRAY);
91  n_arg_types = RARRAY_LENINT(arg_types);
92  Check_Max_Args(name, n_arg_types);
93 
94  normalized_arg_types = rb_ary_new_capa(n_arg_types);
95  for (i = 0; i < n_arg_types; i++) {
96  VALUE arg_type = RARRAY_AREF(arg_types, i);
97  int c_arg_type;
98  arg_type = rb_fiddle_type_ensure(arg_type);
99  c_arg_type = NUM2INT(arg_type);
100  if (c_arg_type == TYPE_VARIADIC) {
101  if (i != n_arg_types - 1) {
103  "Fiddle::TYPE_VARIADIC must be the last argument type: "
104  "%"PRIsVALUE,
105  arg_types);
106  }
107  *is_variadic = true;
108  break;
109  }
110  else {
111  (void)INT2FFI_TYPE(c_arg_type); /* raise */
112  }
113  rb_ary_push(normalized_arg_types, INT2FIX(c_arg_type));
114  }
115 
116  /* freeze to prevent inconsistency at calling #to_int later */
117  OBJ_FREEZE(normalized_arg_types);
118  return normalized_arg_types;
119 }
120 
121 static VALUE
122 initialize(int argc, VALUE argv[], VALUE self)
123 {
124  ffi_cif * cif;
125  VALUE ptr, arg_types, ret_type, abi, kwargs;
126  VALUE name = Qnil;
127  VALUE need_gvl = Qfalse;
128  int c_ret_type;
129  bool is_variadic = false;
130  ffi_abi c_ffi_abi;
131  void *cfunc;
132 
133  rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwargs);
134  rb_iv_set(self, "@closure", ptr);
135 
136  if (!NIL_P(kwargs)) {
137  enum {
138  kw_name,
139  kw_need_gvl,
140  kw_max_,
141  };
142  static ID kw[kw_max_];
143  VALUE args[kw_max_];
144  if (!kw[0]) {
145  kw[kw_name] = rb_intern_const("name");
146  kw[kw_need_gvl] = rb_intern_const("need_gvl");
147  }
148  rb_get_kwargs(kwargs, kw, 0, kw_max_, args);
149  if (args[kw_name] != Qundef) {
150  name = args[kw_name];
151  }
152  if (args[kw_need_gvl] != Qundef) {
153  need_gvl = args[kw_need_gvl];
154  }
155  }
156  rb_iv_set(self, "@name", name);
157  rb_iv_set(self, "@need_gvl", need_gvl);
158 
159  ptr = rb_Integer(ptr);
160  cfunc = NUM2PTR(ptr);
161  PTR2NUM(cfunc);
162  c_ffi_abi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi);
163  abi = INT2FIX(c_ffi_abi);
164  ret_type = rb_fiddle_type_ensure(ret_type);
165  c_ret_type = NUM2INT(ret_type);
166  (void)INT2FFI_TYPE(c_ret_type); /* raise */
167  ret_type = INT2FIX(c_ret_type);
168 
169  arg_types = normalize_argument_types("argument types",
170  arg_types,
171  &is_variadic);
172 #ifndef HAVE_FFI_PREP_CIF_VAR
173  if (is_variadic) {
175  "ffi_prep_cif_var() is required in libffi "
176  "for variadic arguments");
177  }
178 #endif
179 
180  rb_iv_set(self, "@ptr", ptr);
181  rb_iv_set(self, "@argument_types", arg_types);
182  rb_iv_set(self, "@return_type", ret_type);
183  rb_iv_set(self, "@abi", abi);
184  rb_iv_set(self, "@is_variadic", is_variadic ? Qtrue : Qfalse);
185 
186  TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
187  cif->arg_types = NULL;
188 
189  return self;
190 }
191 
193  ffi_cif *cif;
194  void (*fn)(void);
195  void **values;
197 };
198 
199 static void *
200 nogvl_ffi_call(void *ptr)
201 {
202  struct nogvl_ffi_call_args *args = ptr;
203 
204  ffi_call(args->cif, args->fn, &args->retval, args->values);
205 
206  return NULL;
207 }
208 
209 static VALUE
210 function_call(int argc, VALUE argv[], VALUE self)
211 {
212  struct nogvl_ffi_call_args args = { 0 };
213  fiddle_generic *generic_args;
214  VALUE cfunc;
215  VALUE abi;
216  VALUE arg_types;
217  VALUE cPointer;
218  VALUE is_variadic;
219  VALUE need_gvl;
220  int n_arg_types;
221  int n_fixed_args = 0;
222  int n_call_args = 0;
223  int i;
224  int i_call;
225  VALUE converted_args = Qnil;
226  VALUE alloc_buffer = 0;
227 
228  cfunc = rb_iv_get(self, "@ptr");
229  abi = rb_iv_get(self, "@abi");
230  arg_types = rb_iv_get(self, "@argument_types");
231  cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
232  is_variadic = rb_iv_get(self, "@is_variadic");
233  need_gvl = rb_iv_get(self, "@need_gvl");
234 
235  n_arg_types = RARRAY_LENINT(arg_types);
236  n_fixed_args = n_arg_types;
237  if (RTEST(is_variadic)) {
238  if (argc < n_arg_types) {
239  rb_error_arity(argc, n_arg_types, UNLIMITED_ARGUMENTS);
240  }
241  if (((argc - n_arg_types) % 2) != 0) {
243  "variadic arguments must be type and value pairs: "
244  "%"PRIsVALUE,
246  }
247  n_call_args = n_arg_types + ((argc - n_arg_types) / 2);
248  }
249  else {
250  if (argc != n_arg_types) {
251  rb_error_arity(argc, n_arg_types, n_arg_types);
252  }
253  n_call_args = n_arg_types;
254  }
255  Check_Max_Args("the number of arguments", n_call_args);
256 
257  TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif);
258 
259  if (is_variadic && args.cif->arg_types) {
260  xfree(args.cif->arg_types);
261  args.cif->arg_types = NULL;
262  }
263 
264  if (!args.cif->arg_types) {
265  VALUE fixed_arg_types = arg_types;
266  VALUE return_type;
267  int c_return_type;
268  ffi_type *ffi_return_type;
269  ffi_type **ffi_arg_types;
270  ffi_status result;
271 
272  arg_types = rb_ary_dup(fixed_arg_types);
273  for (i = n_fixed_args; i < argc; i += 2) {
274  VALUE arg_type = argv[i];
275  int c_arg_type;
276  arg_type = rb_fiddle_type_ensure(arg_type);
277  c_arg_type = NUM2INT(arg_type);
278  (void)INT2FFI_TYPE(c_arg_type); /* raise */
279  rb_ary_push(arg_types, INT2FIX(c_arg_type));
280  }
281 
282  return_type = rb_iv_get(self, "@return_type");
283  c_return_type = FIX2INT(return_type);
284  ffi_return_type = INT2FFI_TYPE(c_return_type);
285 
286  ffi_arg_types = xcalloc(n_call_args + 1, sizeof(ffi_type *));
287  for (i_call = 0; i_call < n_call_args; i_call++) {
288  VALUE arg_type;
289  int c_arg_type;
290  arg_type = RARRAY_AREF(arg_types, i_call);
291  c_arg_type = FIX2INT(arg_type);
292  ffi_arg_types[i_call] = INT2FFI_TYPE(c_arg_type);
293  }
294  ffi_arg_types[i_call] = NULL;
295 
296  if (is_variadic) {
297 #ifdef HAVE_FFI_PREP_CIF_VAR
298  result = ffi_prep_cif_var(args.cif,
299  FIX2INT(abi),
300  n_fixed_args,
301  n_call_args,
302  ffi_return_type,
303  ffi_arg_types);
304 #else
305  /* This code is never used because ffi_prep_cif_var()
306  * availability check is done in #initialize. */
307  result = FFI_BAD_TYPEDEF;
308 #endif
309  }
310  else {
311  result = ffi_prep_cif(args.cif,
312  FIX2INT(abi),
313  n_call_args,
314  ffi_return_type,
315  ffi_arg_types);
316  }
317  if (result != FFI_OK) {
318  xfree(ffi_arg_types);
319  args.cif->arg_types = NULL;
320  rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
321  }
322  }
323 
324  generic_args = ALLOCV(alloc_buffer,
325  sizeof(fiddle_generic) * n_call_args +
326  sizeof(void *) * (n_call_args + 1));
327  args.values = (void **)((char *)generic_args +
328  sizeof(fiddle_generic) * n_call_args);
329 
330  for (i = 0, i_call = 0;
331  i < argc && i_call < n_call_args;
332  i++, i_call++) {
333  VALUE arg_type;
334  int c_arg_type;
335  VALUE original_src;
336  VALUE src;
337  arg_type = RARRAY_AREF(arg_types, i_call);
338  c_arg_type = FIX2INT(arg_type);
339  if (i >= n_fixed_args) {
340  i++;
341  }
342  src = argv[i];
343 
344  if (c_arg_type == TYPE_VOIDP) {
345  if (NIL_P(src)) {
346  src = INT2FIX(0);
347  }
348  else if (cPointer != CLASS_OF(src)) {
349  src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
350  if (NIL_P(converted_args)) {
351  converted_args = rb_ary_new();
352  }
353  rb_ary_push(converted_args, src);
354  }
355  src = rb_Integer(src);
356  }
357 
358  original_src = src;
359  VALUE2GENERIC(c_arg_type, src, &generic_args[i_call]);
360  if (src != original_src) {
361  if (NIL_P(converted_args)) {
362  converted_args = rb_ary_new();
363  }
364  rb_ary_push(converted_args, src);
365  }
366  args.values[i_call] = (void *)&generic_args[i_call];
367  }
368  args.values[i_call] = NULL;
369  args.fn = (void(*)(void))NUM2PTR(cfunc);
370 
371  if (RTEST(need_gvl)) {
372  ffi_call(args.cif, args.fn, &(args.retval), args.values);
373  }
374  else {
375  (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
376  }
377 
378  {
379  int errno_keep = errno;
380 #if defined(_WIN32)
381  DWORD error = WSAGetLastError();
382  int socket_error = WSAGetLastError();
383  rb_funcall(mFiddle, rb_intern("win32_last_error="), 1,
384  ULONG2NUM(error));
385  rb_funcall(mFiddle, rb_intern("win32_last_socket_error="), 1,
386  INT2NUM(socket_error));
387 #endif
388  rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno_keep));
389  }
390 
391  ALLOCV_END(alloc_buffer);
392 
393  return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.retval);
394 }
395 
396 void
398 {
399  /*
400  * Document-class: Fiddle::Function
401  *
402  * == Description
403  *
404  * A representation of a C function
405  *
406  * == Examples
407  *
408  * === 'strcpy'
409  *
410  * @libc = Fiddle.dlopen "/lib/libc.so.6"
411  * #=> #<Fiddle::Handle:0x00000001d7a8d8>
412  * f = Fiddle::Function.new(
413  * @libc['strcpy'],
414  * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
415  * Fiddle::TYPE_VOIDP)
416  * #=> #<Fiddle::Function:0x00000001d8ee00>
417  * buff = "000"
418  * #=> "000"
419  * str = f.call(buff, "123")
420  * #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
421  * str.to_s
422  * => "123"
423  *
424  * === ABI check
425  *
426  * @libc = Fiddle.dlopen "/lib/libc.so.6"
427  * #=> #<Fiddle::Handle:0x00000001d7a8d8>
428  * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
429  * #=> #<Fiddle::Function:0x00000001d8ee00>
430  * f.abi == Fiddle::Function::DEFAULT
431  * #=> true
432  */
434 
435  /*
436  * Document-const: DEFAULT
437  *
438  * Default ABI
439  *
440  */
441  rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
442 
443 #ifdef HAVE_CONST_FFI_STDCALL
444  /*
445  * Document-const: STDCALL
446  *
447  * FFI implementation of WIN32 stdcall convention
448  *
449  */
450  rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
451 #endif
452 
454 
455  /*
456  * Document-method: call
457  *
458  * Calls the constructed Function, with +args+.
459  * Caller must ensure the underlying function is called in a
460  * thread-safe manner if running in a multi-threaded process.
461  *
462  * Note that it is not thread-safe to use this method to
463  * directly or indirectly call many Ruby C-extension APIs unless
464  * you don't pass +need_gvl: true+ to Fiddle::Function#new.
465  *
466  * For an example see Fiddle::Function
467  *
468  */
469  rb_define_method(cFiddleFunction, "call", function_call, -1);
470 
471  /*
472  * Document-method: new
473  * call-seq: new(ptr,
474  * args,
475  * ret_type,
476  * abi = DEFAULT,
477  * name: nil,
478  * need_gvl: false)
479  *
480  * Constructs a Function object.
481  * * +ptr+ is a referenced function, of a Fiddle::Handle
482  * * +args+ is an Array of arguments, passed to the +ptr+ function
483  * * +ret_type+ is the return type of the function
484  * * +abi+ is the ABI of the function
485  * * +name+ is the name of the function
486  * * +need_gvl+ is whether GVL is needed to call the function
487  *
488  */
489  rb_define_method(cFiddleFunction, "initialize", initialize, -1);
490 }
491 /* vim: set noet sws=4 sw=4: */
rb_const_get
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2645
Qundef
#define Qundef
Definition: special_consts.h:53
rb_ary_new_capa
VALUE rb_ary_new_capa(long capa)
Definition: array.c:748
rb_get_kwargs
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Definition: class.c:2136
TYPE_VOIDP
#define TYPE_VOIDP
Definition: fiddle.h:112
mFiddle
VALUE mFiddle
Definition: fiddle.c:3
GENERIC2VALUE
#define GENERIC2VALUE(_type, _retval)
Definition: conversions.h:41
CLASS_OF
#define CLASS_OF
Definition: globals.h:154
callback_args::cif
ffi_cif * cif
Definition: closure.c:62
VALUE2GENERIC
#define VALUE2GENERIC(_type, _src, _dst)
Definition: conversions.h:37
TYPE_VARIADIC
#define TYPE_VARIADIC
Definition: fiddle.h:122
rb_intern
ID rb_intern(const char *)
Definition: symbol.c:784
PRIsVALUE
#define PRIsVALUE
Definition: inttypes.h:77
fiddle.h
xcalloc
#define xcalloc
Definition: xmalloc.h:46
rb_eArgError
VALUE rb_eArgError
Definition: error.c:1094
xfree
#define xfree
Definition: xmalloc.h:49
rb_fiddle_new_function
VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
Definition: function.c:69
INT2FIX
#define INT2FIX
Definition: long.h:48
DWORD
IUnknown DWORD
Definition: win32ole.c:33
argv
char ** argv
Definition: ruby.c:243
ID
unsigned long ID
Definition: value.h:39
cFiddleFunction
VALUE cFiddleFunction
Definition: function.c:15
ptr
struct RIMemo * ptr
Definition: debug.c:87
UNLIMITED_ARGUMENTS
#define UNLIMITED_ARGUMENTS
Definition: error.h:29
rb_Integer
VALUE rb_Integer(VALUE)
Equivalent to Kernel#Integer in Ruby.
Definition: object.c:3150
stdbool.h
C99 shim for <stdbool.h>
nogvl_ffi_call_args::values
void ** values
Definition: function.c:195
NUM2INT
#define NUM2INT
Definition: int.h:44
thread.h
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
OBJ_FREEZE
#define OBJ_FREEZE
Definition: fl_type.h:143
fiddle_generic
Definition: conversions.h:6
rb_iv_get
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3659
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:3022
nogvl_ffi_call_args::fn
void(* fn)(void)
Definition: function.c:194
NIL_P
#define NIL_P
Definition: special_consts.h:46
rb_iv_set
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3670
INT2NUM
#define INT2NUM
Definition: int.h:43
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:50
ULONG2NUM
#define ULONG2NUM
Definition: long.h:60
Qfalse
#define Qfalse
Definition: special_consts.h:50
rb_ary_push
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:1312
nogvl_ffi_call_args::retval
fiddle_generic retval
Definition: function.c:196
Qnil
#define Qnil
Definition: special_consts.h:51
INT2FFI_TYPE
#define INT2FFI_TYPE(_type)
Definition: conversions.h:39
NUM2PTR
#define NUM2PTR(x)
Definition: conversions.h:46
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:1091
NULL
#define NULL
Definition: regenc.h:69
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2347
RTEST
#define RTEST
Definition: special_consts.h:42
Qtrue
#define Qtrue
Definition: special_consts.h:52
rb_eNotImpError
VALUE rb_eNotImpError
Definition: error.c:1103
nogvl_ffi_call_args
Definition: function.c:192
VALUE
unsigned long VALUE
Definition: value.h:38
ALLOCV_END
#define ALLOCV_END
Definition: memory.h:140
rb_ary_new_from_values
VALUE rb_ary_new_from_values(long n, const VALUE *elts)
Definition: array.c:793
rb_funcall
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1113
Init_fiddle_function
void Init_fiddle_function(void)
Definition: function.c:397
rb_define_const
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:3168
argc
int argc
Definition: ruby.c:242
PTR2NUM
#define PTR2NUM(x)
Definition: conversions.h:45
T_ARRAY
#define T_ARRAY
Definition: value_type.h:56
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
ALLOCV
#define ALLOCV
Definition: memory.h:138
rb_data_type_struct
Definition: rtypeddata.h:70
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition: missing.h:201
Check_Max_Args
#define Check_Max_Args(name, len)
Definition: function.c:19
nogvl_ffi_call_args::cif
ffi_cif * cif
Definition: function.c:193
rb_class_new_instance
VALUE rb_class_new_instance(int, const VALUE *, VALUE)
Allocates and initializes an instance of klass.
Definition: object.c:1964
rb_thread_call_without_gvl
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
rb_ary_dup
VALUE rb_ary_dup(VALUE ary)
Definition: array.c:2675
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:809
callback_args::args
void ** args
Definition: closure.c:64
rb_fiddle_type_ensure
VALUE rb_fiddle_type_ensure(VALUE type)
Definition: conversions.c:4
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:655
size
rb_atomic_t size
Definition: signal.c:509
rb_ary_new
VALUE rb_ary_new(void)
Definition: array.c:754
rb_error_arity
void rb_error_arity(int argc, int min, int max)
Definition: vm_insnhelper.c:442
FIX2INT
#define FIX2INT
Definition: int.h:41
function_data_type
const rb_data_type_t function_data_type
Definition: function.c:55
name
const char * name
Definition: nkf.c:208