fiddle 1.0.0.beta1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,8 @@
1
1
  #include <fiddle.h>
2
2
  #include <ruby/thread.h>
3
3
 
4
+ #include <stdbool.h>
5
+
4
6
  #ifdef PRIsVALUE
5
7
  # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
6
8
  # define RB_OBJ_STRING(obj) (obj)
@@ -19,21 +21,21 @@ VALUE cFiddleFunction;
19
21
  #define Check_Max_Args_Long(name, len) \
20
22
  Check_Max_Args_(name, len, "l")
21
23
  #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
- }
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)
30
32
 
31
33
  static void
32
34
  deallocate(void *p)
33
35
  {
34
- ffi_cif *ptr = p;
35
- if (ptr->arg_types) xfree(ptr->arg_types);
36
- xfree(ptr);
36
+ ffi_cif *cif = p;
37
+ if (cif->arg_types) xfree(cif->arg_types);
38
+ xfree(cif);
37
39
  }
38
40
 
39
41
  static size_t
@@ -87,60 +89,91 @@ parse_keyword_arg_i(VALUE key, VALUE value, VALUE self)
87
89
  return ST_CONTINUE;
88
90
  }
89
91
 
92
+ static VALUE
93
+ normalize_argument_types(const char *name,
94
+ VALUE arg_types,
95
+ bool *is_variadic)
96
+ {
97
+ VALUE normalized_arg_types;
98
+ int i;
99
+ int n_arg_types;
100
+ *is_variadic = false;
101
+
102
+ Check_Type(arg_types, T_ARRAY);
103
+ n_arg_types = RARRAY_LENINT(arg_types);
104
+ Check_Max_Args(name, n_arg_types);
105
+
106
+ normalized_arg_types = rb_ary_new_capa(n_arg_types);
107
+ for (i = 0; i < n_arg_types; i++) {
108
+ VALUE arg_type = RARRAY_AREF(arg_types, i);
109
+ int c_arg_type;
110
+ arg_type = rb_fiddle_type_ensure(arg_type);
111
+ c_arg_type = NUM2INT(arg_type);
112
+ if (c_arg_type == TYPE_VARIADIC) {
113
+ if (i != n_arg_types - 1) {
114
+ rb_raise(rb_eArgError,
115
+ "Fiddle::TYPE_VARIADIC must be the last argument type: "
116
+ "%"PRIsVALUE,
117
+ arg_types);
118
+ }
119
+ *is_variadic = true;
120
+ break;
121
+ }
122
+ else {
123
+ (void)INT2FFI_TYPE(c_arg_type); /* raise */
124
+ }
125
+ rb_ary_push(normalized_arg_types, INT2FIX(c_arg_type));
126
+ }
127
+
128
+ /* freeze to prevent inconsistency at calling #to_int later */
129
+ OBJ_FREEZE(normalized_arg_types);
130
+ return normalized_arg_types;
131
+ }
132
+
90
133
  static VALUE
91
134
  initialize(int argc, VALUE argv[], VALUE self)
92
135
  {
93
136
  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;
137
+ VALUE ptr, arg_types, ret_type, abi, kwds;
138
+ int c_ret_type;
139
+ bool is_variadic = false;
140
+ ffi_abi c_ffi_abi;
99
141
  void *cfunc;
100
142
 
101
- rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds);
143
+ rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwds);
144
+ rb_iv_set(self, "@closure", ptr);
145
+
102
146
  ptr = rb_Integer(ptr);
103
147
  cfunc = NUM2PTR(ptr);
104
148
  PTR2NUM(cfunc);
105
- nabi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi);
106
- abi = INT2FIX(nabi);
107
- i = NUM2INT(ret_type);
108
- rtype = INT2FFI_TYPE(i);
109
- ret_type = INT2FIX(i);
110
-
111
- Check_Type(args, T_ARRAY);
112
- len = RARRAY_LENINT(args);
113
- Check_Max_Args("args", len);
114
- ary = rb_ary_subseq(args, 0, len);
115
- for (i = 0; i < RARRAY_LEN(args); i++) {
116
- VALUE a = RARRAY_PTR(args)[i];
117
- int type = NUM2INT(a);
118
- (void)INT2FFI_TYPE(type); /* raise */
119
- if (INT2FIX(type) != a) rb_ary_store(ary, i, INT2FIX(type));
149
+ c_ffi_abi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi);
150
+ abi = INT2FIX(c_ffi_abi);
151
+ ret_type = rb_fiddle_type_ensure(ret_type);
152
+ c_ret_type = NUM2INT(ret_type);
153
+ (void)INT2FFI_TYPE(c_ret_type); /* raise */
154
+ ret_type = INT2FIX(c_ret_type);
155
+
156
+ arg_types = normalize_argument_types("argument types",
157
+ arg_types,
158
+ &is_variadic);
159
+ #ifndef HAVE_FFI_PREP_CIF_VAR
160
+ if (is_variadic) {
161
+ rb_raise(rb_eNotImpError,
162
+ "ffi_prep_cif_var() is required in libffi "
163
+ "for variadic arguments");
120
164
  }
