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.
- data/Rakefile +52 -29
- data/ext/AbstractMemory.c +72 -28
- data/ext/AutoPointer.c +54 -0
- data/ext/AutoPointer.h +18 -0
- data/ext/Buffer.c +21 -17
- data/ext/Callback.c +81 -43
- data/ext/Callback.h +1 -1
- data/ext/Invoker.c +465 -108
- data/ext/MemoryPointer.c +25 -90
- data/ext/NativeLibrary.c +90 -0
- data/ext/NativeLibrary.h +22 -0
- data/ext/Platform.c +21 -2
- data/ext/Pointer.c +107 -0
- data/ext/Pointer.h +21 -0
- data/ext/Types.c +16 -5
- data/ext/Types.h +3 -1
- data/ext/compat.h +14 -0
- data/ext/extconf.rb +13 -1
- data/ext/ffi.c +11 -1
- data/ext/ffi.mk +3 -3
- data/ext/libffi.darwin.mk +19 -8
- data/gen/Rakefile +12 -0
- data/lib/ffi/autopointer.rb +61 -0
- data/lib/ffi/errno.rb +8 -0
- data/lib/ffi/ffi.rb +38 -201
- data/lib/ffi/io.rb +7 -0
- data/lib/ffi/library.rb +116 -0
- data/lib/ffi/managedstruct.rb +55 -0
- data/lib/ffi/memorypointer.rb +3 -96
- data/lib/ffi/platform.rb +8 -5
- data/lib/ffi/pointer.rb +105 -0
- data/lib/ffi/struct.rb +97 -42
- data/lib/ffi/tools/const_generator.rb +177 -0
- data/lib/ffi/tools/generator.rb +58 -0
- data/lib/ffi/tools/generator_task.rb +35 -0
- data/lib/ffi/tools/struct_generator.rb +194 -0
- data/lib/ffi/tools/types_generator.rb +123 -0
- data/lib/ffi/types.rb +150 -0
- data/lib/ffi/variadic.rb +30 -0
- data/nbproject/Makefile-Default.mk +6 -3
- data/nbproject/Makefile-impl.mk +5 -5
- data/nbproject/Package-Default.bash +72 -0
- data/nbproject/configurations.xml +139 -25
- data/nbproject/private/configurations.xml +1 -1
- data/nbproject/project.xml +4 -0
- data/samples/gettimeofday.rb +6 -2
- data/samples/inotify.rb +59 -0
- data/samples/pty.rb +75 -0
- data/specs/buffer_spec.rb +64 -9
- data/specs/callback_spec.rb +308 -4
- data/specs/errno_spec.rb +13 -0
- data/specs/library_spec.rb +55 -0
- data/specs/managed_struct_spec.rb +40 -0
- data/specs/number_spec.rb +183 -0
- data/specs/pointer_spec.rb +126 -0
- data/specs/rbx/memory_pointer_spec.rb +7 -7
- data/specs/spec_helper.rb +7 -0
- data/specs/string_spec.rb +34 -0
- data/specs/struct_spec.rb +223 -0
- data/specs/typedef_spec.rb +48 -0
- data/specs/variadic_spec.rb +84 -0
- metadata +270 -237
data/ext/Callback.h
CHANGED
data/ext/Invoker.c
CHANGED
@@ -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 "
|
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
|
-
|
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
|
-
|
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
|
38
|
-
static
|
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
|
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
|
-
|
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
|
-
|
57
|
-
|
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,
|
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
|
-
|
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
|
-
|
85
|
-
goto error;
|
132
|
+
rb_raise(rb_eArgError, "Invalid return type");
|
86
133
|
}
|
87
134
|
#ifdef _WIN32
|
88
|
-
abi = strcmp(
|
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
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
105
|
-
if (
|
106
|
-
|
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
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
-
|
214
|
+
return i;
|
124
215
|
}
|
125
216
|
|
126
|
-
|
127
|
-
|
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
|
-
|
237
|
+
FFIStorage* param = ¶mStorage[0];
|
238
|
+
int i, argidx, cbidx, argCount;
|
142
239
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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 <
|
151
|
-
|
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
|
-
|
156
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
168
|
-
|
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
|
-
|
173
|
-
|
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 (
|
178
|
-
|
295
|
+
if (type != T_FLOAT && type != T_FIXNUM) {
|
296
|
+
rb_raise(rb_eTypeError, "Expected a Float parameter");
|
179
297
|
}
|
180
|
-
|
298
|
+
param->f32 = (float) NUM2DBL(argv[argidx]);
|
299
|
+
ADJ(param, FLOAT32);
|
181
300
|
++argidx;
|
182
301
|
break;
|
183
302
|
case FLOAT64:
|
184
|
-
if (
|
185
|
-
|
303
|
+
if (type != T_FLOAT && type != T_FIXNUM) {
|
304
|
+
rb_raise(rb_eTypeError, "Expected a Float parameter");
|
186
305
|
}
|
187
|
-
|
306
|
+
param->f64 = NUM2DBL(argv[argidx]);
|
307
|
+
ADJ(param, FLOAT64);
|
188
308
|
++argidx;
|
189
309
|
break;
|
190
310
|
case STRING:
|
191
|
-
|
192
|
-
|
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
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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
|
-
|
353
|
+
param->ptr = callback_param(callbackProc, invoker->callbackParameters[cbidx++]);
|
214
354
|
} else {
|
215
|
-
|
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",
|
361
|
+
rb_raise(rb_eArgError, "Invalid parameter type: %d", paramTypes[i]);
|
221
362
|
}
|
222
|
-
ffiValues[i] = ¶ms[i];
|
223
363
|
}
|
224
|
-
|
225
|
-
|
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
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
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
|
}
|