ffi 0.4.0 → 0.5.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 (55) hide show
  1. data/README.rdoc +15 -15
  2. data/Rakefile +57 -8
  3. data/ext/ffi_c/AbstractMemory.c +101 -24
  4. data/ext/ffi_c/AbstractMemory.h +98 -6
  5. data/ext/ffi_c/ArrayType.c +129 -0
  6. data/ext/ffi_c/ArrayType.h +58 -0
  7. data/ext/ffi_c/AutoPointer.c +1 -0
  8. data/ext/ffi_c/Buffer.c +59 -43
  9. data/ext/ffi_c/Call.c +853 -0
  10. data/ext/ffi_c/Call.h +86 -0
  11. data/ext/ffi_c/ClosurePool.c +302 -0
  12. data/ext/ffi_c/ClosurePool.h +29 -0
  13. data/ext/ffi_c/DynamicLibrary.c +3 -0
  14. data/ext/ffi_c/Function.c +478 -0
  15. data/ext/ffi_c/Function.h +80 -0
  16. data/ext/ffi_c/FunctionInfo.c +221 -0
  17. data/ext/ffi_c/LastError.c +30 -6
  18. data/ext/ffi_c/MemoryPointer.c +50 -28
  19. data/ext/ffi_c/MethodHandle.c +346 -0
  20. data/ext/ffi_c/MethodHandle.h +53 -0
  21. data/ext/ffi_c/Pointer.c +73 -13
  22. data/ext/ffi_c/Pointer.h +31 -7
  23. data/ext/ffi_c/Struct.c +517 -224
  24. data/ext/ffi_c/Struct.h +60 -6
  25. data/ext/ffi_c/StructByValue.c +140 -0
  26. data/ext/ffi_c/StructByValue.h +53 -0
  27. data/ext/ffi_c/StructLayout.c +450 -0
  28. data/ext/ffi_c/Type.c +121 -22
  29. data/ext/ffi_c/Type.h +37 -8
  30. data/ext/ffi_c/Types.c +46 -61
  31. data/ext/ffi_c/Types.h +38 -7
  32. data/ext/ffi_c/Variadic.c +260 -0
  33. data/ext/ffi_c/compat.h +53 -3
  34. data/ext/ffi_c/extconf.rb +6 -7
  35. data/ext/ffi_c/ffi.c +45 -39
  36. data/ext/ffi_c/libffi.darwin.mk +2 -2
  37. data/ext/ffi_c/rbffi.h +3 -0
  38. data/lib/ffi/ffi.rb +7 -4
  39. data/lib/ffi/library.rb +34 -59
  40. data/lib/ffi/platform.rb +14 -4
  41. data/lib/ffi/struct.rb +110 -281
  42. data/lib/ffi/union.rb +4 -9
  43. data/lib/ffi/variadic.rb +1 -6
  44. data/spec/ffi/buffer_spec.rb +6 -0
  45. data/spec/ffi/callback_spec.rb +34 -3
  46. data/spec/ffi/function_spec.rb +73 -0
  47. data/spec/ffi/library_spec.rb +56 -52
  48. data/spec/ffi/pointer_spec.rb +3 -3
  49. data/spec/ffi/struct_callback_spec.rb +26 -3
  50. data/spec/ffi/struct_spec.rb +56 -3
  51. metadata +42 -11
  52. data/ext/ffi_c/Callback.c +0 -374
  53. data/ext/ffi_c/Callback.h +0 -47
  54. data/ext/ffi_c/Invoker.c +0 -962
  55. data/ext/ffi_c/NullPointer.c +0 -143