121
- OBJ_FREEZE(ary);
165
+ #endif
122
166
 
123
167
  rb_iv_set(self, "@ptr", ptr);
124
- rb_iv_set(self, "@args", args);
168
+ rb_iv_set(self, "@argument_types", arg_types);
125
169
  rb_iv_set(self, "@return_type", ret_type);
126
170
  rb_iv_set(self, "@abi", abi);
171
+ rb_iv_set(self, "@is_variadic", is_variadic ? Qtrue : Qfalse);
127
172
 
128
173
  if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self);
129
174
 
130
175
  TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
131
-
132
- arg_types = xcalloc(len + 1, sizeof(ffi_type *));
133
-
134
- for (i = 0; i < RARRAY_LEN(args); i++) {
135
- int type = NUM2INT(RARRAY_AREF(args, i));
136
- arg_types[i] = INT2FFI_TYPE(type);
137
- }
138
- arg_types[len] = NULL;
139
-
140
- result = ffi_prep_cif(cif, nabi, len, rtype, arg_types);
141
-
142
- if (result)
143
- rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
176
+ cif->arg_types = NULL;
144
177
 
145
178
  return self;
146
179
  }
@@ -167,54 +200,160 @@ function_call(int argc, VALUE argv[], VALUE self)
167
200
  {
168
201
  struct nogvl_ffi_call_args args = { 0 };
169
202
  fiddle_generic *generic_args;
170
- VALUE cfunc, types, cPointer;
203
+ VALUE cfunc;
204
+ VALUE abi;
205
+ VALUE arg_types;
206
+ VALUE cPointer;
207
+ VALUE is_variadic;
208
+ int n_arg_types;
209
+ int n_fixed_args = 0;
210
+ int n_call_args = 0;
171
211
  int i;
212
+ int i_call;
213
+ VALUE converted_args = Qnil;
172
214
  VALUE alloc_buffer = 0;
173
215
 
174
216
  cfunc = rb_iv_get(self, "@ptr");
175
- types = rb_iv_get(self, "@args");
217
+ abi = rb_iv_get(self, "@abi");
218
+ arg_types = rb_iv_get(self, "@argument_types");
176
219
  cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
177
-
178
- Check_Max_Args("number of arguments", argc);
179
- if (argc != (i = RARRAY_LENINT(types))) {
180
- rb_error_arity(argc, i, i);
220
+ is_variadic = rb_iv_get(self, "@is_variadic");
221
+
222
+ n_arg_types = RARRAY_LENINT(arg_types);
223
+ n_fixed_args = n_arg_types;
224
+ if (RTEST(is_variadic)) {
225
+ if (argc < n_arg_types) {
226
+ rb_error_arity(argc, n_arg_types, UNLIMITED_ARGUMENTS);
227
+ }
228
+ if (((argc - n_arg_types) % 2) != 0) {
229
+ rb_raise(rb_eArgError,
230
+ "variadic arguments must be type and value pairs: "
231
+ "%"PRIsVALUE,
232
+ rb_ary_new_from_values(argc, argv));
233
+ }
234
+ n_call_args = n_arg_types + ((argc - n_arg_types) / 2);
181
235
  }
236
+ else {
237
+ if (argc != n_arg_types) {
238
+ rb_error_arity(argc, n_arg_types, n_arg_types);
239
+ }
240
+ n_call_args = n_arg_types;
241
+ }
242
+ Check_Max_Args("the number of arguments", n_call_args);
182
243
 
183
244
  TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif);
184
245
 
185
- if (rb_safe_level() >= 1) {
186
- for (i = 0; i < argc; i++) {
187
- VALUE src = argv[i];
188
- if (OBJ_TAINTED(src)) {
189
- rb_raise(rb_eSecurityError, "tainted parameter not allowed");
190
- }
191
- }
246
+ if (is_variadic && args.cif->arg_types) {
247
+ xfree(args.cif->arg_types);
248
+ args.cif->arg_types = NULL;
249
+ }
250
+
251
+ if (!args.cif->arg_types) {
252
+ VALUE fixed_arg_types = arg_types;
253
+ VALUE return_type;
254
+ int c_return_type;
255
+ ffi_type *ffi_return_type;
256
+ ffi_type **ffi_arg_types;
257
+ ffi_status result;
258
+
259
+ arg_types = rb_ary_dup(fixed_arg_types);
260
+ for (i = n_fixed_args; i < argc; i += 2) {
261
+ VALUE arg_type = argv[i];
262
+ int c_arg_type;
263
+ arg_type = rb_fiddle_type_ensure(arg_type);
264
+ c_arg_type = NUM2INT(arg_type);
265
+ (void)INT2FFI_TYPE(c_arg_type); /* raise */
266
+ rb_ary_push(arg_types, INT2FIX(c_arg_type));
267
+ }
268
+
269
+ return_type = rb_iv_get(self, "@return_type");
270
+ c_return_type = FIX2INT(return_type);
271
+ ffi_return_type = INT2FFI_TYPE(c_return_type);
272
+
273
+ ffi_arg_types = xcalloc(n_call_args + 1, sizeof(ffi_type *));
274
+ for (i_call = 0; i_call < n_call_args; i_call++) {
275
+ VALUE arg_type;
276
+ int c_arg_type;
277
+ arg_type = RARRAY_AREF(arg_types, i_call);
278
+ c_arg_type = FIX2INT(arg_type);
279
+ ffi_arg_types[i_call] = INT2FFI_TYPE(c_arg_type);
280
+ }
281
+ ffi_arg_types[i_call] = NULL;
282
+
283
+ if (is_variadic) {
284
+ #ifdef HAVE_FFI_PREP_CIF_VAR
285
+ result = ffi_prep_cif_var(args.cif,
286
+ FIX2INT(abi),
287
+ n_fixed_args,
288
+ n_call_args,
289
+ ffi_return_type,
290
+ ffi_arg_types);
291
+ #else
292
+ /* This code is never used because ffi_prep_cif_var()
293
+ * availability check is done in #initialize. */
294
+ result = FFI_BAD_TYPEDEF;
295
+ #endif
296
+ }
297
+ else {
298
+ result = ffi_prep_cif(args.cif,
299
+ FIX2INT(abi),
300
+ n_call_args,
301
+ ffi_return_type,
302
+ ffi_arg_types);
303
+ }
304
+ if (result != FFI_OK) {
305
+ xfree(ffi_arg_types);
306
+ args.cif->arg_types = NULL;
307
+ rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
308
+ }
192
309
  }
193
310
 
194
311
  generic_args = ALLOCV(alloc_buffer,
195
- (size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic));
312
+ sizeof(fiddle_generic) * n_call_args +
313
+ sizeof(void *) * (n_call_args + 1));
196
314
  args.values = (void **)((char *)generic_args +
197
- (size_t)argc * sizeof(fiddle_generic));
198
-
199
- for (i = 0; i < argc; i++) {
200
- VALUE type = RARRAY_AREF(types, i);
201
- VALUE src = argv[i];
202
- int argtype = FIX2INT(type);
203
-
204
- if (argtype == TYPE_VOIDP) {
205
- if(NIL_P(src)) {
206
- src = INT2FIX(0);
207
- } else if(cPointer != CLASS_OF(src)) {
208
- src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
209
- }
210
- src = rb_Integer(src);
211
- }
212
-
213
- VALUE2GENERIC(argtype, src, &generic_args[i]);
214
- args.values[i] = (void *)&generic_args[i];
315
+ sizeof(fiddle_generic) * n_call_args);
316
+
317
+ for (i = 0, i_call = 0;
318
+ i < argc && i_call < n_call_args;
319
+ i++, i_call++) {
320
+ VALUE arg_type;
321
+ int c_arg_type;
322
+ VALUE original_src;
323
+ VALUE src;
324
+ arg_type = RARRAY_AREF(arg_types, i_call);
325
+ c_arg_type = FIX2INT(arg_type);
326
+ if (i >= n_fixed_args) {
327
+ i++;
328
+ }
329
+ src = argv[i];
330
+
331
+ if (c_arg_type == TYPE_VOIDP) {
332
+ if (NIL_P(src)) {
333
+ src = INT2FIX(0);
334
+ }
335
+ else if (cPointer != CLASS_OF(src)) {
336
+ src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
337
+ if (NIL_P(converted_args)) {
338
+ converted_args = rb_ary_new();
339
+ }
340
+ rb_ary_push(converted_args, src);
341
+ }
342
+ src = rb_Integer(src);
343
+ }
344
+
345
+ original_src = src;
346
+ VALUE2GENERIC(c_arg_type, src, &generic_args[i_call]);
347
+ if (src != original_src) {
348
+ if (NIL_P(converted_args)) {
349
+ converted_args = rb_ary_new();
350
+ }
351
+ rb_ary_push(converted_args, src);
352
+ }
353
+ args.values[i_call] = (void *)&generic_args[i_call];
215
354
  }
216
- args.values[argc] = NULL;
217
- args.fn = NUM2PTR(cfunc);
355
+ args.values[i_call] = NULL;
356
+ args.fn = (void(*)(void))NUM2PTR(cfunc);
218
357
 
219
358
  (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
220
359
 
@@ -1,8 +1,6 @@
1
1
  #include <ruby.h>
2
2
  #include <fiddle.h>
3
3
 
4
- #define SafeStringValueCStr(v) (rb_check_safe_obj(rb_string_value(&v)), StringValueCStr(v))
5
-
6
4
  VALUE rb_cHandle;
7
5
 
8
6
  struct dl_handle {
@@ -76,14 +74,14 @@ rb_fiddle_handle_close(VALUE self)
76
74
  /* Check dlclose for successful return value */
77
75
  if(ret) {
78
76
  #if defined(HAVE_DLERROR)
79
- rb_raise(rb_eFiddleError, "%s", dlerror());
77
+ rb_raise(rb_eFiddleDLError, "%s", dlerror());
80
78
  #else
81
- rb_raise(rb_eFiddleError, "could not close handle");
79
+ rb_raise(rb_eFiddleDLError, "could not close handle");
82
80
  #endif
83
81
  }
84
82
  return INT2NUM(ret);
85
83
  }
86
- rb_raise(rb_eFiddleError, "dlclose() called too many times");
84
+ rb_raise(rb_eFiddleDLError, "dlclose() called too many times");
87
85
 
88
86
  UNREACHABLE;
89
87
  }
@@ -145,11 +143,11 @@ rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
145
143
  cflag = RTLD_LAZY | RTLD_GLOBAL;
146
144
  break;
147
145
  case 1:
148
- clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
146
+ clib = NIL_P(lib) ? NULL : StringValueCStr(lib);
149
147
  cflag = RTLD_LAZY | RTLD_GLOBAL;
150
148
  break;
151
149
  case 2:
152
- clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
150
+ clib = NIL_P(lib) ? NULL : StringValueCStr(lib);
153
151
  cflag = NUM2INT(flag);
154
152
  break;
155
153
  default:
@@ -179,12 +177,12 @@ rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
179
177
  ptr = dlopen(clib, cflag);
180
178
  #if defined(HAVE_DLERROR)
181
179
  if( !ptr && (err = dlerror()) ){
182
- rb_raise(rb_eFiddleError, "%s", err);
180
+ rb_raise(rb_eFiddleDLError, "%s", err);
183
181
  }
184
182
  #else
185
183
  if( !ptr ){
186
184
  err = dlerror();
187
- rb_raise(rb_eFiddleError, "%s", err);
185
+ rb_raise(rb_eFiddleDLError, "%s", err);
188
186
  }
189
187
  #endif
190
188
  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
@@ -280,7 +278,7 @@ rb_fiddle_handle_sym(VALUE self, VALUE sym)
280
278
 
281
279
  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
282
280
  if( ! fiddle_handle->open ){
283
- rb_raise(rb_eFiddleError, "closed handle");
281
+ rb_raise(rb_eFiddleDLError, "closed handle");
284
282
  }
285
283
 
286
284
  return fiddle_handle_sym(fiddle_handle->ptr, sym);
@@ -319,7 +317,7 @@ fiddle_handle_sym(void *handle, VALUE symbol)
319
317
  # define CHECK_DLERROR
320
318
  #endif
321
319
  void (*func)();
322
- const char *name = SafeStringValueCStr(symbol);
320
+ const char *name = StringValueCStr(symbol);
323
321
 
324
322
  #ifdef HAVE_DLERROR
325
323
  dlerror();
@@ -368,7 +366,7 @@ fiddle_handle_sym(void *handle, VALUE symbol)
368
366
  }
