Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

function.c
Go to the documentation of this file.
1 #include <fiddle.h>
2 #include <ruby/thread.h>
3
4 #ifdef PRIsVALUE
5 # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
6 # define RB_OBJ_STRING(obj) (obj)
7 #else
8 # define PRIsVALUE "s"
9 # define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
10 # define RB_OBJ_STRING(obj) StringValueCStr(obj)
11 #endif
12
13 VALUE cFiddleFunction;
14
15 #define MAX_ARGS (SIZE_MAX / (sizeof(void *) + sizeof(fiddle_generic)) - 1)
16
17 #define Check_Max_Args(name, len) \
18 Check_Max_Args_(name, len, "")
19 #define Check_Max_Args_Long(name, len) \
20 Check_Max_Args_(name, len, "l")
21 #define Check_Max_Args_(name, len, fmt) \
22 if ((size_t)(len) < MAX_ARGS) { \
23 /* OK */ \
24 } \
25 else { \
26 rb_raise(rb_eTypeError, \
27 name" is so large that it can cause integer overflow (%"fmt"d)", \
28 (len)); \
29 }
30
31 static void
32 deallocate(void *p)
33 {
34 ffi_cif *ptr = p;
35 if (ptr->arg_types) xfree(ptr->arg_types);
36 xfree(ptr);
37 }
38
39 static size_t
40 function_memsize(const void *p)
41 {
42 /* const */ffi_cif *ptr = (ffi_cif *)p;
43 size_t size = 0;
44
45 size += sizeof(*ptr);
46 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
47 size += ffi_raw_size(ptr);
48 #endif
49
50 return size;
51 }
52
53 const rb_data_type_t function_data_type = {
54 "fiddle/function",
55 {0, deallocate, function_memsize,},
56 };
57
58 static VALUE
59 allocate(VALUE klass)
60 {
61 ffi_cif * cif;
62
63 return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
64 }
65
66 VALUE
67 rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
68 {
69 VALUE argv[3];
70
71 argv[0] = address;
72 argv[1] = arg_types;
73 argv[2] = ret_type;
74
75 return rb_class_new_instance(3, argv, cFiddleFunction);
76 }
77
78 static int
79 parse_keyword_arg_i(VALUE key, VALUE value, VALUE self)
80 {
81 if (key == ID2SYM(rb_intern("name"))) {
82 rb_iv_set(self, "@name", value);
83 } else {
84 rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE,
85 RB_OBJ_STRING(key));
86 }
87 return ST_CONTINUE;
88 }
89
90 static VALUE
91 initialize(int argc, VALUE argv[], VALUE self)
92 {
93 ffi_cif * cif;
94 ffi_type **arg_types, *rtype;
95 ffi_status result;
96 VALUE ptr, args, ret_type, abi, kwds, ary;
97 int i, len;
98 int nabi;
99 void *cfunc;
100
101 rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds);
102 rb_iv_set(self, "@closure", ptr);
103
104 ptr = rb_Integer(ptr);
105 cfunc = NUM2PTR(ptr);
106 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 ary = rb_ary_subseq(args, 0, len);
117 for (i = 0; i < RARRAY_LEN(args); i++) {
118 VALUE a = RARRAY_AREF(args, i);
119 int type = NUM2INT(a);
120 (void)INT2FFI_TYPE(type); /* raise */
121 if (INT2FIX(type) != a) rb_ary_store(ary, i, INT2FIX(type));
122 }
123 OBJ_FREEZE(ary);
124
125 rb_iv_set(self, "@ptr", ptr);
126 rb_iv_set(self, "@args", args);
127 rb_iv_set(self, "@return_type", ret_type);
128 rb_iv_set(self, "@abi", abi);
129
130 if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self);
131
132 TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
133
134 arg_types = xcalloc(len + 1, sizeof(ffi_type *));
135
136 for (i = 0; i < RARRAY_LEN(args); i++) {
137 int type = NUM2INT(RARRAY_AREF(args, i));
138 arg_types[i] = INT2FFI_TYPE(type);
139 }
140 arg_types[len] = NULL;
141
142 result = ffi_prep_cif(cif, nabi, len, rtype, arg_types);
143
144 if (result)
145 rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
146
147 return self;
148 }
149
150 struct nogvl_ffi_call_args {
151 ffi_cif *cif;
152 void (*fn)(void);
153 void **values;
154 fiddle_generic retval;
155 };
156
157 static void *
158 nogvl_ffi_call(void *ptr)
159 {
160 struct nogvl_ffi_call_args *args = ptr;
161
162 ffi_call(args->cif, args->fn, &args->retval, args->values);
163
164 return NULL;
165 }
166
167 static VALUE
168 function_call(int argc, VALUE argv[], VALUE self)
169 {
170 struct nogvl_ffi_call_args args = { 0 };
171 fiddle_generic *generic_args;
172 VALUE cfunc, types, cPointer;
173 int i;
174 VALUE alloc_buffer = 0;
175
176 cfunc = rb_iv_get(self, "@ptr");
177 types = rb_iv_get(self, "@args");
178 cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
179
180 Check_Max_Args("number of arguments", argc);
181 if (argc != (i = RARRAY_LENINT(types))) {
182 rb_error_arity(argc, i, i);
183 }
184
185 TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif);
186
187 generic_args = ALLOCV(alloc_buffer,
188 (size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic));
189 args.values = (void **)((char *)generic_args +
190 (size_t)argc * sizeof(fiddle_generic));
191
192 for (i = 0; i < argc; i++) {
193 VALUE type = RARRAY_AREF(types, i);
194 VALUE src = argv[i];
195 int argtype = FIX2INT(type);
196
197 if (argtype == TYPE_VOIDP) {
198 if(NIL_P(src)) {
199 src = INT2FIX(0);
200 } else if(cPointer != CLASS_OF(src)) {
201 src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
202 }
203 src = rb_Integer(src);
204 }
205
206 VALUE2GENERIC(argtype, src, &generic_args[i]);
207 args.values[i] = (void *)&generic_args[i];
208 }
209 args.values[argc] = NULL;
210 args.fn = (void(*)(void))NUM2PTR(cfunc);
211
212 (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
213
214 rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
215 #if defined(_WIN32)
216 rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
217 #endif
218
219 ALLOCV_END(alloc_buffer);
220
221 return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.retval);
222 }
223
224 void
225 Init_fiddle_function(void)
226 {
227 /*
228 * Document-class: Fiddle::Function
229 *
230 * == Description
231 *
232 * A representation of a C function
233 *
234 * == Examples
235 *
236 * === 'strcpy'
237 *
238 * @libc = Fiddle.dlopen "/lib/libc.so.6"
239 * #=> #<Fiddle::Handle:0x00000001d7a8d8>
240 * f = Fiddle::Function.new(
241 * @libc['strcpy'],
242 * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
243 * Fiddle::TYPE_VOIDP)
244 * #=> #<Fiddle::Function:0x00000001d8ee00>
245 * buff = "000"
246 * #=> "000"
247 * str = f.call(buff, "123")
248 * #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
249 * str.to_s
250 * => "123"
251 *
252 * === ABI check
253 *
254 * @libc = Fiddle.dlopen "/lib/libc.so.6"
255 * #=> #<Fiddle::Handle:0x00000001d7a8d8>
256 * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
257 * #=> #<Fiddle::Function:0x00000001d8ee00>
258 * f.abi == Fiddle::Function::DEFAULT
259 * #=> true
260 */
261 cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
262
263 /*
264 * Document-const: DEFAULT
265 *
266 * Default ABI
267 *
268 */
269 rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
270
271 #ifdef HAVE_CONST_FFI_STDCALL
272 /*
273 * Document-const: STDCALL
274 *
275 * FFI implementation of WIN32 stdcall convention
276 *
277 */
278 rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
279 #endif
280
281 rb_define_alloc_func(cFiddleFunction, allocate);
282
283 /*
284 * Document-method: call
285 *
286 * Calls the constructed Function, with +args+.
287 * Caller must ensure the underlying function is called in a
288 * thread-safe manner if running in a multi-threaded process.
289 *
290 * For an example see Fiddle::Function
291 *
292 */
293 rb_define_method(cFiddleFunction, "call", function_call, -1);
294
295 /*
296 * Document-method: new
297 * call-seq: new(ptr, args, ret_type, abi = DEFAULT)
298 *
299 * Constructs a Function object.
300 * * +ptr+ is a referenced function, of a Fiddle::Handle
301 * * +args+ is an Array of arguments, passed to the +ptr+ function
302 * * +ret_type+ is the return type of the function
303 * * +abi+ is the ABI of the function
304 *
305 */
306 rb_define_method(cFiddleFunction, "initialize", initialize, -1);
307 }
308 /* vim: set noet sws=4 sw=4: */
TYPE_VOIDP
#define TYPE_VOIDP
Definition: fiddle.h:108
void
void
Definition: rb_mjit_min_header-2.7.0.h:13273
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: ruby.h:1244
Check_Type
#define Check_Type(v, t)
Definition: ruby.h:595
xcalloc
#define xcalloc
Definition: defines.h:213
FIX2INT
#define FIX2INT(x)
Definition: ruby.h:717
mFiddle
VALUE mFiddle
Definition: fiddle.c:3
size_t
long unsigned int size_t
Definition: rb_mjit_min_header-2.7.0.h:666
GENERIC2VALUE
#define GENERIC2VALUE(_type, _retval)
Definition: conversions.h:33
callback_args::cif
ffi_cif * cif
Definition: closure.c:61
VALUE2GENERIC
#define VALUE2GENERIC(_type, _src, _dst)
Definition: conversions.h:31
rb_scan_args
#define rb_scan_args(argc, argvp, fmt,...)
Definition: rb_mjit_min_header-2.7.0.h:6372
INT2FIX
#define INT2FIX(i)
Definition: ruby.h:263
FFI_STDCALL
@ FFI_STDCALL
Definition: ffitarget.h:110
fiddle.h
VALUE
unsigned long VALUE
Definition: ruby.h:102
rb_eArgError
VALUE rb_eArgError
Definition: error.c:923
rb_intern
#define rb_intern(str)
rb_ary_store
void rb_ary_store(VALUE ary, long idx, VALUE val)
Definition: array.c:1079
rb_fiddle_new_function
VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
Definition: function.c:67
rb_define_method
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1551
INT2NUM
#define INT2NUM(x)
Definition: ruby.h:1609
cFiddleFunction
VALUE cFiddleFunction
Definition: function.c:13
ptr
struct RIMemo * ptr
Definition: debug.c:74
rb_Integer
VALUE rb_Integer(VALUE)
Equivalent to Kernel#Integer in Ruby.
Definition: object.c:3106
nogvl_ffi_call_args::values
void ** values
Definition: function.c:153
NULL
#define NULL
Definition: _sdbm.c:101
PRIsVALUE
#define PRIsVALUE
Definition: ruby.h:166
fiddle_generic
Definition: conversions.h:6
ID2SYM
#define ID2SYM(x)
Definition: ruby.h:414
OBJ_FREEZE
#define OBJ_FREEZE(x)
Definition: ruby.h:1377
RARRAY_LENINT
#define RARRAY_LENINT(ary)
Definition: ruby.h:1071
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2669
nogvl_ffi_call_args::fn
void(* fn)(void)
Definition: function.c:152
ALLOCV_END
#define ALLOCV_END(v)
Definition: ruby.h:1750
rb_iv_get
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3294
i
uint32_t i
Definition: rb_mjit_min_header-2.7.0.h:5464
nogvl_ffi_call_args::retval
fiddle_generic retval
Definition: function.c:154
INT2FFI_TYPE
#define INT2FFI_TYPE(_type)
Definition: conversions.h:32
NUM2PTR
#define NUM2PTR(x)
Definition: conversions.h:37
rb_ary_subseq
#define rb_ary_subseq(ary, beg, len)
Definition: cparse.c:76
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:920
rb_thread_call_without_gvl
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition: ruby.h:1101
size
int size
Definition: encoding.c:58
rb_hash_foreach
void rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
Definition: hash.c:1461
rb_error_arity
MJIT_STATIC void rb_error_arity(int argc, int min, int max)
Definition: vm_insnhelper.c:387
FFI_DEFAULT_ABI
@ FFI_DEFAULT_ABI
Definition: ffitarget.h:38
ffi_raw_size
size_t ffi_raw_size(ffi_cif *cif)
Definition: raw_api.c:35
key
key
Definition: openssl_missing.h:181
RB_OBJ_STRING
#define RB_OBJ_STRING(obj)
Definition: function.c:6
CLASS_OF
#define CLASS_OF(v)
Definition: ruby.h:484
src
__inline__ const void *__restrict src
Definition: rb_mjit_min_header-2.7.0.h:2836
RARRAY_LEN
#define RARRAY_LEN(a)
Definition: ruby.h:1070
nogvl_ffi_call_args
Definition: function.c:150
rb_cObject
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:2010
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: ruby.h:1252
rb_funcall
#define rb_funcall(recv, mid, argc,...)
Definition: rb_mjit_min_header-2.7.0.h:6585
T_ARRAY
#define T_ARRAY
Definition: ruby.h:530
argv
char ** argv
Definition: ruby.c:223
ST_CONTINUE
@ ST_CONTINUE
Definition: st.h:99
klass
VALUE klass
Definition: rb_mjit_min_header-2.7.0.h:13254
types
enum imemo_type types
Definition: debug.c:72
Init_fiddle_function
void Init_fiddle_function(void)
Definition: function.c:225
NIL_P
#define NIL_P(v)
Definition: ruby.h:482
ffi_prep_cif
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
argc
int argc
Definition: ruby.c:222
PTR2NUM
#define PTR2NUM(x)
Definition: conversions.h:36
rb_define_const
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:2880
rb_data_type_struct
Definition: ruby.h:1148
xfree
#define xfree
Definition: defines.h:216
Check_Max_Args
#define Check_Max_Args(name, len)
Definition: function.c:17
nogvl_ffi_call_args::cif
ffi_cif * cif
Definition: function.c:151
errno
int errno
len
uint8_t len
Definition: escape.c:17
ffi_call
void ffi_call(ffi_cif *cif, void(*fn)(void), void *rvalue, void **avalue)
Definition: ffi.c:813
rb_class_new_instance
VALUE rb_class_new_instance(int, const VALUE *, VALUE)
Allocates and initializes an instance of klass.
Definition: object.c:1955
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:698
callback_args::args
void ** args
Definition: closure.c:63
NUM2INT
#define NUM2INT(x)
Definition: ruby.h:715
thread.h
ALLOCV
#define ALLOCV(v, n)
Definition: ruby.h:1748
function_data_type
const rb_data_type_t function_data_type
Definition: function.c:53
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:39
rb_iv_set
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3307
rb_const_get
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2387