Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)
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
33static void
34deallocate(void *p)
35{
36 ffi_cif *cif = p;
37 if (cif->arg_types) xfree(cif->arg_types);
38 xfree(cif);
39}
40
41static size_t
42function_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
50#endif
51
52 return size;
53}
54
56 "fiddle/function",
57 {0, deallocate, function_memsize,},
58};
59
60static VALUE
61allocate(VALUE klass)
62{
63 ffi_cif * cif;
64
65 return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
66}
67
69rb_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
80static VALUE
81normalize_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
121static VALUE
122initialize(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
199static void *
200nogvl_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
209static VALUE
210function_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) {
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 int socket_error = WSAGetLastError();
382 rb_funcall(mFiddle, rb_intern("win32_last_error="), 1,
383 INT2NUM(errno_keep));
384 rb_funcall(mFiddle, rb_intern("win32_last_socket_error="), 1,
385 INT2NUM(socket_error));
386#endif
387 rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno_keep));
388 }
389
390 ALLOCV_END(alloc_buffer);
391
392 return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.retval);
393}
394
395void
397{
398 /*
399 * Document-class: Fiddle::Function
400 *
401 * == Description
402 *
403 * A representation of a C function
404 *
405 * == Examples
406 *
407 * === 'strcpy'
408 *
409 * @libc = Fiddle.dlopen "/lib/libc.so.6"
410 * #=> #<Fiddle::Handle:0x00000001d7a8d8>
411 * f = Fiddle::Function.new(
412 * @libc['strcpy'],
413 * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
414 * Fiddle::TYPE_VOIDP)
415 * #=> #<Fiddle::Function:0x00000001d8ee00>
416 * buff = "000"
417 * #=> "000"
418 * str = f.call(buff, "123")
419 * #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
420 * str.to_s
421 * => "123"
422 *
423 * === ABI check
424 *
425 * @libc = Fiddle.dlopen "/lib/libc.so.6"
426 * #=> #<Fiddle::Handle:0x00000001d7a8d8>
427 * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
428 * #=> #<Fiddle::Function:0x00000001d8ee00>
429 * f.abi == Fiddle::Function::DEFAULT
430 * #=> true
431 */
433
434 /*
435 * Document-const: DEFAULT
436 *
437 * Default ABI
438 *
439 */
441
442#ifdef HAVE_CONST_FFI_STDCALL
443 /*
444 * Document-const: STDCALL
445 *
446 * FFI implementation of WIN32 stdcall convention
447 *
448 */
450#endif
451
453
454 /*
455 * Document-method: call
456 *
457 * Calls the constructed Function, with +args+.
458 * Caller must ensure the underlying function is called in a
459 * thread-safe manner if running in a multi-threaded process.
460 *
461 * Note that it is not thread-safe to use this method to
462 * directly or indirectly call many Ruby C-extension APIs unless
463 * you don't pass +need_gvl: true+ to Fiddle::Function#new.
464 *
465 * For an example see Fiddle::Function
466 *
467 */
468 rb_define_method(cFiddleFunction, "call", function_call, -1);
469
470 /*
471 * Document-method: new
472 * call-seq: new(ptr,
473 * args,
474 * ret_type,
475 * abi = DEFAULT,
476 * name: nil,
477 * need_gvl: false)
478 *
479 * Constructs a Function object.
480 * * +ptr+ is a referenced function, of a Fiddle::Handle
481 * * +args+ is an Array of arguments, passed to the +ptr+ function
482 * * +ret_type+ is the return type of the function
483 * * +abi+ is the ABI of the function
484 * * +name+ is the name of the function
485 * * +need_gvl+ is whether GVL is needed to call the function
486 *
487 */
488 rb_define_method(cFiddleFunction, "initialize", initialize, -1);
489}
490/* vim: set noet sws=4 sw=4: */
void ffi_call(ffi_cif *cif, void(*fn)(void), void *rvalue, void **avalue)
Definition: ffi.c:813
ffi_abi
Definition: ffitarget.h:34
@ FFI_DEFAULT_ABI
Definition: ffitarget.h:38
VALUE rb_ary_new_from_values(long n, const VALUE *elts)
Definition: array.c:788
VALUE rb_ary_dup(VALUE ary)
Definition: array.c:2666
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:1301
VALUE rb_ary_new(void)
Definition: array.c:749
VALUE rb_ary_new_capa(long capa)
Definition: array.c:743
VALUE rb_fiddle_type_ensure(VALUE type)
Definition: conversions.c:4
#define NUM2PTR(x)
Definition: conversions.h:46
#define INT2FFI_TYPE(_type)
Definition: conversions.h:39
#define VALUE2GENERIC(_type, _src, _dst)
Definition: conversions.h:37
#define GENERIC2VALUE(_type, _retval)
Definition: conversions.h:41
#define PTR2NUM(x)
Definition: conversions.h:45
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
struct RIMemo * ptr
Definition: debug.c:88
VALUE mFiddle
Definition: fiddle.c:3
#define TYPE_VARIADIC
Definition: fiddle.h:118
#define TYPE_VOIDP
Definition: fiddle.h:108
const rb_data_type_t function_data_type
Definition: function.c:55
VALUE cFiddleFunction
Definition: function.c:15
#define PRIsVALUE
Definition: function.c:10
VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
Definition: function.c:69
#define Check_Max_Args(name, len)
Definition: function.c:19
void Init_fiddle_function(void)
Definition: function.c:396
#define CLASS_OF
Definition: globals.h:153
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:797
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2296
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Definition: class.c:2085
#define OBJ_FREEZE
Definition: fl_type.h:134
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
VALUE rb_eNotImpError
Definition: error.c:1067
VALUE rb_eRuntimeError
Definition: error.c:1055
VALUE rb_eArgError
Definition: error.c:1058
VALUE rb_cObject
Object class.
Definition: object.c:49
VALUE rb_class_new_instance(int, const VALUE *, VALUE)
Allocates and initializes an instance of klass.
Definition: object.c:1953
VALUE rb_Integer(VALUE)
Equivalent to Kernel#Integer in Ruby.
Definition: object.c:3138
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1077
#define UNLIMITED_ARGUMENTS
Definition: error.h:29
void rb_error_arity(int, int, int)
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2624
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
ID rb_intern(const char *)
Definition: symbol.c:785
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3580
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:3150
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3569
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
#define FIX2INT
Definition: int.h:41
#define NUM2INT
Definition: int.h:44
#define INT2NUM
Definition: int.h:43
voidpf void uLong size
Definition: ioapi.h:138
#define INT2FIX
Definition: long.h:48
#define ALLOCV
Definition: memory.h:138
#define ALLOCV_END
Definition: memory.h:140
const char * name
Definition: nkf.c:208
ffi_status ffi_prep_cif_var(ffi_cif *cif, ffi_abi abi, unsigned int nfixedargs, unsigned int ntotalargs, ffi_type *rtype, ffi_type **atypes)
Definition: prep_cif.c:232
ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs, ffi_type *rtype, ffi_type **atypes)
Definition: prep_cif.c:226
#define RARRAY_AREF(a, i)
Definition: psych_emitter.c:7
size_t ffi_raw_size(ffi_cif *cif)
Definition: raw_api.c:35
#define NULL
Definition: regenc.h:69
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
int argc
Definition: ruby.c:240
char ** argv
Definition: ruby.c:241
#define Qundef
#define Qtrue
#define RTEST
#define Qnil
#define Qfalse
#define NIL_P
C99 shim for <stdbool.h>
void(* fn)(void)
Definition: function.c:194
fiddle_generic retval
Definition: function.c:196
unsigned long VALUE
Definition: value.h:38
unsigned long ID
Definition: value.h:39
#define T_ARRAY
Definition: value_type.h:55
@ FFI_STDCALL
Definition: ffitarget.h:110
#define xfree
Definition: xmalloc.h:49
#define xcalloc
Definition: xmalloc.h:46