data/ext/ffi_c/Call.h ADDED
@@ -0,0 +1,86 @@
1
+ /*
2
+ * Copyright (c) 2009, Wayne Meissner
3
+ * Copyright (c) 2009, Luc Heinrich <luc@honk-honk.com>
4
+ * Copyright (c) 2009, Mike Dalessio <mike.dalessio@gmail.com>
5
+ * Copyright (c) 2009, Aman Gupta.
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without
9
+ * modification, are permitted provided that the following conditions are met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright notice, this
12
+ * list of conditions and the following disclaimer.
13
+ * * Redistributions in binary form must reproduce the above copyright notice
14
+ * this list of conditions and the following disclaimer in the documentation
15
+ * and/or other materials provided with the distribution.
16
+ * * The name of the author or authors may not be used to endorse or promote
17
+ * products derived from this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #ifndef _INVOKE_H
32
+ #define _INVOKE_H
33
+
34
+ #ifdef __cplusplus
35
+ extern "C" {
36
+ #endif
37
+
38
+ #if defined(__i386__) && !defined(_WIN32) && !defined(__WIN32__)
39
+ # define USE_RAW
40
+ #endif
41
+
42
+ #if (defined(__i386__) || defined(__x86_64__)) && !(defined(_WIN32) || defined(__WIN32__))
43
+ # define BYPASS_FFI 1
44
+ #endif
45
+
46
+ typedef union {
47
+ #ifdef USE_RAW
48
+ signed int s8, s16, s32;
49
+ unsigned int u8, u16, u32;
50
+ #else
51
+ signed char s8;
52
+ unsigned char u8;
53
+ signed short s16;
54
+ unsigned short u16;
55
+ signed int s32;
56
+ unsigned int u32;
57
+ #endif
58
+ signed long long i64;
59
+ unsigned long long u64;
60
+ void* ptr;
61
+ float f32;
62
+ double f64;
63
+ } FFIStorage;
64
+
65
+
66
+ extern void rbffi_Call_Init(VALUE moduleFFI);
67
+
68
+ extern void rbffi_SetupCallParams(int argc, VALUE* argv, int paramCount, NativeType* paramTypes,
69
+ FFIStorage* paramStorage, void** ffiValues,
70
+ VALUE* callbackParameters, int callbackCount, VALUE enums);
71
+
72
+ extern VALUE rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo);
73
+
74
+ typedef VALUE (*Invoker)(int argc, VALUE* argv, void* function, FunctionType* fnInfo);
75
+
76
+ Invoker rbffi_GetInvoker(FunctionType* fnInfo);
77
+
78
+ extern VALUE rbffi_GetEnumValue(VALUE enums, VALUE value);
79
+ extern int rbffi_GetSignedIntValue(VALUE value, int type, int minValue, int maxValue, const char* typeName, VALUE enums);
80
+
81
+ #ifdef __cplusplus
82
+ }
83
+ #endif
84
+
85
+ #endif /* _INVOKE_H */
86
+
@@ -0,0 +1,302 @@
1
+ /*
2
+ * Copyright (c) 2009, Wayne Meissner
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * * Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ * * Redistributions in binary form must reproduce the above copyright notice
11
+ * this list of conditions and the following disclaimer in the documentation
12
+ * and/or other materials provided with the distribution.
13
+ * * The name of the author or authors may not be used to endorse or promote
14
+ * products derived from this software without specific prior written permission.
15
+ *
16
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ */
27
+
28
+ #include <sys/param.h>
29
+ #include <sys/types.h>
30
+ #ifndef _WIN32
31
+ # include <sys/mman.h>
32
+ #endif
33
+ #include <stdio.h>
34
+ #include <stdint.h>
35
+ #include <stdbool.h>
36
+ #ifndef _WIN32
37
+ # include <unistd.h>
38
+ #endif
39
+ #include <errno.h>
40
+ #include <ruby.h>
41
+ #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32) && !defined(__WIN32__)
42
+ # include <pthread.h>
43
+ #endif
44
+
45
+ #include <ffi.h>
46
+ #include "rbffi.h"
47
+ #include "compat.h"
48
+
49
+ #include "Function.h"
50
+ #include "Types.h"
51
+ #include "Type.h"
52
+ #include "LastError.h"
53
+ #include "Call.h"
54
+
55
+ #include "ClosurePool.h"
56
+
57
+
58
+ #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32) && !defined(__WIN32__)
59
+ # define USE_PTHREAD_LOCAL
60
+ #endif
61
+
62
+ #ifndef roundup
63
+ # define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
64
+ #endif
65
+ #ifdef _WIN32
66
+ typedef char* caddr_t;
67
+ #endif
68
+
69
+ typedef struct Memory {
70
+ void* code;
71
+ void* data;
72
+ struct Memory* next;
73
+ } Memory;
74
+
75
+ struct ClosurePool_ {
76
+ void* ctx;
77
+ int closureSize;
78
+ bool (*prep)(void* ctx, void *code, Closure* closure, char* errbuf, size_t errbufsize);
79
+ #if defined (HAVE_NATIVETHREAD) && !defined(_WIN32)
80
+ pthread_mutex_t mutex;
81
+ #endif
82
+ struct Memory* blocks; /* Keeps track of all the allocated memory for this pool */
83
+ Closure* list;
84
+ long refcnt;
85
+ };
86
+
87
+ #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32)
88
+ # define pool_lock(p) pthread_mutex_lock(&(p)->mutex)
89
+ # define pool_unlock(p) pthread_mutex_unlock(&(p)->mutex)
90
+ #else
91
+ # define pool_lock(p)
92
+ # define pool_unlock(p)
93
+ #endif
94
+
95
+ static int pageSize;
96
+
97
+ static void* allocatePage(void);
98
+ static bool freePage(void *);
99
+ static bool protectPage(void *);
100
+
101
+ ClosurePool*
102
+ rbffi_ClosurePool_New(int closureSize,
103
+ bool (*prep)(void* ctx, void *code, Closure* closure, char* errbuf, size_t errbufsize),
104
+ void* ctx)
105
+ {
106
+ ClosurePool* pool;
107
+
108
+ pool = xcalloc(1, sizeof(*pool));
109
+ pool->closureSize = closureSize;
110
+ pool->ctx = ctx;
111
+ pool->prep = prep;
112
+ pool->refcnt = 1;
113
+
114
+ #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32) && !defined(__WIN32__)
115
+ pthread_mutex_init(&pool->mutex, NULL);
116
+ #endif
117
+
118
+ return pool;
119
+ }
120
+
121
+ void
122
+ cleanup_closure_pool(ClosurePool* pool)
123
+ {
124
+ Memory* memory;
125
+
126
+ for (memory = pool->blocks; memory != NULL; ) {
127
+ Memory* next = memory->next;
128
+ freePage(memory->code);
129
+ free(memory->data);
130
+ free(memory);
131
+ memory = next;
132
+ }
133
+ free(pool);
134
+ }
135
+
136
+ void
137
+ rbffi_ClosurePool_Free(ClosurePool* pool)
138
+ {
139
+ if (pool != NULL) {
140
+ int refcnt;
141
+ pool_lock(pool);
142
+ refcnt = --(pool->refcnt);
143
+ pool_unlock(pool);
144
+
145
+ if (refcnt == 0) {
146
+ cleanup_closure_pool(pool);
147
+ }
148
+ }
149
+ }
150
+
151
+ Closure*
152
+ rbffi_Closure_Alloc(ClosurePool* pool)
153
+ {
154
+ Closure *list = NULL;
155
+ Memory* block = NULL;
156
+ caddr_t code = NULL;
157
+ char errmsg[256];
158
+ int nclosures, trampolineSize;
159
+ int i;
160
+
161
+ pool_lock(pool);
162
+ if (pool->list != NULL) {
163
+ Closure* closure = pool->list;
164
+ pool->list = pool->list->next;
165
+ pool->refcnt++;
166
+ pool_unlock(pool);
167
+
168
+ return closure;
169
+ }
170
+
171
+ trampolineSize = roundup(pool->closureSize, 8);
172
+ nclosures = pageSize / trampolineSize;
173
+ block = calloc(1, sizeof(*block));
174
+ list = calloc(nclosures, sizeof(*list));
175
+ code = allocatePage();
176
+
177
+ if (block == NULL || list == NULL || code == NULL) {
178
+ pool_unlock(pool);
179
+ snprintf(errmsg, sizeof(errmsg), "failed to allocate a page. errno=%d (%s)", errno, strerror(errno));
180
+ goto error;
181
+ }
182
+
183
+ for (i = 0; i < nclosures; ++i) {
184
+ Closure* closure = &list[i];
185
+ closure->next = &list[i + 1];
186
+ closure->pool = pool;
187
+ closure->code = (code + (i * trampolineSize));
188
+
189
+ if (!(*pool->prep)(pool->ctx, closure->code, closure, errmsg, sizeof(errmsg))) {
190
+ goto error;
191
+ }
192
+ }
193
+
194
+ if (!protectPage(code)) {
195
+ goto error;
196
+ }
197
+
198
+ /* Track the allocated page + Closure memory area */
199
+ block->data = list;
200
+ block->code = code;
201
+ block->next = pool->blocks;
202
+ pool->blocks = block;
203
+
204
+ /* Thread the new block onto the free list, apart from the first one. */
205
+ list[nclosures - 1].next = pool->list;
206
+ pool->list = list->next;
207
+ pool->refcnt++;
208
+
209
+ pool_unlock(pool);
210
+
211
+ /* Use the first one as the new handle */
212
+ return list;
213
+
214
+ error:
215
+ pool_unlock(pool);
216
+ free(block);
217
+ free(list);
218
+ if (code != NULL) {
219
+ freePage(code);
220
+ }
221
+
222
+
223
+ rb_raise(rb_eRuntimeError, "%s", errmsg);
224
+ return NULL;
225
+ }
226
+
227
+ void
228
+ rbffi_Closure_Free(Closure* closure)
229
+ {
230
+ if (closure != NULL) {
231
+ ClosurePool* pool = closure->pool;
232
+ int refcnt;
233
+ pool_lock(pool);
234
+ // Just push it on the front of the free list
235
+ closure->next = pool->list;
236
+ pool->list = closure;
237
+ refcnt = --(pool->refcnt);
238
+ pool_unlock(pool);
239
+
240
+ if (refcnt == 0) {
241
+ cleanup_closure_pool(pool);
242
+ }
243
+ }
244
+ }
245
+
246
+ void*
247
+ rbffi_Closure_CodeAddress(Closure* handle)
248
+ {
249
+ return handle->code;
250
+ }
251
+
252
+
253
+ static int
254
+ getPageSize()
255
+ {
256
+ #ifdef _WIN32
257
+ SYSTEM_INFO si;
258
+ GetSystemInfo(&si);
259
+ return si.dwPageSize;
260
+ #else
261
+ return sysconf(_SC_PAGESIZE);
262
+ #endif
263
+ }
264
+
265
+ static void*
266
+ allocatePage(void)
267
+ {
268
+ #ifdef _WIN32
269
+ return VirtualAlloc(NULL, pageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
270
+ #else
271
+ caddr_t page = mmap(NULL, pageSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
272
+ return (page != (caddr_t) -1) ? page : NULL;
273
+ #endif
274
+ }
275
+
276
+ static bool
277
+ freePage(void *addr)
278
+ {
279
+ #ifdef _WIN32
280
+ return VirtualFree(addr, 0, MEM_RELEASE);
281
+ #else
282
+ return munmap(addr, pageSize) == 0;
283
+ #endif
284
+ }
285
+
286
+ static bool
287
+ protectPage(void* page)
288
+ {
289
+ #ifdef _WIN32
290
+ DWORD oldProtect;
291
+ return VirtualProtect(page, pageSize, PAGE_EXECUTE_READ, &oldProtect);
292
+ #else
293
+ return mprotect(page, pageSize, PROT_READ | PROT_EXEC) == 0;
294
+ #endif
295
+ }
296
+
297
+ void
298
+ rbffi_ClosurePool_Init(VALUE module)
299
+ {
300
+ pageSize = getPageSize();
301
+ }
302
+
@@ -0,0 +1,29 @@
1
+ #ifndef RUBYFFI_CLOSUREPOOL_H
2
+ #define RUBYFFI_CLOSUREPOOL_H
3
+
4
+ typedef struct ClosurePool_ ClosurePool;
5
+ typedef struct Closure_ Closure;
6
+
7
+ struct Closure_ {
8
+ void* info; /* opaque handle for storing closure-instance specific data */
9
+ void* function; /* closure-instance specific function, called by custom trampoline */
10
+ void* code; /* The native trampoline code location */
11
+ struct ClosurePool_* pool;
12
+ Closure* next;
13
+ };
14
+
15
+ void rbffi_ClosurePool_Init(VALUE module);
16
+
17
+ ClosurePool* rbffi_ClosurePool_New(int closureSize,
18
+ bool (*prep)(void* ctx, void *code, Closure* closure, char* errbuf, size_t errbufsize),
19
+ void* ctx);
20
+
21
+ void rbffi_ClosurePool_Free(ClosurePool *);
22
+
23
+ Closure* rbffi_Closure_Alloc(ClosurePool *);
24
+ void rbffi_Closure_Free(Closure *);
25
+
26
+ void* rbffi_Closure_GetCodeAddress(Closure *);
27
+
28
+ #endif /* RUBYFFI_CLOSUREPOOL_H */
29
+
@@ -155,6 +155,9 @@ symbol_new(VALUE library, void* address, VALUE name)
155
155
 
156
156
  sym->memory.address = address;
157
157
  sym->memory.size = LONG_MAX;
158
+ sym->memory.typeSize = 1;
159
+ sym->memory.access = MEM_RD | MEM_WR;
160
+ sym->memory.ops = &rbffi_AbstractMemoryOps;
158
161
  sym->library = library;
159
162
  sym->name = name;
160
163
 
@@ -0,0 +1,478 @@
1
+ /*
2
+ * Copyright (c) 2009, Wayne Meissner
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * * Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ * * Redistributions in binary form must reproduce the above copyright notice
11
+ * this list of conditions and the following disclaimer in the documentation
12
+ * and/or other materials provided with the distribution.
13
+ * * The name of the author or authors may not be used to endorse or promote
14
+ * products derived from this software without specific prior written permission.
15
+ *
16
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ */
27
+
28
+ #include "MethodHandle.h"
29
+
30
+
31
+ #include <sys/param.h>
32
+ #include <sys/types.h>
33
+ #ifndef _WIN32
34
+ # include <sys/mman.h>
35
+ #endif
36
+ #include <stdio.h>
37
+ #include <stdint.h>
38
+ #include <stdbool.h>
39
+ #include <ruby.h>
40
+
41
+ #include <ffi.h>
42
+ #include "rbffi.h"
43
+ #include "compat.h"
44
+
45
+ #include "AbstractMemory.h"
46
+ #include "Pointer.h"
47
+ #include "Struct.h"
48
+ #include "Platform.h"
49
+ #include "Type.h"
50
+ #include "LastError.h"
51
+ #include "Call.h"
52
+ #include "ClosurePool.h"
53
+ #include "Function.h"
54
+
55
+ typedef struct Function_ {
56
+ AbstractMemory memory;
57
+ FunctionType* info;
58
+ MethodHandle* methodHandle;
59
+ bool autorelease;
60
+ Closure* closure;
61
+ VALUE rbProc;
62
+ VALUE rbFunctionInfo;
63
+ } Function;
64
+
65
+ static void function_mark(Function *);
66
+ static void function_free(Function *);
67
+ static VALUE function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc);
68
+ static void callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data);
69
+ static bool callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize);
70
+
71
+ VALUE rbffi_FunctionClass = Qnil;
72
+
73
+ static ID id_call = 0, id_cbtable = 0, id_cb_ref = 0;
74
+
75
+ static VALUE
76
+ function_allocate(VALUE klass)
77
+ {
78
+ Function *fn;
79
+ VALUE obj;
80
+
81
+ obj = Data_Make_Struct(klass, Function, function_mark, function_free, fn);
82
+
83
+ fn->memory.access = MEM_RD;
84
+ fn->memory.ops = &rbffi_AbstractMemoryOps;
85
+
86
+ fn->rbProc = Qnil;
87
+ fn->rbFunctionInfo = Qnil;
88
+ fn->autorelease = true;
89
+
90
+ return obj;
91
+ }
92
+
93
+ static void
94
+ function_mark(Function *fn)
95
+ {
96
+ rb_gc_mark(fn->rbProc);
97
+ rb_gc_mark(fn->rbFunctionInfo);
98
+ }
99
+
100
+ static void
101
+ function_free(Function *fn)
102
+ {
103
+ if (fn->methodHandle != NULL) {
104
+ rbffi_MethodHandle_Free(fn->methodHandle);
105
+ }
106
+
107
+ if (fn->closure != NULL && fn->autorelease) {
108
+ rbffi_Closure_Free(fn->closure);
109
+ }
110
+
111
+ xfree(fn);
112
+ }
113
+
114
+ static VALUE
115
+ function_initialize(int argc, VALUE* argv, VALUE self)
116
+ {
117
+
118
+ VALUE rbReturnType = Qnil, rbParamTypes = Qnil, rbProc = Qnil, rbOptions = Qnil;
119
+ VALUE rbFunctionInfo = Qnil;
120
+ VALUE infoArgv[3];
121
+ int nargs;
122
+
123
+ nargs = rb_scan_args(argc, argv, "22", &rbReturnType, &rbParamTypes, &rbProc, &rbOptions);
124
+
125
+ //
126
+ // Callback with block,
127
+ // e.g. Function.new(:int, [ :int ]) { |i| blah }
128
+ // or Function.new(:int, [ :int ], { :convention => :stdcall }) { |i| blah }
129
+ //
130
+ if (rb_block_given_p()) {
131
+ if (nargs > 3) {
132
+ rb_raise(rb_eArgError, "cannot create function with both proc/address and block");
133
+ }
134
+ rbOptions = rbProc;
135
+ rbProc = rb_block_proc();
136
+ } else {
137
+ // Callback with proc, or Function with address
138
+ // e.g. Function.new(:int, [ :int ], Proc.new { |i| })
139
+ // Function.new(:int, [ :int ], Proc.new { |i| }, { :convention => :stdcall })
140
+ // Function.new(:int, [ :int ], addr)
141
+ // Function.new(:int, [ :int ], addr, { :convention => :stdcall })
142
+ }
143
+
144
+ infoArgv[0] = rbReturnType;
145
+ infoArgv[1] = rbParamTypes;
146
+ infoArgv[2] = rbOptions;
147
+ rbFunctionInfo = rb_class_new_instance(rbOptions != Qnil ? 3 : 2, infoArgv, rbffi_FunctionTypeClass);
148
+
149
+ function_init(self, rbFunctionInfo, rbProc);
150
+
151
+ return self;
152
+ }
153
+
154
+ VALUE
155
+ rbffi_Function_NewInstance(VALUE rbFunctionInfo, VALUE rbProc)
156
+ {
157
+ return function_init(function_allocate(rbffi_FunctionClass), rbFunctionInfo, rbProc);
158
+ }
159
+
160
+ VALUE
161
+ rbffi_Function_ForProc(VALUE rbFunctionInfo, VALUE proc)
162
+ {
163
+ VALUE callback, cbref, cbTable;
164
+ Function* fp;
165
+
166
+ cbref = RTEST(rb_ivar_defined(proc, id_cb_ref)) ? rb_ivar_get(proc, id_cb_ref) : Qnil;
167
+ /* If the first callback reference has the same function function signature, use it */
168
+ if (cbref != Qnil && CLASS_OF(cbref) == rbffi_FunctionClass) {
169
+ Data_Get_Struct(cbref, Function, fp);
170
+ if (fp->rbFunctionInfo == rbFunctionInfo) {
171
+ return cbref;
172
+ }
173
+ }
174
+
175
+ cbTable = RTEST(rb_ivar_defined(proc, id_cbtable)) ? rb_ivar_get(proc, id_cbtable) : Qnil;
176
+ if (cbTable != Qnil && (callback = rb_hash_aref(cbTable, rbFunctionInfo)) != Qnil) {
177
+ return callback;
178
+ }
179
+
180
+ /* No existing function for the proc with that signature, create a new one and cache it */
181
+ callback = rbffi_Function_NewInstance(rbFunctionInfo, proc);
182
+ if (cbref == Qnil) {
183
+ /* If there is no other cb already cached for this proc, we can use the ivar slot */
184
+ rb_ivar_set(proc, id_cb_ref, callback);
185
+ } else {
186
+ /* The proc instance has been used as more than one type of callback, store extras in a hash */
187
+ cbTable = rb_hash_new();
188
+ rb_ivar_set(proc, id_cbtable, cbTable);
189
+ rb_hash_aset(cbTable, rbFunctionInfo, callback);
190
+ }
191
+
192
+ return callback;
193
+ }
194
+
195
+ static VALUE
196
+ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
197
+ {
198
+ Function* fn = NULL;
199
+
200
+ Data_Get_Struct(self, Function, fn);
201
+
202
+ fn->rbFunctionInfo = rbFunctionInfo;
203
+
204
+ Data_Get_Struct(fn->rbFunctionInfo, FunctionType, fn->info);
205
+
206
+ if (rb_obj_is_kind_of(rbProc, rbffi_PointerClass)) {
207
+ AbstractMemory* memory;
208
+ Data_Get_Struct(rbProc, AbstractMemory, memory);
209
+ fn->memory = *memory;
210
+
211
+ } else if (rb_obj_is_kind_of(rbProc, rb_cProc) || rb_respond_to(rbProc, id_call)) {
212
+ if (fn->info->closurePool == NULL) {
213
+ fn->info->closurePool = rbffi_ClosurePool_New(sizeof(ffi_closure), callback_prep, fn->info);
214
+ if (fn->info->closurePool == NULL) {
215
+ rb_raise(rb_eNoMemError, "failed to create closure pool");
216
+ }
217
+ }
218
+
219
+ fn->closure = rbffi_Closure_Alloc(fn->info->closurePool);
220
+ fn->closure->info = fn;
221
+ fn->memory.address = fn->closure->code;
222
+ fn->memory.size = sizeof(*fn->closure);
223
+ fn->autorelease = true;
224
+
225
+ } else {
226
+ rb_raise(rb_eTypeError, "wrong argument type. Expected pointer or proc");
227
+ }
228
+
229
+ fn->rbProc = rbProc;
230
+
231
+ return self;
232
+ }
233
+
234
+ static VALUE
235
+ function_call(int argc, VALUE* argv, VALUE self)
236
+ {
237
+ Function* fn;
238
+
239
+ Data_Get_Struct(self, Function, fn);
240
+
241
+ return (*fn->info->invoke)(argc, argv, fn->memory.address, fn->info);
242
+ }
243
+
244
+ static VALUE
245
+ function_attach(VALUE self, VALUE module, VALUE name)
246
+ {
247
+ Function* fn;
248
+ char var[1024];
249
+
250
+ Data_Get_Struct(self, Function, fn);
251
+
252
+ if (fn->info->parameterCount == -1) {
253
+ rb_raise(rb_eRuntimeError, "Cannot attach variadic functions");
254
+ }
255
+
256
+ if (fn->methodHandle == NULL) {
257
+ fn->methodHandle = rbffi_MethodHandle_Alloc(fn->info, fn->memory.address);
258
+ }
259
+
260
+ //
261
+ // Stash the Function in a module variable so it does not get garbage collected
262
+ //
263
+ snprintf(var, sizeof(var), "@@%s", StringValueCStr(name));
264
+ rb_cv_set(module, var, self);
265
+
266
+ rb_define_singleton_method(module, StringValueCStr(name),
267
+ rbffi_MethodHandle_CodeAddress(fn->methodHandle), -1);
268
+
269
+
270
+ rb_define_method(module, StringValueCStr(name),
271
+ rbffi_MethodHandle_CodeAddress(fn->methodHandle), -1);
272
+
273
+ return self;
274
+ }
275
+
276
+ static VALUE
277
+ function_set_autorelease(VALUE self, VALUE autorelease)
278
+ {
279
+ Function* fn;
280
+
281
+ Data_Get_Struct(self, Function, fn);
282
+
283
+ fn->autorelease = RTEST(autorelease);
284
+
285
+ return self;
286
+ }
287
+
288
+ static VALUE
289
+ function_autorelease_p(VALUE self)
290
+ {
291
+ Function* fn;
292
+
293
+ Data_Get_Struct(self, Function, fn);
294
+
295
+ return fn->autorelease ? Qtrue : Qfalse;
296
+ }
297
+
298
+ static VALUE
299
+ function_release(VALUE self)
300
+ {
301
+ Function* fn;
302
+
303
+ Data_Get_Struct(self, Function, fn);
304
+
305
+ if (fn->closure == NULL) {
306
+ rb_raise(rb_eRuntimeError, "cannot free function which was not allocated");
307
+ }
308
+
309
+ rbffi_Closure_Free(fn->closure);
310
+ fn->closure = NULL;
311
+
312
+ return self;
313
+ }
314
+
315
+ static void
316
+ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
317
+ {
318
+ Closure* closure = (Closure *) user_data;
319
+ Function* fn = (Function *) closure->info;
320
+ FunctionType *cbInfo = fn->info;
321
+ VALUE* rbParams;
322
+ VALUE rbReturnValue;
323
+ int i;
324
+
325
+ rbParams = ALLOCA_N(VALUE, cbInfo->parameterCount);
326
+ for (i = 0; i < cbInfo->parameterCount; ++i) {
327
+ VALUE param;
328
+ switch (cbInfo->parameterTypes[i]->nativeType) {
329
+ case NATIVE_INT8:
330
+ param = INT2NUM(*(int8_t *) parameters[i]);
331
+ break;
332
+ case NATIVE_UINT8:
333
+ param = UINT2NUM(*(uint8_t *) parameters[i]);
334
+ break;
335
+ case NATIVE_INT16:
336
+ param = INT2NUM(*(int16_t *) parameters[i]);
337
+ break;
338
+ case NATIVE_UINT16:
339
+ param = UINT2NUM(*(uint16_t *) parameters[i]);
340
+ break;
341
+ case NATIVE_INT32:
342
+ param = INT2NUM(*(int32_t *) parameters[i]);
343
+ break;
344
+ case NATIVE_UINT32:
345
+ param = UINT2NUM(*(uint32_t *) parameters[i]);
346
+ break;
347
+ case NATIVE_INT64:
348
+ param = LL2NUM(*(int64_t *) parameters[i]);
349
+ break;
350
+ case NATIVE_UINT64:
351
+ param = ULL2NUM(*(uint64_t *) parameters[i]);
352
+ break;
353
+ case NATIVE_FLOAT32:
354
+ param = rb_float_new(*(float *) parameters[i]);
355
+ break;
356
+ case NATIVE_FLOAT64:
357
+ param = rb_float_new(*(double *) parameters[i]);
358
+ break;
359
+ case NATIVE_STRING:
360
+ param = (*(void **) parameters[i] != NULL) ? rb_tainted_str_new2(*(char **) parameters[i]) : Qnil;
361
+ break;
362
+ case NATIVE_POINTER:
363
+ param = rbffi_Pointer_NewInstance(*(void **) parameters[i]);
364
+ break;
365
+ case NATIVE_BOOL:
366
+ param = (*(void **) parameters[i]) ? Qtrue : Qfalse;
367
+ break;
368
+
369
+ case NATIVE_FUNCTION:
370
+ case NATIVE_CALLBACK:
371
+ param = rbffi_NativeValue_ToRuby(cbInfo->parameterTypes[i],
372
+ rb_ary_entry(cbInfo->rbParameterTypes, i), parameters[i], Qnil);
373
+ break;
374
+ default:
375
+ param = Qnil;
376
+ break;
377
+ }
378
+ rbParams[i] = param;
379
+ }
380
+ rbReturnValue = rb_funcall2(fn->rbProc, id_call, cbInfo->parameterCount, rbParams);
381
+ if (rbReturnValue == Qnil || TYPE(rbReturnValue) == T_NIL) {
382
+ memset(retval, 0, cbInfo->ffiReturnType->size);
383
+ } else switch (cbInfo->returnType->nativeType) {
384
+ case NATIVE_INT8:
385
+ case NATIVE_INT16:
386
+ case NATIVE_INT32:
387
+ *((ffi_sarg *) retval) = NUM2INT(rbReturnValue);
388
+ break;
389
+ case NATIVE_UINT8:
390
+ case NATIVE_UINT16:
391
+ case NATIVE_UINT32:
392
+ *((ffi_arg *) retval) = NUM2UINT(rbReturnValue);
393
+ break;
394
+ case NATIVE_INT64:
395
+ *((int64_t *) retval) = NUM2LL(rbReturnValue);
396
+ break;
397
+ case NATIVE_UINT64:
398
+ *((uint64_t *) retval) = NUM2ULL(rbReturnValue);
399
+ break;
400
+ case NATIVE_FLOAT32:
401
+ *((float *) retval) = (float) NUM2DBL(rbReturnValue);
402
+ break;
403
+ case NATIVE_FLOAT64:
404
+ *((double *) retval) = NUM2DBL(rbReturnValue);
405
+ break;
406
+ case NATIVE_POINTER:
407
+ if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
408
+ *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
409
+ } else {
410
+ // Default to returning NULL if not a value pointer object. handles nil case as well
411
+ *((void **) retval) = NULL;
412
+ }
413
+ break;
414
+ case NATIVE_BOOL:
415
+ *((ffi_sarg *) retval) = TYPE(rbReturnValue) == T_TRUE ? 1 : 0;
416
+ break;
417
+
418
+ case NATIVE_FUNCTION:
419
+ case NATIVE_CALLBACK:
420
+ if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
421
+
422
+ *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
423
+
424
+ } else if (rb_obj_is_kind_of(rbReturnValue, rb_cProc) || rb_respond_to(rbReturnValue, id_call)) {
425
+ VALUE function;
426
+
427
+ function = rbffi_Function_ForProc(cbInfo->rbReturnType, rbReturnValue);
428
+
429
+ *((void **) retval) = ((AbstractMemory *) DATA_PTR(function))->address;
430
+ } else {
431
+ *((void **) retval) = NULL;
432
+ }
433
+ break;
434
+
435
+ default:
436
+ *((ffi_arg *) retval) = 0;
437
+ break;
438
+ }
439
+ }
440
+
441
+
442
+ static bool
443
+ callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize)
444
+ {
445
+ FunctionType* fnInfo = (FunctionType *) ctx;
446
+ ffi_status ffiStatus;
447
+
448
+ ffiStatus = ffi_prep_closure(code, &fnInfo->ffi_cif, callback_invoke, closure);
449
+ if (ffiStatus != FFI_OK) {
450
+ snprintf(errmsg, errmsgsize, "ffi_prep_closure failed. status=%#x", ffiStatus);
451
+ return false;
452
+ }
453
+
454
+ return true;
455
+ }
456
+
457
+ void
458
+ rbffi_Function_Init(VALUE moduleFFI)
459
+ {
460
+ rbffi_FunctionInfo_Init(moduleFFI);
461
+ rbffi_FunctionClass = rb_define_class_under(moduleFFI, "Function", rbffi_PointerClass);
462
+
463
+ rb_global_variable(&rbffi_FunctionClass);
464
+ rb_define_alloc_func(rbffi_FunctionClass, function_allocate);
465
+
466
+ rb_define_method(rbffi_FunctionClass, "initialize", function_initialize, -1);
467
+ rb_define_method(rbffi_FunctionClass, "call", function_call, -1);
468
+ rb_define_method(rbffi_FunctionClass, "attach", function_attach, 2);
469
+ rb_define_method(rbffi_FunctionClass, "free", function_release, 0);
470
+ rb_define_method(rbffi_FunctionClass, "autorelease=", function_set_autorelease, 1);
471
+ rb_define_method(rbffi_FunctionClass, "autorelease", function_autorelease_p, 0);
472
+ rb_define_method(rbffi_FunctionClass, "autorelease?", function_autorelease_p, 0);
473
+
474
+ id_call = rb_intern("call");
475
+ id_cbtable = rb_intern("@__ffi_callback_table__");
476
+ id_cb_ref = rb_intern("@__ffi_callback__");
477
+ }
478
+