369
367
  #endif
370
368
  if( !func ){
371
- rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
369
+ rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
372
370
  }
373
371
 
374
372
  return PTR2NUM(func);
@@ -0,0 +1,123 @@
1
+ #include <fiddle.h>
2
+
3
+ VALUE rb_cPinned;
4
+ VALUE rb_eFiddleClearedReferenceError;
5
+
6
+ struct pinned_data {
7
+ VALUE ptr;
8
+ };
9
+
10
+ static void
11
+ pinned_mark(void *ptr)
12
+ {
13
+ struct pinned_data *data = (struct pinned_data*)ptr;
14
+ /* Ensure reference is pinned */
15
+ if (data->ptr) {
16
+ rb_gc_mark(data->ptr);
17
+ }
18
+ }
19
+
20
+ static size_t
21
+ pinned_memsize(const void *ptr)
22
+ {
23
+ return sizeof(struct pinned_data);
24
+ }
25
+
26
+ static const rb_data_type_t pinned_data_type = {
27
+ "fiddle/pinned",
28
+ {pinned_mark, xfree, pinned_memsize, },
29
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
30
+ };
31
+
32
+ static VALUE
33
+ allocate(VALUE klass)
34
+ {
35
+ struct pinned_data *data;
36
+ VALUE obj = TypedData_Make_Struct(klass, struct pinned_data, &pinned_data_type, data);
37
+ data->ptr = 0;
38
+ return obj;
39
+ }
40
+
41
+ /*
42
+ * call-seq:
43
+ * Fiddle::Pinned.new(object) => pinned_object
44
+ *
45
+ * Create a new pinned object reference. The Fiddle::Pinned instance will
46
+ * prevent the GC from moving +object+.
47
+ */
48
+ static VALUE
49
+ initialize(VALUE self, VALUE ref)
50
+ {
51
+ struct pinned_data *data;
52
+ TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
53
+ RB_OBJ_WRITE(self, &data->ptr, ref);
54
+ return self;
55
+ }
56
+
57
+ /*
58
+ * call-seq: ref
59
+ *
60
+ * Return the object that this pinned instance references.
61
+ */
62
+ static VALUE
63
+ ref(VALUE self)
64
+ {
65
+ struct pinned_data *data;
66
+ TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
67
+ if (data->ptr) {
68
+ return data->ptr;
69
+ } else {
70
+ rb_raise(rb_eFiddleClearedReferenceError, "`ref` called on a cleared object");
71
+ }
72
+ }
73
+
74
+ /*
75
+ * call-seq: clear
76
+ *
77
+ * Clear the reference to the object this is pinning.
78
+ */
79
+ static VALUE
80
+ clear(VALUE self)
81
+ {
82
+ struct pinned_data *data;
83
+ TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
84
+ data->ptr = 0;
85
+ return self;
86
+ }
87
+
88
+ /*
89
+ * call-seq: cleared?
90
+ *
91
+ * Returns true if the reference has been cleared, otherwise returns false.
92
+ */
93
+ static VALUE
94
+ cleared_p(VALUE self)
95
+ {
96
+ struct pinned_data *data;
97
+ TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
98
+ if (data->ptr) {
99
+ return Qfalse;
100
+ } else {
101
+ return Qtrue;
102
+ }
103
+ }
104
+
105
+ extern VALUE rb_eFiddleError;
106
+
107
+ void
108
+ Init_fiddle_pinned(void)
109
+ {
110
+ rb_cPinned = rb_define_class_under(mFiddle, "Pinned", rb_cObject);
111
+ rb_define_alloc_func(rb_cPinned, allocate);
112
+ rb_define_method(rb_cPinned, "initialize", initialize, 1);
113
+ rb_define_method(rb_cPinned, "ref", ref, 0);
114
+ rb_define_method(rb_cPinned, "clear", clear, 0);
115
+ rb_define_method(rb_cPinned, "cleared?", cleared_p, 0);
116
+
117
+ /*
118
+ * Document-class: Fiddle::ClearedReferenceError
119
+ *
120
+ * Cleared reference exception
121
+ */
122
+ rb_eFiddleClearedReferenceError = rb_define_class_under(mFiddle, "ClearedReferenceError", rb_eFiddleError);
123
+ }