fiddle 1.0.0.beta1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }