ffi 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ffi might be problematic. Click here for more details.

Files changed (62) hide show
  1. data/Rakefile +52 -29
  2. data/ext/AbstractMemory.c +72 -28
  3. data/ext/AutoPointer.c +54 -0
  4. data/ext/AutoPointer.h +18 -0
  5. data/ext/Buffer.c +21 -17
  6. data/ext/Callback.c +81 -43
  7. data/ext/Callback.h +1 -1
  8. data/ext/Invoker.c +465 -108
  9. data/ext/MemoryPointer.c +25 -90
  10. data/ext/NativeLibrary.c +90 -0
  11. data/ext/NativeLibrary.h +22 -0
  12. data/ext/Platform.c +21 -2
  13. data/ext/Pointer.c +107 -0
  14. data/ext/Pointer.h +21 -0
  15. data/ext/Types.c +16 -5
  16. data/ext/Types.h +3 -1
  17. data/ext/compat.h +14 -0
  18. data/ext/extconf.rb +13 -1
  19. data/ext/ffi.c +11 -1
  20. data/ext/ffi.mk +3 -3
  21. data/ext/libffi.darwin.mk +19 -8
  22. data/gen/Rakefile +12 -0
  23. data/lib/ffi/autopointer.rb +61 -0
  24. data/lib/ffi/errno.rb +8 -0
  25. data/lib/ffi/ffi.rb +38 -201
  26. data/lib/ffi/io.rb +7 -0
  27. data/lib/ffi/library.rb +116 -0
  28. data/lib/ffi/managedstruct.rb +55 -0
  29. data/lib/ffi/memorypointer.rb +3 -96
  30. data/lib/ffi/platform.rb +8 -5
  31. data/lib/ffi/pointer.rb +105 -0
  32. data/lib/ffi/struct.rb +97 -42
  33. data/lib/ffi/tools/const_generator.rb +177 -0
  34. data/lib/ffi/tools/generator.rb +58 -0
  35. data/lib/ffi/tools/generator_task.rb +35 -0
  36. data/lib/ffi/tools/struct_generator.rb +194 -0
  37. data/lib/ffi/tools/types_generator.rb +123 -0
  38. data/lib/ffi/types.rb +150 -0
  39. data/lib/ffi/variadic.rb +30 -0
  40. data/nbproject/Makefile-Default.mk +6 -3
  41. data/nbproject/Makefile-impl.mk +5 -5
  42. data/nbproject/Package-Default.bash +72 -0
  43. data/nbproject/configurations.xml +139 -25
  44. data/nbproject/private/configurations.xml +1 -1
  45. data/nbproject/project.xml +4 -0
  46. data/samples/gettimeofday.rb +6 -2
  47. data/samples/inotify.rb +59 -0
  48. data/samples/pty.rb +75 -0
  49. data/specs/buffer_spec.rb +64 -9
  50. data/specs/callback_spec.rb +308 -4
  51. data/specs/errno_spec.rb +13 -0
  52. data/specs/library_spec.rb +55 -0
  53. data/specs/managed_struct_spec.rb +40 -0
  54. data/specs/number_spec.rb +183 -0
  55. data/specs/pointer_spec.rb +126 -0
  56. data/specs/rbx/memory_pointer_spec.rb +7 -7
  57. data/specs/spec_helper.rb +7 -0
  58. data/specs/string_spec.rb +34 -0
  59. data/specs/struct_spec.rb +223 -0
  60. data/specs/typedef_spec.rb +48 -0
  61. data/specs/variadic_spec.rb +84 -0
  62. metadata +270 -237
@@ -36,7 +36,7 @@ typedef struct {
36
36
  VALUE rbProc;
37
37
  } NativeCallback;
38
38
 
39
- extern VALUE rb_FFI_Callback_class;
39
+ extern VALUE rb_FFI_CallbackInfo_class;
40
40
  extern VALUE rb_FFI_NativeCallback_new(VALUE, VALUE);
41
41
 
42
42
  #ifdef __cplusplus
@@ -2,21 +2,24 @@
2
2
  #include <stdio.h>
3
3
  #include <stdint.h>
4
4
  #include <dlfcn.h>
5
+ #include <errno.h>
5
6
  #include <ruby.h>
6
7
 
7
8
  #include <ffi.h>
8
-
9
+ #ifdef HAVE_NATIVETHREAD
10
+ # include <pthread.h>
11
+ #endif
9
12
  #include "rbffi.h"
10
13
  #include "compat.h"
11
14
 
12
15
  #include "AbstractMemory.h"
13
- #include "MemoryPointer.h"
16
+ #include "Pointer.h"
14
17
  #include "Platform.h"
15
18
  #include "Callback.h"
16
19
  #include "Types.h"
17
20
 
18
21
  typedef struct Invoker {
19
- void* dlhandle;
22
+ VALUE library;
20
23
  void* function;
21
24
  ffi_cif cif;
22
25
  int paramCount;
@@ -26,203 +29,418 @@ typedef struct Invoker {
26
29
  VALUE callbackArray;
27
30
  int callbackCount;
28
31
  VALUE* callbackParameters;
32
+ ffi_abi abi;
29
33
  } Invoker;
30
-
31
- static VALUE invoker_new(VALUE self, VALUE libname, VALUE cname, VALUE parameterTypes,
34
+ typedef struct ThreadData {
35
+ int td_errno;
36
+ } ThreadData;
37
+ static VALUE invoker_new(VALUE self, VALUE library, VALUE function, VALUE parameterTypes,
32
38
  VALUE returnType, VALUE convention);
33
39
  static void invoker_mark(Invoker *);
34
40
  static void invoker_free(Invoker *);
35
41
  static VALUE invoker_call(int argc, VALUE* argv, VALUE self);
42
+ static VALUE invoker_call0(VALUE self);
43
+ static VALUE invoker_arity(VALUE self);
36
44
  static void* callback_param(VALUE proc, VALUE cbinfo);
37
- static VALUE classInvoker = Qnil;
38
- static ID cbTableID;
45
+ static inline ThreadData* thread_data_get(void);
46
+ static VALUE classInvoker = Qnil, classVariadicInvoker = Qnil;
47
+ static ID cbTableID, to_ptr;
48
+
49
+ #ifdef HAVE_NATIVETHREAD
50
+ static pthread_key_t threadDataKey;
51
+ #endif
52
+
53
+ #define threadData (thread_data_get())
54
+
55
+ #if defined(__i386__)
56
+ # define USE_RAW
57
+ #endif
58
+
59
+ #ifdef USE_RAW
60
+ # ifndef __i386__
61
+ # error "RAW argument packing only supported on i386"
62
+ # endif
63
+
64
+ #define INT8_SIZE (sizeof(char))
65
+ #define INT16_SIZE (sizeof(short))
66
+ #define INT32_SIZE (sizeof(int))
67
+ #define INT64_SIZE (sizeof(long long))
68
+ #define FLOAT32_SIZE (sizeof(float))
69
+ #define FLOAT64_SIZE (sizeof(double))
70
+ #define ADDRESS_SIZE (sizeof(void *))
71
+ #define INT8_ADJ (4)
72
+ #define INT16_ADJ (4)
73
+ #define INT32_ADJ (4)
74
+ #define INT64_ADJ (8)
75
+ #define FLOAT32_ADJ (4)
76
+ #define FLOAT64_ADJ (8)
77
+ #define ADDRESS_ADJ (sizeof(void *))
78
+
79
+ #endif /* USE_RAW */
80
+
81
+ #ifdef USE_RAW
82
+ # define ADJ(p, a) ((p) = (FFIStorage*) (((caddr_t) p) + a##_ADJ))
83
+ #else
84
+ # define ADJ(p, a) (++(p))
85
+ #endif
86
+
39
87
 
40
88
  static VALUE
41
- invoker_new(VALUE self, VALUE libname, VALUE cname, VALUE parameterTypes,
89
+ invoker_new(VALUE klass, VALUE library, VALUE function, VALUE parameterTypes,
42
90
  VALUE returnType, VALUE convention)
43
91
  {
44
92
  Invoker* invoker = NULL;
45
93
  ffi_type* ffiReturnType;
46
94
  ffi_abi abi;
47
95
  ffi_status ffiStatus;
48
- const char* errmsg = "Failed to create invoker";
96
+ VALUE retval = Qnil;
49
97
  int i;
50
98
 
51
- Check_Type(cname, T_STRING);
52
99
  Check_Type(parameterTypes, T_ARRAY);
53
100
  Check_Type(returnType, T_FIXNUM);
54
101
  Check_Type(convention, T_STRING);
102
+ Check_Type(library, T_DATA);
103
+ Check_Type(function, T_DATA);
55
104
 
56
- invoker = ALLOC(Invoker);
57
- MEMZERO(invoker, Invoker, 1);
58
-
105
+ retval = Data_Make_Struct(klass, Invoker, invoker_mark, invoker_free, invoker);
106
+ invoker->library = library;
107
+ invoker->function = ((AbstractMemory *) DATA_PTR(function))->address;
59
108
  invoker->paramCount = RARRAY_LEN(parameterTypes);
60
109
  invoker->paramTypes = ALLOC_N(NativeType, invoker->paramCount);
61
110
  invoker->ffiParamTypes = ALLOC_N(ffi_type *, invoker->paramCount);
62
111
 
63
112
  for (i = 0; i < invoker->paramCount; ++i) {
64
113
  VALUE entry = rb_ary_entry(parameterTypes, i);
65
- if (rb_obj_is_kind_of(entry, rb_FFI_Callback_class)) {
114
+ if (rb_obj_is_kind_of(entry, rb_FFI_CallbackInfo_class)) {
66
115
  invoker->callbackParameters = REALLOC_N(invoker->callbackParameters, VALUE,
67
116
  invoker->callbackCount + 1);
68
117
  invoker->callbackParameters[invoker->callbackCount++] = entry;
69
118
  invoker->paramTypes[i] = CALLBACK;
70
- invoker->ffiParamTypes[i] = &ffi_type_pointer;
119
+ invoker->ffiParamTypes[i] = &ffi_type_pointer;
71
120
  } else {
72
121
  int paramType = FIX2INT(entry);
73
122
  invoker->paramTypes[i] = paramType;
74
123
  invoker->ffiParamTypes[i] = rb_FFI_NativeTypeToFFI(paramType);
75
124
  }
76
125
  if (invoker->ffiParamTypes[i] == NULL) {
77
- errmsg = "Invalid parameter type";
78
- goto error;
126
+ rb_raise(rb_eArgError, "Invalid parameter type");
79
127
  }
80
128
  }
81
129
  invoker->returnType = FIX2INT(returnType);
82
130
  ffiReturnType = rb_FFI_NativeTypeToFFI(invoker->returnType);
83
131
  if (ffiReturnType == NULL) {
84
- errmsg = "Invalid return type";
85
- goto error;
132
+ rb_raise(rb_eArgError, "Invalid return type");
86
133
  }
87
134
  #ifdef _WIN32
88
- abi = strcmp(StringValuePtr(convention), "stdcall") == 0 ? FFI_STDCALL : FFI_DEFAULT_ABI;
135
+ abi = strcmp(StringValueCPtr(convention), "stdcall") == 0 ? FFI_STDCALL : FFI_DEFAULT_ABI;
89
136
  #else
90
137
  abi = FFI_DEFAULT_ABI;
91
138
  #endif
92
139
  ffiStatus = ffi_prep_cif(&invoker->cif, abi, invoker->paramCount,
93
140
  ffiReturnType, invoker->ffiParamTypes);
94
- if (ffiStatus != FFI_OK) {
95
- errmsg = "ffi_prep_cif failed";
96
- goto error;
141
+ switch (ffiStatus) {
142
+ case FFI_BAD_ABI:
143
+ rb_raise(rb_eArgError, "Invalid ABI specified");
144
+ case FFI_BAD_TYPEDEF:
145
+ rb_raise(rb_eArgError, "Invalid argument type specified");
146
+ case FFI_OK:
147
+ break;
148
+ default:
149
+ rb_raise(rb_eArgError, "Unknown FFI error");
97
150
  }
151
+
152
+ return retval;
153
+ }
154
+
155
+ static VALUE
156
+ variadic_invoker_new(VALUE klass, VALUE library, VALUE function, VALUE returnType, VALUE convention)
157
+ {
158
+ Invoker* invoker = NULL;
159
+ VALUE retval = Qnil;
98
160
 
99
- invoker->dlhandle = dlopen(libname != Qnil ? StringValuePtr(libname) : NULL, RTLD_LAZY);
100
- if (invoker->dlhandle == NULL) {
101
- errmsg = "No such library";
102
- goto error;
161
+ Check_Type(returnType, T_FIXNUM);
162
+ Check_Type(convention, T_STRING);
163
+ Check_Type(library, T_DATA);
164
+ Check_Type(function, T_DATA);
165
+
166
+ retval = Data_Make_Struct(klass, Invoker, invoker_mark, invoker_free, invoker);
167
+ invoker->library = library;
168
+ invoker->function = ((AbstractMemory *) DATA_PTR(function))->address;
169
+ #ifdef _WIN32
170
+ invoker->abi = strcmp(StringValueCPtr(convention), "stdcall") == 0 ? FFI_STDCALL : FFI_DEFAULT_ABI;
171
+ #else
172
+ invoker->abi = FFI_DEFAULT_ABI;
173
+ #endif
174
+ invoker->returnType = FIX2INT(returnType);
175
+ invoker->paramCount = -1;
176
+ return retval;
177
+ }
178
+
179
+ typedef union {
180
+ signed long i;
181
+ unsigned long u;
182
+ signed long long i64;
183
+ unsigned long long u64;
184
+ void* ptr;
185
+ float f32;
186
+ double f64;
187
+ } FFIStorage;
188
+
189
+ static inline int
190
+ getSignedInt(VALUE value, int type, int minValue, int maxValue, const char* typeName)
191
+ {
192
+ int i;
193
+ if (type != T_FIXNUM && type != T_BIGNUM) {
194
+ rb_raise(rb_eTypeError, "Expected an Integer parameter");
103
195
  }
104
- invoker->function = dlsym(invoker->dlhandle, StringValuePtr(cname));
105
- if (invoker->function == NULL) {
106
- errmsg = "Could not locate function within library";
107
- goto error;
196
+ i = NUM2INT(value);
197
+ if (i < minValue || i > maxValue) {
198
+ rb_raise(rb_eRangeError, "Value %d outside %s range", i, typeName);
108
199
  }
109
- return Data_Wrap_Struct(classInvoker, invoker_mark, invoker_free, invoker);
110
- error:
111
- if (invoker != NULL) {
112
- if (invoker->dlhandle != NULL) {
113
- dlclose(invoker->dlhandle);
114
- }
115
- if (invoker->paramTypes != NULL) {
116
- xfree(invoker->paramTypes);
117
- }
118
- if (invoker->ffiParamTypes != NULL) {
119
- xfree(invoker->ffiParamTypes);
120
- }
121
- xfree(invoker);
200
+ return i;
201
+ }
202
+
203
+ static inline int
204
+ getUnsignedInt(VALUE value, int type, int maxValue, const char* typeName)
205
+ {
206
+ int i;
207
+ if (type != T_FIXNUM && type != T_BIGNUM) {
208
+ rb_raise(rb_eTypeError, "Expected an Integer parameter");
209
+ }
210
+ i = NUM2INT(value);
211
+ if (i < 0 || i > maxValue) {
212
+ rb_raise(rb_eRangeError, "Value %d outside %s range", i, typeName);
122
213
  }
123
- rb_raise(rb_eRuntimeError, errmsg);
214
+ return i;
124
215
  }
125
216
 
126
- static VALUE
127
- invoker_call(int argc, VALUE* argv, VALUE self)
217
+ /* Special handling/checking for unsigned 32 bit integers */
218
+ static inline unsigned int
219
+ getUnsignedInt32(VALUE value, int type)
220
+ {
221
+ long long i;
222
+ if (type != T_FIXNUM && type != T_BIGNUM) {
223
+ rb_raise(rb_eTypeError, "Expected an Integer parameter");
224
+ }
225
+ i = NUM2LL(value);
226
+ if (i < 0L || i > 0xffffffffL) {
227
+ rb_raise(rb_eRangeError, "Value %lld outside unsigned int range", i);
228
+ }
229
+ return (unsigned int) i;
230
+ }
231
+
232
+ static void
233
+ ffi_arg_setup(const Invoker* invoker, int argc, VALUE* argv, NativeType* paramTypes,
234
+ FFIStorage* paramStorage, void** ffiValues)
128
235
  {
129
- Invoker* invoker;
130
- union {
131
- signed long i;
132
- unsigned long u;
133
- signed long long i64;
134
- unsigned long long u64;
135
- void* ptr;
136
- float f32;
137
- double f64;
138
- } params[MAX_PARAMETERS], retval;
139
- void* ffiValues[MAX_PARAMETERS];
140
236
  VALUE callbackProc = Qnil;
141
- int i, argidx, cbidx;
237
+ FFIStorage* param = &paramStorage[0];
238
+ int i, argidx, cbidx, argCount;
142
239
 
143
- Data_Get_Struct(self, Invoker, invoker);
144
- if (argc < invoker->paramCount && invoker->callbackCount == 1 && rb_block_given_p()) {
145
- callbackProc = rb_block_proc();
146
- } else if (argc != invoker->paramCount) {
147
- rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, invoker->paramCount);
240
+ if (invoker->paramCount != -1 && invoker->paramCount != argc) {
241
+ if (argc == (invoker->paramCount - 1) && invoker->callbackCount == 1 && rb_block_given_p()) {
242
+ callbackProc = rb_block_proc();
243
+ } else {
244
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, invoker->paramCount);
245
+ }
148
246
  }
149
-
150
- for (i = 0, argidx = 0, cbidx = 0; i < invoker->paramCount; ++i) {
151
- switch (invoker->paramTypes[i]) {
247
+ argCount = invoker->paramCount != -1 ? invoker->paramCount : argc;
248
+ for (i = 0, argidx = 0, cbidx = 0; i < argCount; ++i) {
249
+ int type = argidx < argc ? TYPE(argv[argidx]) : T_NONE;
250
+ ffiValues[i] = param;
251
+
252
+ switch (paramTypes[i]) {
152
253
  case INT8:
254
+ param->i = getSignedInt(argv[argidx++], type, -128, 127, "char");
255
+ ADJ(param, INT8);
256
+ break;
153
257
  case INT16:
258
+ param->i = getSignedInt(argv[argidx++], type, -0x8000, 0x7fff, "short");
259
+ ADJ(param, INT16);
260
+ break;
154
261
  case INT32:
155
- Check_Type(argv[argidx], T_FIXNUM);
156
- params[i].i = NUM2INT(argv[argidx]);
157
- ++argidx;
262
+ param->i = getSignedInt(argv[argidx++], type, -0x80000000, 0x7fffffff, "int");
263
+ ADJ(param, INT32);
158
264
  break;
159
265
  case UINT8:
266
+ param->u = getUnsignedInt(argv[argidx++], type, 0xff, "unsigned char");
267
+ ADJ(param, INT8);
268
+ break;
160
269
  case UINT16:
270
+ param->u = getUnsignedInt(argv[argidx++], type, 0xffff, "unsigned short");
271
+ ADJ(param, INT16);
272
+ break;
161
273
  case UINT32:
162
- Check_Type(argv[argidx], T_FIXNUM);
163
- params[i].u = NUM2UINT(argv[argidx]);
164
- ++argidx;
274
+ /* Special handling/checking for unsigned 32 bit integers */
275
+ param->u = getUnsignedInt32(argv[argidx++], type);
276
+ ADJ(param, INT32);
165
277
  break;
166
278
  case INT64:
167
- Check_Type(argv[argidx], T_FIXNUM);
168
- params[i].i64 = NUM2LL(argv[argidx]);
279
+ if (type != T_FIXNUM && type != T_BIGNUM) {
280
+ rb_raise(rb_eTypeError, "Expected an Integer parameter");
281
+ }
282
+ param->i64 = NUM2LL(argv[argidx]);
283
+ ADJ(param, INT64);
169
284
  ++argidx;
170
285
  break;
171
286
  case UINT64:
172
- Check_Type(argv[argidx], T_FIXNUM);
173
- params[i].i64 = NUM2ULL(argv[argidx]);
287
+ if (type != T_FIXNUM && type != T_BIGNUM) {
288
+ rb_raise(rb_eTypeError, "Expected an Integer parameter");
289
+ }
290
+ param->i64 = NUM2ULL(argv[argidx]);
291
+ ADJ(param, INT64);
174
292
  ++argidx;
175
293
  break;
176
294
  case FLOAT32:
177
- if (TYPE(argv[argidx]) != T_FLOAT && TYPE(argv[argidx]) != T_FIXNUM) {
178
- Check_Type(argv[argidx], T_FLOAT);
295
+ if (type != T_FLOAT && type != T_FIXNUM) {
296
+ rb_raise(rb_eTypeError, "Expected a Float parameter");
179
297
  }
180
- params[i].f32 = (float) NUM2DBL(argv[argidx]);
298
+ param->f32 = (float) NUM2DBL(argv[argidx]);
299
+ ADJ(param, FLOAT32);
181
300
  ++argidx;
182
301
  break;
183
302
  case FLOAT64:
184
- if (TYPE(argv[argidx]) != T_FLOAT && TYPE(argv[argidx]) != T_FIXNUM) {
185
- Check_Type(argv[argidx], T_FLOAT);
303
+ if (type != T_FLOAT && type != T_FIXNUM) {
304
+ rb_raise(rb_eTypeError, "Expected a Float parameter");
186
305
  }
187
- params[i].f64 = NUM2DBL(argv[argidx]);
306
+ param->f64 = NUM2DBL(argv[argidx]);
307
+ ADJ(param, FLOAT64);
188
308
  ++argidx;
189
309
  break;
190
310
  case STRING:
191
- Check_Type(argv[argidx], T_STRING);
192
- params[i].ptr = StringValuePtr(argv[argidx]);
311
+ if (type == T_STRING) {
312
+ if (rb_safe_level() >= 1 && OBJ_TAINTED(argv[argidx])) {
313
+ rb_raise(rb_eSecurityError, "Unsafe string parameter");
314
+ }
315
+ param->ptr = StringValuePtr(argv[argidx]);
316
+ } else if (type == T_NIL) {
317
+ param->ptr = NULL;
318
+ } else {
319
+ rb_raise(rb_eArgError, "Invalid String value");
320
+ }
321
+ ADJ(param, ADDRESS);
193
322
  ++argidx;
194
323
  break;
195
324
  case POINTER:
196
- if (rb_obj_is_kind_of(argv[argidx], rb_FFI_AbstractMemory_class)) {
197
- params[i].ptr = ((AbstractMemory *) DATA_PTR(argv[argidx]))->address;
198
- } else if (TYPE(argv[argidx]) == T_STRING) {
199
- params[i].ptr = StringValuePtr(argv[argidx]);
200
- } else if (TYPE(argv[argidx] == T_NIL)) {
201
- params[i].ptr = NULL;
202
- } else if (TYPE(argv[argidx] == T_FIXNUM)) {
203
- params[i].ptr = (void *) (uintptr_t) FIX2INT(argv[argidx]);
204
- } else if (TYPE(argv[argidx] == T_BIGNUM)) {
205
- params[i].ptr = (void *) (uintptr_t) NUM2ULL(argv[argidx]);
325
+ case BUFFER_IN:
326
+ case BUFFER_OUT:
327
+ case BUFFER_INOUT:
328
+ if (rb_obj_is_kind_of(argv[argidx], rb_FFI_AbstractMemory_class) && type == T_DATA) {
329
+ param->ptr = ((AbstractMemory *) DATA_PTR(argv[argidx]))->address;
330
+ } else if (type == T_STRING) {
331
+ if (rb_safe_level() >= 1 && OBJ_TAINTED(argv[argidx])) {
332
+ rb_raise(rb_eSecurityError, "Unsafe string parameter");
333
+ }
334
+ param->ptr = StringValuePtr(argv[argidx]);
335
+ } else if (type == T_NIL) {
336
+ param->ptr = NULL;
337
+ } else if (rb_respond_to(argv[argidx], to_ptr)) {
338
+ VALUE ptr = rb_funcall2(argv[argidx], to_ptr, 0, NULL);
339
+ if (rb_obj_is_kind_of(ptr, rb_FFI_Pointer_class) && TYPE(ptr) == T_DATA) {
340
+ param->ptr = ((AbstractMemory *) DATA_PTR(ptr))->address;
341
+ } else {
342
+ rb_raise(rb_eArgError, "to_ptr returned an invalid pointer");
343
+ }
344
+
206
345
  } else {
207
346
  rb_raise(rb_eArgError, ":pointer argument is not a valid pointer");
208
347
  }
348
+ ADJ(param, ADDRESS);
209
349
  ++argidx;
210
350
  break;
211
351
  case CALLBACK:
212
352
  if (callbackProc != Qnil) {
213
- params[i].ptr = callback_param(callbackProc, invoker->callbackParameters[cbidx++]);
353
+ param->ptr = callback_param(callbackProc, invoker->callbackParameters[cbidx++]);
214
354
  } else {
215
- params[i].ptr = callback_param(argv[argidx], invoker->callbackParameters[cbidx++]);
355
+ param->ptr = callback_param(argv[argidx], invoker->callbackParameters[cbidx++]);
216
356
  ++argidx;
217
- }
357
+ }
358
+ ADJ(param, ADDRESS);
218
359
  break;
219
360
  default:
220
- rb_raise(rb_eArgError, "Invalid parameter type: %d", invoker->paramTypes[i]);
361
+ rb_raise(rb_eArgError, "Invalid parameter type: %d", paramTypes[i]);
221
362
  }
222
- ffiValues[i] = &params[i];
223
363
  }
224
- ffi_call(&invoker->cif, FFI_FN(invoker->function), &retval, ffiValues);
225
- return rb_FFI_NativeValueToRuby(invoker->returnType, &retval);
364
+ }
365
+ static inline VALUE
366
+ ffi_invoke(ffi_cif* cif, void* function, NativeType returnType, void** ffiValues)
367
+ {
368
+ FFIStorage retval;
369
+ #ifdef USE_RAW
370
+ ffi_raw_call(cif, FFI_FN(function), &retval, (ffi_raw *) ffiValues[0]);
371
+ #else
372
+ ffi_call(cif, FFI_FN(function), &retval, ffiValues);
373
+ #endif
374
+ threadData->td_errno = errno;
375
+ return rb_FFI_NativeValueToRuby(returnType, &retval);
376
+ }
377
+ static VALUE
378
+ invoker_call(int argc, VALUE* argv, VALUE self)
379
+ {
380
+ Invoker* invoker;
381
+ FFIStorage params[MAX_PARAMETERS];
382
+ void* ffiValues[MAX_PARAMETERS];
383
+
384
+ Data_Get_Struct(self, Invoker, invoker);
385
+ ffi_arg_setup(invoker, argc, argv, invoker->paramTypes, params, ffiValues);
386
+ return ffi_invoke(&invoker->cif, invoker->function, invoker->returnType, ffiValues);
387
+ }
388
+
389
+ static VALUE
390
+ invoker_call0(VALUE self)
391
+ {
392
+ Invoker* invoker;
393
+ FFIStorage arg0;
394
+ void* ffiValues[] = { &arg0 };
395
+
396
+ Data_Get_Struct(self, Invoker, invoker);
397
+ return ffi_invoke(&invoker->cif, invoker->function, invoker->returnType, ffiValues);
398
+ }
399
+
400
+ static VALUE
401
+ invoker_call1(VALUE self, VALUE arg1)
402
+ {
403
+ Invoker* invoker;
404
+ void* ffiValues[1];
405
+ FFIStorage params[1];
406
+
407
+ Data_Get_Struct(self, Invoker, invoker);
408
+ ffi_arg_setup(invoker, 1, &arg1, invoker->paramTypes, params, ffiValues);
409
+ return ffi_invoke(&invoker->cif, invoker->function, invoker->returnType, ffiValues);
410
+ }
411
+
412
+ static VALUE
413
+ invoker_call2(VALUE self, VALUE arg1, VALUE arg2)
414
+ {
415
+ Invoker* invoker;
416
+ void* ffiValues[2];
417
+ FFIStorage params[2];
418
+ VALUE argv[] = { arg1, arg2 };
419
+
420
+ Data_Get_Struct(self, Invoker, invoker);
421
+ ffi_arg_setup(invoker, 2, argv, invoker->paramTypes, params, ffiValues);
422
+ return ffi_invoke(&invoker->cif, invoker->function, invoker->returnType, ffiValues);
423
+ }
424
+
425
+ static VALUE
426
+ invoker_call3(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3)
427
+ {
428
+ Invoker* invoker;
429
+ void* ffiValues[3];
430
+ FFIStorage params[3];
431
+ VALUE argv[] = { arg1, arg2, arg3 };
432
+
433
+ Data_Get_Struct(self, Invoker, invoker);
434
+ ffi_arg_setup(invoker, 3, argv, invoker->paramTypes, params, ffiValues);
435
+ return ffi_invoke(&invoker->cif, invoker->function, invoker->returnType, ffiValues);
436
+ }
437
+
438
+ static VALUE
439
+ invoker_arity(VALUE self)
440
+ {
441
+ Invoker* invoker;
442
+ Data_Get_Struct(self, Invoker, invoker);
443
+ return INT2FIX(invoker->paramCount);
226
444
  }
227
445
 
228
446
  static void
@@ -231,23 +449,106 @@ invoker_mark(Invoker *invoker)
231
449
  if (invoker->callbackCount > 0) {
232
450
  rb_gc_mark_locations(&invoker->callbackParameters[0], &invoker->callbackParameters[invoker->callbackCount]);
233
451
  }
452
+ if (invoker->library != Qnil) {
453
+ rb_gc_mark(invoker->library);
454
+ }
234
455
  }
235
456
 
236
457
  static void
237
458
  invoker_free(Invoker *invoker)
238
459
  {
239
- xfree(invoker->callbackParameters);
240
- xfree(invoker->paramTypes);
241
- xfree(invoker->ffiParamTypes);
242
- dlclose(invoker->dlhandle);
243
- xfree(invoker);
460
+ if (invoker != NULL) {
461
+ if (invoker->paramTypes != NULL) {
462
+ xfree(invoker->paramTypes);
463
+ invoker->paramTypes = NULL;
464
+ }
465
+ if (invoker->ffiParamTypes != NULL) {
466
+ xfree(invoker->ffiParamTypes);
467
+ invoker->ffiParamTypes = NULL;
468
+ }
469
+ if (invoker->callbackParameters != NULL) {
470
+ xfree(invoker->callbackParameters);
471
+ invoker->callbackParameters = NULL;
472
+ }
473
+ xfree(invoker);
474
+ }
475
+ }
476
+
477
+ static VALUE
478
+ variadic_invoker_call(VALUE self, VALUE parameterTypes, VALUE parameterValues)
479
+ {
480
+ Invoker* invoker;
481
+ FFIStorage* params;
482
+ ffi_cif cif;
483
+ void** ffiValues;
484
+ ffi_type** ffiParamTypes;
485
+ ffi_type* ffiReturnType;
486
+ NativeType* paramTypes;
487
+ VALUE* argv;
488
+ int paramCount = 0, i;
489
+ ffi_status ffiStatus;
490
+
491
+ Check_Type(parameterTypes, T_ARRAY);
492
+ Check_Type(parameterValues, T_ARRAY);
493
+
494
+ Data_Get_Struct(self, Invoker, invoker);
495
+ paramCount = RARRAY_LEN(parameterTypes);
496
+ paramTypes = ALLOCA_N(NativeType, paramCount);
497
+ ffiParamTypes = ALLOCA_N(ffi_type *, paramCount);
498
+ params = ALLOCA_N(FFIStorage, paramCount);
499
+ ffiValues = ALLOCA_N(void*, paramCount);
500
+ argv = ALLOCA_N(VALUE, paramCount);
501
+
502
+ for (i = 0; i < paramCount; ++i) {
503
+ VALUE entry = rb_ary_entry(parameterTypes, i);
504
+ int paramType = FIX2INT(entry);
505
+ switch (paramType) {
506
+ case INT8:
507
+ case INT16:
508
+ case INT32:
509
+ paramType = INT32;
510
+ break;
511
+ case UINT8:
512
+ case UINT16:
513
+ case UINT32:
514
+ paramType = UINT32;
515
+ break;
516
+ case FLOAT32:
517
+ paramType = FLOAT64;
518
+ break;
519
+ }
520
+ paramTypes[i] = paramType;
521
+ ffiParamTypes[i] = rb_FFI_NativeTypeToFFI(paramType);
522
+ if (ffiParamTypes[i] == NULL) {
523
+ rb_raise(rb_eArgError, "Invalid parameter type #%x", paramType);
524
+ }
525
+ argv[i] = rb_ary_entry(parameterValues, i);
526
+ }
527
+
528
+ ffiReturnType = rb_FFI_NativeTypeToFFI(invoker->returnType);
529
+ if (ffiReturnType == NULL) {
530
+ rb_raise(rb_eArgError, "Invalid return type");
531
+ }
532
+ ffiStatus = ffi_prep_cif(&cif, invoker->abi, paramCount, ffiReturnType, ffiParamTypes);
533
+ switch (ffiStatus) {
534
+ case FFI_BAD_ABI:
535
+ rb_raise(rb_eArgError, "Invalid ABI specified");
536
+ case FFI_BAD_TYPEDEF:
537
+ rb_raise(rb_eArgError, "Invalid argument type specified");
538
+ case FFI_OK:
539
+ break;
540
+ default:
541
+ rb_raise(rb_eArgError, "Unknown FFI error");
542
+ }
543
+ ffi_arg_setup(invoker, paramCount, argv, paramTypes, params, ffiValues);
544
+ return ffi_invoke(&cif, invoker->function, invoker->returnType, ffiValues);
244
545
  }
245
546
 
246
547
  static void*
247
548
  callback_param(VALUE proc, VALUE cbInfo)
248
549
  {
249
550
  VALUE callback;
250
- VALUE cbTable = rb_ivar_get(proc, cbTableID);
551
+ VALUE cbTable = RTEST(rb_ivar_defined(proc, cbTableID)) ? rb_ivar_get(proc, cbTableID) : Qnil;
251
552
  if (!cbTable || cbTable == Qnil) {
252
553
  cbTable = rb_hash_new();
253
554
  rb_ivar_set(proc, cbTableID, cbTable);
@@ -260,12 +561,68 @@ callback_param(VALUE proc, VALUE cbInfo)
260
561
  rb_hash_aset(cbTable, cbInfo, callback);
261
562
  return ((NativeCallback *) DATA_PTR(callback))->code;
262
563
  }
564
+ #ifdef HAVE_NATIVETHREAD
565
+ static ThreadData*
566
+ thread_data_init()
567
+ {
568
+ ThreadData* td = ALLOC_N(ThreadData, 1);
569
+ memset(td, 0, sizeof(*td));
570
+ pthread_setspecific(threadDataKey, td);
571
+ return td;
572
+ }
573
+ static inline ThreadData*
574
+ thread_data_get()
575
+ {
576
+ ThreadData* td = pthread_getspecific(threadDataKey);
577
+ return td != NULL ? td : thread_data_init();
578
+ }
579
+
580
+ static void
581
+ thread_data_free(void *ptr)
582
+ {
583
+ xfree(ptr);
584
+ }
585
+ #else /* !HAVE_NATIVETHREAD */
586
+ static ThreadData td0;
587
+ static inline ThreadData*
588
+ thread_data_get()
589
+ {
590
+ return &td0;
591
+ }
592
+ #endif
593
+ static VALUE
594
+ get_last_error(VALUE self)
595
+ {
596
+ return INT2NUM(threadData->td_errno);
597
+ }
598
+ static VALUE
599
+ set_last_error(VALUE self, VALUE error)
600
+ {
601
+ return Qnil;
602
+ }
603
+
263
604
  void
264
605
  rb_FFI_Invoker_Init()
265
606
  {
266
607
  VALUE moduleFFI = rb_define_module("FFI");
608
+ VALUE moduleError = rb_define_module_under(moduleFFI, "LastError");
267
609
  classInvoker = rb_define_class_under(moduleFFI, "Invoker", rb_cObject);
268
610
  rb_define_singleton_method(classInvoker, "new", invoker_new, 5);
269
611
  rb_define_method(classInvoker, "call", invoker_call, -1);
612
+ rb_define_method(classInvoker, "call0", invoker_call0, 0);
613
+ rb_define_method(classInvoker, "call1", invoker_call1, 1);
614
+ rb_define_method(classInvoker, "call2", invoker_call2, 2);
615
+ rb_define_method(classInvoker, "call3", invoker_call3, 3);
616
+ rb_define_method(classInvoker, "arity", invoker_arity, 0);
617
+ classVariadicInvoker = rb_define_class_under(moduleFFI, "VariadicInvoker", rb_cObject);
618
+ rb_define_singleton_method(classVariadicInvoker, "__new", variadic_invoker_new, 4);
619
+ rb_define_method(classVariadicInvoker, "invoke", variadic_invoker_call, 2);
270
620
  cbTableID = rb_intern("__ffi_callback_table__");
621
+ to_ptr = rb_intern("to_ptr");
622
+
623
+ rb_define_module_function(moduleError, "error", get_last_error, 0);
624
+ rb_define_module_function(moduleError, "error=", set_last_error, 1);
625
+ #ifdef HAVE_NATIVETHREAD
626
+ pthread_key_create(&threadDataKey, thread_data_free);
627
+ #endif
271
628
  }