ffi 1.9.10 → 1.9.11
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.
- checksums.yaml +4 -4
- data/Rakefile +30 -8
- data/ext/ffi_c/AbstractMemory.c +1 -1
- data/ext/ffi_c/Buffer.c +1 -1
- data/ext/ffi_c/Call.c +12 -25
- data/ext/ffi_c/Call.h +17 -0
- data/ext/ffi_c/Function.c +24 -26
- data/ext/ffi_c/MethodHandle.c +3 -5
- data/ext/ffi_c/Pointer.c +3 -3
- data/ext/ffi_c/Variadic.c +24 -0
- data/ext/ffi_c/compat.h +0 -5
- data/ext/ffi_c/libffi/src/mips/ffi.c +4 -2
- data/ext/ffi_c/rbffi_endian.h +1 -1
- data/ffi.gemspec +1 -1
- data/lib/ffi/autopointer.rb +36 -27
- data/lib/ffi/enum.rb +1 -3
- data/lib/ffi/library.rb +14 -1
- data/lib/ffi/pointer.rb +1 -1
- data/lib/ffi/struct.rb +1 -1
- data/lib/ffi/tools/types_generator.rb +6 -7
- data/lib/ffi/types.rb +8 -4
- data/lib/ffi/version.rb +1 -1
- data/libtest/FunctionTest.c +1 -1
- data/libtest/VariadicTest.c +37 -0
- data/spec/ffi/fixtures/ClosureTest.c +15 -0
- data/spec/ffi/fixtures/FunctionTest.c +88 -4
- data/spec/ffi/fixtures/PipeHelper.h +21 -0
- data/spec/ffi/fixtures/PipeHelperPosix.c +41 -0
- data/spec/ffi/fixtures/PipeHelperWindows.c +72 -0
- data/spec/ffi/function_spec.rb +15 -10
- data/spec/ffi/rbx/attach_function_spec.rb +22 -21
- data/spec/ffi/struct_spec.rb +4 -4
- data/spec/ffi/variadic_spec.rb +19 -0
- metadata +8 -5
data/ext/ffi_c/Variadic.c
CHANGED
@@ -64,6 +64,7 @@ typedef struct VariadicInvoker_ {
|
|
64
64
|
ffi_abi abi;
|
65
65
|
void* function;
|
66
66
|
int paramCount;
|
67
|
+
bool blocking;
|
67
68
|
} VariadicInvoker;
|
68
69
|
|
69
70
|
|
@@ -84,6 +85,7 @@ variadic_allocate(VALUE klass)
|
|
84
85
|
invoker->rbAddress = Qnil;
|
85
86
|
invoker->rbEnums = Qnil;
|
86
87
|
invoker->rbReturnType = Qnil;
|
88
|
+
invoker->blocking = false;
|
87
89
|
|
88
90
|
return obj;
|
89
91
|
}
|
@@ -115,6 +117,7 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE
|
|
115
117
|
invoker->rbEnums = rb_hash_aref(options, ID2SYM(rb_intern("enums")));
|
116
118
|
invoker->rbAddress = rbFunction;
|
117
119
|
invoker->function = rbffi_AbstractMemory_Cast(rbFunction, rbffi_PointerClass)->address;
|
120
|
+
invoker->blocking = RTEST(rb_hash_aref(options, ID2SYM(rb_intern("blocking"))));
|
118
121
|
|
119
122
|
#if defined(X86_WIN32)
|
120
123
|
rbConventionStr = rb_funcall2(convention, rb_intern("to_s"), 0, NULL);
|
@@ -253,7 +256,28 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
|
|
253
256
|
ffiValues, NULL, 0, invoker->rbEnums);
|
254
257
|
|
255
258
|
rbffi_frame_push(&frame);
|
259
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
260
|
+
/* In Call.c, blocking: true is supported on older ruby variants
|
261
|
+
* without rb_thread_call_without_gvl by allocating on the heap instead
|
262
|
+
* of the stack. Since this functionality is being added later,
|
263
|
+
* we’re skipping support for old rubies here. */
|
264
|
+
if(unlikely(invoker->blocking)) {
|
265
|
+
rbffi_blocking_call_t* bc;
|
266
|
+
bc = ALLOCA_N(rbffi_blocking_call_t, 1);
|
267
|
+
bc->retval = retval;
|
268
|
+
bc->function = invoker->function;
|
269
|
+
bc->ffiValues = ffiValues;
|
270
|
+
bc->params = params;
|
271
|
+
bc->frame = &frame;
|
272
|
+
bc->cif = cif;
|
273
|
+
|
274
|
+
rb_rescue2(rbffi_do_blocking_call, (VALUE) bc, rbffi_save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0);
|
275
|
+
} else {
|
276
|
+
ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
|
277
|
+
}
|
278
|
+
#else
|
256
279
|
ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
|
280
|
+
#endif
|
257
281
|
rbffi_frame_pop(&frame);
|
258
282
|
|
259
283
|
rbffi_save_errno();
|
data/ext/ffi_c/compat.h
CHANGED
@@ -76,6 +76,7 @@ static void ffi_prep_args(char *stack,
|
|
76
76
|
void **p_argv;
|
77
77
|
char *argp;
|
78
78
|
ffi_type **p_arg;
|
79
|
+
int ecif_test;
|
79
80
|
|
80
81
|
#ifdef FFI_MIPS_N32
|
81
82
|
/* If more than 8 double words are used, the remainder go
|
@@ -92,10 +93,11 @@ static void ffi_prep_args(char *stack,
|
|
92
93
|
memset(stack, 0, bytes);
|
93
94
|
|
94
95
|
#ifdef FFI_MIPS_N32
|
95
|
-
|
96
|
+
ecif_test = ecif->cif->rstruct_flag != 0;
|
96
97
|
#else
|
97
|
-
|
98
|
+
ecif_test = ecif->cif->rtype->type == FFI_TYPE_STRUCT;
|
98
99
|
#endif
|
100
|
+
if (ecif_test)
|
99
101
|
{
|
100
102
|
*(ffi_arg *) argp = (ffi_arg) ecif->rvalue;
|
101
103
|
argp += sizeof(ffi_arg);
|
data/ext/ffi_c/rbffi_endian.h
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
#include <sys/types.h>
|
9
9
|
|
10
|
-
#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__)
|
10
|
+
#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) || defined(__HAIKU__)
|
11
11
|
# include <endian.h>
|
12
12
|
# if !defined(LITTLE_ENDIAN) && defined(__LITTLE_ENDIAN)
|
13
13
|
# define LITTLE_ENDIAN __LITTLE_ENDIAN
|
data/ffi.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.required_ruby_version = '>= 1.8.7'
|
18
18
|
s.add_development_dependency 'rake', '~> 10.1'
|
19
19
|
s.add_development_dependency 'rake-compiler', '~> 0.9'
|
20
|
-
s.add_development_dependency 'rake-compiler-dock', '~> 0.
|
20
|
+
s.add_development_dependency 'rake-compiler-dock', '~> 0.5.2'
|
21
21
|
s.add_development_dependency 'rspec', '~> 2.14.1'
|
22
22
|
s.add_development_dependency 'rubygems-tasks', "~> 0.2.4"
|
23
23
|
end
|
data/lib/ffi/autopointer.rb
CHANGED
@@ -34,27 +34,29 @@ module FFI
|
|
34
34
|
extend DataConverter
|
35
35
|
|
36
36
|
# @overload initialize(pointer, method)
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
37
|
+
# @param pointer [Pointer]
|
38
|
+
# @param method [Method]
|
39
|
+
# @return [self]
|
40
|
+
# The passed Method will be invoked at GC time.
|
41
41
|
# @overload initialize(pointer, proc)
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
42
|
+
# @param pointer [Pointer]
|
43
|
+
# @return [self]
|
44
|
+
# The passed Proc will be invoked at GC time (SEE WARNING BELOW!)
|
45
|
+
# @note WARNING: passing a proc _may_ cause your pointer to never be
|
46
|
+
# GC'd, unless you're careful to avoid trapping a reference to the
|
47
|
+
# pointer in the proc. See the test specs for examples.
|
48
48
|
# @overload initialize(pointer) { |p| ... }
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
49
|
+
# @param pointer [Pointer]
|
50
|
+
# @yieldparam [Pointer] p +pointer+ passed to the block
|
51
|
+
# @return [self]
|
52
|
+
# The passed block will be invoked at GC time.
|
53
|
+
# @note
|
54
|
+
# WARNING: passing a block will cause your pointer to never be GC'd.
|
55
|
+
# This is bad.
|
54
56
|
# @overload initialize(pointer)
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
57
|
+
# @param pointer [Pointer]
|
58
|
+
# @return [self]
|
59
|
+
# The pointer's release() class method will be invoked at GC time.
|
58
60
|
#
|
59
61
|
# @note The safest, and therefore preferred, calling
|
60
62
|
# idiom is to pass a Method as the second parameter. Example usage:
|
@@ -79,11 +81,15 @@ module FFI
|
|
79
81
|
|| ptr.kind_of?(MemoryPointer) || ptr.kind_of?(AutoPointer)
|
80
82
|
|
81
83
|
@releaser = if proc
|
82
|
-
|
84
|
+
if not proc.respond_to?(:call)
|
85
|
+
raise RuntimeError.new("proc must be callable")
|
86
|
+
end
|
83
87
|
CallableReleaser.new(ptr, proc)
|
84
88
|
|
85
89
|
else
|
86
|
-
|
90
|
+
if not self.class.respond_to?(:release)
|
91
|
+
raise RuntimeError.new("no release method defined")
|
92
|
+
end
|
87
93
|
DefaultReleaser.new(ptr, self.class)
|
88
94
|
end
|
89
95
|
|
@@ -143,16 +149,15 @@ module FFI
|
|
143
149
|
def call(*args)
|
144
150
|
release(@ptr) if @autorelease && @ptr
|
145
151
|
end
|
146
|
-
|
147
152
|
end
|
148
153
|
|
149
|
-
# DefaultReleaser is a {Releaser} used when an {AutoPointer} is defined
|
150
|
-
# or Method. In this case, the pointer to release must be of
|
151
|
-
# AutoPointer with a
|
154
|
+
# DefaultReleaser is a {Releaser} used when an {AutoPointer} is defined
|
155
|
+
# without Proc or Method. In this case, the pointer to release must be of
|
156
|
+
# a class derived from AutoPointer with a {release} class method.
|
152
157
|
class DefaultReleaser < Releaser
|
153
158
|
# @param [Pointer] ptr
|
154
159
|
# @return [nil]
|
155
|
-
# Release +ptr+
|
160
|
+
# Release +ptr+ using the {release} class method of its class.
|
156
161
|
def release(ptr)
|
157
162
|
@proc.release(ptr)
|
158
163
|
end
|
@@ -161,7 +166,9 @@ module FFI
|
|
161
166
|
# CallableReleaser is a {Releaser} used when an {AutoPointer} is defined with a
|
162
167
|
# Proc or a Method.
|
163
168
|
class CallableReleaser < Releaser
|
164
|
-
# Release +ptr+ by using Proc or Method defined at +ptr+
|
169
|
+
# Release +ptr+ by using Proc or Method defined at +ptr+
|
170
|
+
# {AutoPointer#initialize initialization}.
|
171
|
+
#
|
165
172
|
# @param [Pointer] ptr
|
166
173
|
# @return [nil]
|
167
174
|
def release(ptr)
|
@@ -175,7 +182,9 @@ module FFI
|
|
175
182
|
# @return [Type::POINTER]
|
176
183
|
# @raise {RuntimeError} if class does not implement a +#release+ method
|
177
184
|
def self.native_type
|
178
|
-
|
185
|
+
if not self.respond_to?(:release)
|
186
|
+
raise RuntimeError.new("no release method defined for #{self.inspect}")
|
187
|
+
end
|
179
188
|
Type::POINTER
|
180
189
|
end
|
181
190
|
|
data/lib/ffi/enum.rb
CHANGED
@@ -146,7 +146,7 @@ module FFI
|
|
146
146
|
def symbol_map
|
147
147
|
@kv_map
|
148
148
|
end
|
149
|
-
|
149
|
+
|
150
150
|
alias to_h symbol_map
|
151
151
|
alias to_hash symbol_map
|
152
152
|
|
@@ -168,7 +168,5 @@ module FFI
|
|
168
168
|
def from_native(val, ctx)
|
169
169
|
@vk_map[val] || val
|
170
170
|
end
|
171
|
-
|
172
171
|
end
|
173
|
-
|
174
172
|
end
|
data/lib/ffi/library.rb
CHANGED
@@ -109,6 +109,7 @@ module FFI
|
|
109
109
|
|
110
110
|
libnames.each do |libname|
|
111
111
|
begin
|
112
|
+
orig = libname
|
112
113
|
lib = FFI::DynamicLibrary.open(libname, lib_flags)
|
113
114
|
break if lib
|
114
115
|
|
@@ -121,10 +122,22 @@ module FFI
|
|
121
122
|
end
|
122
123
|
end
|
123
124
|
|
125
|
+
# TODO better library lookup logic
|
126
|
+
unless libname.start_with?("/")
|
127
|
+
path = ['/usr/lib/','/usr/local/lib/'].find do |pth|
|
128
|
+
File.exist?(pth + libname)
|
129
|
+
end
|
130
|
+
if path
|
131
|
+
libname = path + libname
|
132
|
+
retry
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
124
136
|
if ldscript
|
125
137
|
retry
|
126
138
|
else
|
127
|
-
|
139
|
+
libr = (orig == libname ? orig : "#{orig} #{libname}")
|
140
|
+
errors[libr] = ex
|
128
141
|
end
|
129
142
|
end
|
130
143
|
end
|
data/lib/ffi/pointer.rb
CHANGED
data/lib/ffi/struct.rb
CHANGED
@@ -341,7 +341,7 @@ module FFI
|
|
341
341
|
# @raise if Ruby 1.8
|
342
342
|
# Add hash +spec+ to +builder+.
|
343
343
|
def hash_layout(builder, spec)
|
344
|
-
raise "Ruby version not supported" if RUBY_VERSION =~
|
344
|
+
raise "Ruby version not supported" if RUBY_VERSION =~ /^1\.8\..*/
|
345
345
|
spec[0].each do |name, type|
|
346
346
|
builder.add name, find_field_type(type), nil
|
347
347
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'tempfile'
|
2
2
|
|
3
3
|
module FFI
|
4
|
-
|
4
|
+
|
5
5
|
# @private
|
6
6
|
class TypesGenerator
|
7
7
|
|
@@ -65,7 +65,7 @@ module FFI
|
|
65
65
|
typedefs = `#{cmd} #{io.path}`
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
code = ""
|
70
70
|
|
71
71
|
typedefs.each_line do |type|
|
@@ -73,11 +73,11 @@ module FFI
|
|
73
73
|
next unless type =~ /typedef/
|
74
74
|
# Ignore unions or structs
|
75
75
|
next if type =~ /union|struct/
|
76
|
-
|
76
|
+
|
77
77
|
# strip off the starting typedef and ending ;
|
78
78
|
type.gsub!(/^(.*typedef\s*)/, "")
|
79
79
|
type.gsub!(/\s*;\s*$/, "")
|
80
|
-
|
80
|
+
|
81
81
|
parts = type.split(/\s+/)
|
82
82
|
def_type = parts.join(" ")
|
83
83
|
|
@@ -99,7 +99,7 @@ module FFI
|
|
99
99
|
else
|
100
100
|
final_type = parts.pop
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
def_type = case type
|
104
104
|
when /__QI__/ then "char"
|
105
105
|
when /__HI__/ then "short"
|
@@ -114,7 +114,7 @@ module FFI
|
|
114
114
|
final_type = parts.pop
|
115
115
|
def_type = parts.join(" ")
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
if type = TYPE_MAP[def_type]
|
119
119
|
code << "rbx.platform.typedef.#{final_type} = #{type}\n"
|
120
120
|
TYPE_MAP[final_type] = TYPE_MAP[def_type]
|
@@ -129,7 +129,6 @@ module FFI
|
|
129
129
|
|
130
130
|
code
|
131
131
|
end
|
132
|
-
|
133
132
|
end
|
134
133
|
end
|
135
134
|
|
data/lib/ffi/types.rb
CHANGED
@@ -65,7 +65,6 @@ module FFI
|
|
65
65
|
|
66
66
|
elsif name.is_a?(DataConverter)
|
67
67
|
(type_map || TypeDefs)[name] = Type::Mapped.new(name)
|
68
|
-
|
69
68
|
else
|
70
69
|
raise TypeError, "unable to resolve type '#{name}'"
|
71
70
|
end
|
@@ -149,19 +148,24 @@ module FFI
|
|
149
148
|
:varargs => Type::VARARGS,
|
150
149
|
})
|
151
150
|
|
152
|
-
|
151
|
+
# This will convert a pointer to a Ruby string (just like `:string`), but
|
152
|
+
# also allow to work with the pointer itself. This is useful when you want
|
153
|
+
# a Ruby string already containing a copy of the data, but also the pointer
|
154
|
+
# to the data for you to do something with it, like freeing it, in case the
|
155
|
+
# library handed the memory to off to the caller (Ruby-FFI).
|
156
|
+
#
|
157
|
+
# It's {typedef}'d as +:strptr+.
|
153
158
|
class StrPtrConverter
|
154
159
|
extend DataConverter
|
155
160
|
native_type Type::POINTER
|
156
161
|
|
157
162
|
# @param [Pointer] val
|
158
|
-
# @param ctx
|
163
|
+
# @param ctx not used
|
159
164
|
# @return [Array(String, Pointer)]
|
160
165
|
# Returns a [ String, Pointer ] tuple so the C memory for the string can be freed
|
161
166
|
def self.from_native(val, ctx)
|
162
167
|
[ val.null? ? nil : val.get_string(0), val ]
|
163
168
|
end
|
164
|
-
|
165
169
|
end
|
166
170
|
|
167
171
|
typedef(StrPtrConverter, :strptr)
|
data/lib/ffi/version.rb
CHANGED
data/libtest/FunctionTest.c
CHANGED
data/libtest/VariadicTest.c
CHANGED
@@ -60,3 +60,40 @@ void pack_varargs(s64* buf, const char* fmt, ...)
|
|
60
60
|
va_end(ap);
|
61
61
|
}
|
62
62
|
|
63
|
+
int pack_varargs2(s64* buf, int retval, const char* fmt, ...)
|
64
|
+
{
|
65
|
+
va_list ap;
|
66
|
+
int c;
|
67
|
+
double d;
|
68
|
+
va_start(ap, fmt);
|
69
|
+
while ((c = *fmt++)) {
|
70
|
+
switch (c) {
|
71
|
+
case 'c':
|
72
|
+
case 's':
|
73
|
+
case 'i':
|
74
|
+
*buf++ = va_arg(ap, s32);
|
75
|
+
break;
|
76
|
+
case 'l':
|
77
|
+
*buf++ = va_arg(ap, long);
|
78
|
+
break;
|
79
|
+
case 'j':
|
80
|
+
*buf++ = va_arg(ap, s64);
|
81
|
+
break;
|
82
|
+
case 'f':
|
83
|
+
case 'd':
|
84
|
+
d = va_arg(ap, double);
|
85
|
+
memcpy(buf++, &d, sizeof(d));
|
86
|
+
break;
|
87
|
+
case 'C':
|
88
|
+
case 'S':
|
89
|
+
case 'I':
|
90
|
+
*buf++ = va_arg(ap, u32);
|
91
|
+
break;
|
92
|
+
case 'L':
|
93
|
+
*buf++ = va_arg(ap, unsigned long);
|
94
|
+
break;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
va_end(ap);
|
98
|
+
return retval + 1;
|
99
|
+
}
|
@@ -50,6 +50,21 @@ P(D, double);
|
|
50
50
|
P(P, const void*);
|
51
51
|
P(UL, unsigned long);
|
52
52
|
|
53
|
+
#if defined(_WIN32) && !defined(_WIN64)
|
54
|
+
bool __stdcall testClosureStdcall(long *a1, void __stdcall(*closure)(void *, long), long a2) { \
|
55
|
+
void* sp_pre;
|
56
|
+
void* sp_post;
|
57
|
+
|
58
|
+
asm volatile (" movl %%esp,%0" : "=g" (sp_pre));
|
59
|
+
(*closure)(a1, a2);
|
60
|
+
asm volatile (" movl %%esp,%0" : "=g" (sp_post));
|
61
|
+
|
62
|
+
/* %esp before pushing parameters on the stack and after the call returns
|
63
|
+
* should be equal, if both sides respects the stdcall convention */
|
64
|
+
return sp_pre == sp_post;
|
65
|
+
}
|
66
|
+
#endif
|
67
|
+
|
53
68
|
void testOptionalClosureBrV(void (*closure)(char), char a1)
|
54
69
|
{
|
55
70
|
if (closure) {
|