ffi 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ffi might be problematic. Click here for more details.
- data/Rakefile +4 -4
- data/ext/ffi_c/AbstractMemory.c +367 -14
- data/ext/ffi_c/AbstractMemory.h +4 -0
- data/ext/ffi_c/ArrayType.c +28 -0
- data/ext/ffi_c/Buffer.c +101 -25
- data/ext/ffi_c/Call.c +8 -5
- data/ext/ffi_c/ClosurePool.c +9 -8
- data/ext/ffi_c/DataConverter.c +29 -0
- data/ext/ffi_c/DynamicLibrary.c +64 -1
- data/ext/ffi_c/Function.c +111 -10
- data/ext/ffi_c/FunctionInfo.c +13 -1
- data/ext/ffi_c/LastError.c +16 -0
- data/ext/ffi_c/MappedType.c +22 -0
- data/ext/ffi_c/MemoryPointer.c +11 -1
- data/ext/ffi_c/MethodHandle.c +18 -11
- data/ext/ffi_c/Platform.c +9 -3
- data/ext/ffi_c/Pointer.c +98 -0
- data/ext/ffi_c/Struct.c +4 -4
- data/ext/ffi_c/Struct.h +2 -1
- data/ext/ffi_c/StructLayout.c +2 -2
- data/ext/ffi_c/Thread.c +124 -1
- data/ext/ffi_c/Type.c +108 -17
- data/ext/ffi_c/Types.c +9 -2
- data/ext/ffi_c/Variadic.c +5 -4
- data/ext/ffi_c/compat.h +8 -0
- data/ext/ffi_c/endian.h +7 -1
- data/ext/ffi_c/extconf.rb +46 -35
- data/ext/ffi_c/ffi.c +5 -0
- data/ext/ffi_c/libffi.darwin.mk +15 -15
- data/ext/ffi_c/libffi.gnu.mk +3 -3
- data/ext/ffi_c/libffi.mk +4 -4
- data/lib/ffi.rb +13 -9
- data/lib/ffi/autopointer.rb +88 -26
- data/lib/ffi/enum.rb +42 -0
- data/lib/ffi/errno.rb +6 -1
- data/lib/ffi/ffi.rb +1 -0
- data/lib/ffi/io.rb +13 -2
- data/lib/ffi/library.rb +212 -19
- data/lib/ffi/memorypointer.rb +1 -33
- data/lib/ffi/platform.rb +23 -7
- data/lib/ffi/platform/i386-freebsd/types.conf +152 -0
- data/lib/ffi/platform/i386-netbsd/types.conf +126 -0
- data/lib/ffi/platform/x86_64-freebsd/types.conf +126 -0
- data/lib/ffi/platform/x86_64-netbsd/types.conf +126 -0
- data/lib/ffi/pointer.rb +44 -0
- data/lib/ffi/struct.rb +1 -1
- data/lib/ffi/struct_layout_builder.rb +2 -1
- data/lib/ffi/tools/const_generator.rb +72 -17
- data/lib/ffi/types.rb +21 -1
- data/spec/ffi/rbx/memory_pointer_spec.rb +4 -2
- data/spec/ffi/struct_spec.rb +10 -0
- data/spec/ffi/typedef_spec.rb +11 -0
- data/tasks/extension.rake +0 -1
- data/tasks/gem.rake +0 -1
- data/tasks/yard.rake +11 -0
- metadata +15 -8
data/ext/ffi_c/libffi.gnu.mk
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
|
7
7
|
# Tack the extra deps onto the autogenerated variables
|
8
|
-
INCFLAGS += -I$(LIBFFI_BUILD_DIR)/include
|
8
|
+
INCFLAGS += -I"$(LIBFFI_BUILD_DIR)"/include
|
9
9
|
LOCAL_LIBS += $(LIBFFI)
|
10
10
|
BUILD_DIR = $(shell pwd)
|
11
11
|
LIBFFI_CFLAGS = $(FFI_MMAP_EXEC)
|
@@ -17,8 +17,8 @@ else
|
|
17
17
|
LIBFFI_SRC_DIR := $(abspath $(srcdir)/libffi)
|
18
18
|
endif
|
19
19
|
|
20
|
-
LIBFFI = $(LIBFFI_BUILD_DIR)/.libs/libffi_convenience.a
|
21
|
-
LIBFFI_CONFIGURE = $(LIBFFI_SRC_DIR)/configure --disable-static \
|
20
|
+
LIBFFI = "$(LIBFFI_BUILD_DIR)"/.libs/libffi_convenience.a
|
21
|
+
LIBFFI_CONFIGURE = "$(LIBFFI_SRC_DIR)"/configure --disable-static \
|
22
22
|
--with-pic=yes --disable-dependency-tracking
|
23
23
|
|
24
24
|
$(OBJS): $(LIBFFI)
|
data/ext/ffi_c/libffi.mk
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
include ${srcdir}/libffi.gnu.mk
|
4
4
|
|
5
5
|
$(LIBFFI):
|
6
|
-
@mkdir -p $(LIBFFI_BUILD_DIR)
|
7
|
-
@if [ ! -f $(LIBFFI_BUILD_DIR)/Makefile ]; then \
|
6
|
+
@mkdir -p "$(LIBFFI_BUILD_DIR)"
|
7
|
+
@if [ ! -f "$(LIBFFI_BUILD_DIR)"/Makefile ]; then \
|
8
8
|
echo "Configuring libffi"; \
|
9
|
-
cd $(LIBFFI_BUILD_DIR) && \
|
9
|
+
cd "$(LIBFFI_BUILD_DIR)" && \
|
10
10
|
/usr/bin/env CFLAGS="$(LIBFFI_CFLAGS)" \
|
11
11
|
/bin/sh $(LIBFFI_CONFIGURE) $(LIBFFI_HOST) > /dev/null; \
|
12
12
|
fi
|
13
|
-
|
13
|
+
$(MAKE) -C "$(LIBFFI_BUILD_DIR)"
|
data/lib/ffi.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
|
2
|
+
begin
|
3
|
+
if RUBY_VERSION =~ /1.8/
|
4
|
+
require '1.8/ffi_c'
|
5
|
+
elsif RUBY_VERSION =~ /1.9/
|
6
|
+
require '1.9/ffi_c'
|
7
|
+
else
|
8
|
+
require 'ffi_c'
|
9
|
+
end
|
10
|
+
rescue Exception
|
11
|
+
require 'ffi_c'
|
6
12
|
end
|
7
|
-
rescue Exception
|
8
|
-
require 'ffi_c'
|
9
|
-
end
|
10
13
|
|
11
|
-
require 'ffi/ffi'
|
14
|
+
require 'ffi/ffi'
|
15
|
+
end
|
data/lib/ffi/autopointer.rb
CHANGED
@@ -23,17 +23,31 @@ module FFI
|
|
23
23
|
class AutoPointer < Pointer
|
24
24
|
extend DataConverter
|
25
25
|
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
26
|
+
# @overload initialize(pointer, method)
|
27
|
+
# @param [Pointer] pointer
|
28
|
+
# @param [Method] method
|
29
|
+
# @return [self]
|
30
|
+
# The passed Method will be invoked at GC time.
|
31
|
+
# @overload initialize(pointer, proc)
|
32
|
+
# @param [Pointer] pointer
|
33
|
+
# @return [self]
|
34
|
+
# The passed Proc will be invoked at GC time (SEE WARNING BELOW!)
|
35
|
+
# @note WARNING: passing a proc _may_ cause your pointer to never be GC'd, unless you're
|
36
|
+
# careful to avoid trapping a reference to the pointer in the proc. See the test
|
37
|
+
# specs for examples.
|
38
|
+
# @overload initialize(pointer) { |p| ... }
|
39
|
+
# @param [Pointer] pointer
|
40
|
+
# @yieldparam [Pointer] p +pointer+ passed to the block
|
41
|
+
# @return [self]
|
42
|
+
# The passed block will be invoked at GC time.
|
43
|
+
# @note WARNING: passing a block will cause your pointer to never be GC'd. This is bad.
|
44
|
+
# @overload initialize(pointer)
|
45
|
+
# @param [Pointer] pointer
|
46
|
+
# @return [self]
|
47
|
+
# The pointer's release() class method will be invoked at GC time.
|
31
48
|
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# Please note that the safest, and therefore preferred, calling
|
36
|
-
# idiom is to pass a Method as the second parameter. Example usage:
|
49
|
+
# @note The safest, and therefore preferred, calling
|
50
|
+
# idiom is to pass a Method as the second parameter. Example usage:
|
37
51
|
#
|
38
52
|
# class PointerHelper
|
39
53
|
# def self.release(pointer)
|
@@ -43,12 +57,12 @@ module FFI
|
|
43
57
|
#
|
44
58
|
# p = AutoPointer.new(other_pointer, PointerHelper.method(:release))
|
45
59
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# The last calling idiom (only one parameter) is generally only
|
49
|
-
# going to be useful if you subclass AutoPointer, and override
|
50
|
-
# release(), which by default does nothing.
|
60
|
+
# The above code will cause PointerHelper#release to be invoked at GC time.
|
51
61
|
#
|
62
|
+
# @note
|
63
|
+
# The last calling idiom (only one parameter) is generally only
|
64
|
+
# going to be useful if you subclass {AutoPointer}, and override
|
65
|
+
# #release, which by default does nothing.
|
52
66
|
def initialize(ptr, proc=nil, &block)
|
53
67
|
super(ptr)
|
54
68
|
raise TypeError, "Invalid pointer" if ptr.nil? || !ptr.kind_of?(Pointer) \
|
@@ -67,52 +81,100 @@ module FFI
|
|
67
81
|
self
|
68
82
|
end
|
69
83
|
|
84
|
+
# @return [nil]
|
85
|
+
# Free the pointer.
|
70
86
|
def free
|
71
87
|
@releaser.free
|
72
88
|
end
|
73
89
|
|
90
|
+
# @param [Boolean] autorelease
|
91
|
+
# @return [Boolean] +autorelease+
|
92
|
+
# Set +autorelease+ property. See {Pointer Autorelease section at Pointer}.
|
74
93
|
def autorelease=(autorelease)
|
75
94
|
@releaser.autorelease=(autorelease)
|
76
95
|
end
|
77
96
|
|
97
|
+
# @abstract Base class for {AutoPointer}'s releasers.
|
98
|
+
#
|
99
|
+
# All subclasses of Releaser should define a +#release(ptr)+ method.
|
100
|
+
# A releaser is an object in charge of release an {AutoPointer}.
|
78
101
|
class Releaser
|
102
|
+
# @param [Pointer] ptr
|
103
|
+
# @param [#call] proc
|
104
|
+
# @return [nil]
|
105
|
+
# A new instance of Releaser.
|
79
106
|
def initialize(ptr, proc)
|
80
107
|
@ptr = ptr
|
81
108
|
@proc = proc
|
82
109
|
@autorelease = true
|
83
110
|
end
|
84
111
|
|
112
|
+
# @return [nil]
|
113
|
+
# Free pointer.
|
85
114
|
def free
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
115
|
+
if @ptr
|
116
|
+
release(@ptr)
|
117
|
+
@autorelease = false
|
118
|
+
@ptr = nil
|
119
|
+
@proc = nil
|
120
|
+
end
|
90
121
|
end
|
91
122
|
|
123
|
+
# @param [Boolean] autorelease
|
124
|
+
# @return [Boolean] autorelease
|
125
|
+
# Set +autorelease+ attribute for pointer managed by Releaser.
|
92
126
|
def autorelease=(autorelease)
|
93
|
-
|
94
|
-
@autorelease = autorelease
|
127
|
+
@autorelease = autorelease if @ptr
|
95
128
|
end
|
96
|
-
|
129
|
+
|
130
|
+
# @param args
|
131
|
+
# Release pointer if +autorelease+ is set.
|
132
|
+
def call(*args)
|
133
|
+
release(@ptr) if @autorelease && @ptr
|
134
|
+
end
|
135
|
+
|
97
136
|
end
|
98
137
|
|
138
|
+
# DefaultReleaser is a {Releaser} used when an {AutoPointer} is defined without Proc
|
139
|
+
# or Method. In this case, the pointer to release must be of a class derived from
|
140
|
+
# AutoPointer with a +#release+ class method.
|
99
141
|
class DefaultReleaser < Releaser
|
100
|
-
|
101
|
-
|
142
|
+
# @param [Pointer] ptr
|
143
|
+
# @return [nil]
|
144
|
+
# Release +ptr+ by using his #release class method.
|
145
|
+
def release(ptr)
|
146
|
+
@proc.release(ptr)
|
102
147
|
end
|
103
148
|
end
|
104
149
|
|
150
|
+
# CallableReleaser is a {Releaser} used when an {AutoPointer} is defined with a
|
151
|
+
# Proc or a Method.
|
105
152
|
class CallableReleaser < Releaser
|
106
|
-
|
107
|
-
|
153
|
+
# @param [Pointer] ptr
|
154
|
+
# @return [nil]
|
155
|
+
# Release +ptr+ by using Proc or Method defined at +ptr+ {AutoPointer#initialize initialization}.
|
156
|
+
def release(ptr)
|
157
|
+
@proc.call(ptr)
|
108
158
|
end
|
109
159
|
end
|
110
160
|
|
161
|
+
# Return native type of AutoPointer.
|
162
|
+
#
|
163
|
+
# Override {DataConverter#native_type}.
|
164
|
+
# @return [Type::POINTER]
|
165
|
+
# @raise {RuntimeError} if class does not implement a +#release+ method
|
111
166
|
def self.native_type
|
112
167
|
raise RuntimeError.new("no release method defined for #{self.inspect}") unless self.respond_to?(:release)
|
113
168
|
Type::POINTER
|
114
169
|
end
|
115
170
|
|
171
|
+
# Create a new AutoPointer.
|
172
|
+
#
|
173
|
+
# Override {DataConverter#from_native}.
|
174
|
+
# @overload self.from_native(ptr, ctx)
|
175
|
+
# @param [Pointer] ptr
|
176
|
+
# @param ctx not used. Please set +nil+.
|
177
|
+
# @return [AutoPointer]
|
116
178
|
def self.from_native(val, ctx)
|
117
179
|
self.new(val)
|
118
180
|
end
|
data/lib/ffi/enum.rb
CHANGED
@@ -21,20 +21,27 @@
|
|
21
21
|
|
22
22
|
module FFI
|
23
23
|
|
24
|
+
# An instance of this class permits to manage {Enum}s. In fact, Enums is a collection of {Enum}s.
|
24
25
|
class Enums
|
25
26
|
|
27
|
+
# @return [nil]
|
26
28
|
def initialize
|
27
29
|
@all_enums = Array.new
|
28
30
|
@tagged_enums = Hash.new
|
29
31
|
@symbol_map = Hash.new
|
30
32
|
end
|
31
33
|
|
34
|
+
# @param [Enum] enum
|
35
|
+
# Add an {Enum} to the collection.
|
32
36
|
def <<(enum)
|
33
37
|
@all_enums << enum
|
34
38
|
@tagged_enums[enum.tag] = enum unless enum.tag.nil?
|
35
39
|
@symbol_map.merge!(enum.symbol_map)
|
36
40
|
end
|
37
41
|
|
42
|
+
# @param query enum tag or part of an enum name
|
43
|
+
# @return [Enum]
|
44
|
+
# Find a {Enum} in collection.
|
38
45
|
def find(query)
|
39
46
|
if @tagged_enums.has_key?(query)
|
40
47
|
@tagged_enums[query]
|
@@ -43,17 +50,33 @@ module FFI
|
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
53
|
+
# @param symbol a symbol to find in merge symbol maps of all enums.
|
54
|
+
# @return a symbol
|
46
55
|
def __map_symbol(symbol)
|
47
56
|
@symbol_map[symbol]
|
48
57
|
end
|
49
58
|
|
50
59
|
end
|
51
60
|
|
61
|
+
# Represents a C enum.
|
62
|
+
#
|
63
|
+
# For a C enum:
|
64
|
+
# enum fruits {
|
65
|
+
# apple,
|
66
|
+
# banana,
|
67
|
+
# orange,
|
68
|
+
# pineapple
|
69
|
+
# };
|
70
|
+
# are defined this vocabulary:
|
71
|
+
# * a _symbol_ is a word from the enumeration (ie. _apple_, by example);
|
72
|
+
# * a _value_ is the value of a symbol in the enumeration (by example, apple has value _0_ and banana _1_).
|
52
73
|
class Enum
|
53
74
|
include DataConverter
|
54
75
|
|
55
76
|
attr_reader :tag
|
56
77
|
|
78
|
+
# @param [nil, Enumerable] info
|
79
|
+
# @param tag enum tag
|
57
80
|
def initialize(info, tag=nil)
|
58
81
|
@tag = tag
|
59
82
|
@kv_map = Hash.new
|
@@ -75,10 +98,20 @@ module FFI
|
|
75
98
|
@vk_map = Hash[@kv_map.map{|k,v| [v,k]}]
|
76
99
|
end
|
77
100
|
|
101
|
+
# @return [Array] enum symbol names
|
78
102
|
def symbols
|
79
103
|
@kv_map.keys
|
80
104
|
end
|
81
105
|
|
106
|
+
# Get a symbol or a value from the enum.
|
107
|
+
# @overload [](query)
|
108
|
+
# Get enum value from symbol.
|
109
|
+
# @param [Symbol] query
|
110
|
+
# @return [Integer]
|
111
|
+
# @overload [](query)
|
112
|
+
# Get enum symbol from value.
|
113
|
+
# @param [Integer] query
|
114
|
+
# @return [Symbol]
|
82
115
|
def [](query)
|
83
116
|
case query
|
84
117
|
when Symbol
|
@@ -89,6 +122,8 @@ module FFI
|
|
89
122
|
end
|
90
123
|
alias find []
|
91
124
|
|
125
|
+
# Get the symbol map.
|
126
|
+
# @return [Hash]
|
92
127
|
def symbol_map
|
93
128
|
@kv_map
|
94
129
|
end
|
@@ -96,10 +131,15 @@ module FFI
|
|
96
131
|
alias to_h symbol_map
|
97
132
|
alias to_hash symbol_map
|
98
133
|
|
134
|
+
# Get native type of Enum
|
135
|
+
# @return [Type::INT]
|
99
136
|
def native_type
|
100
137
|
Type::INT
|
101
138
|
end
|
102
139
|
|
140
|
+
# @param [Symbol, Integer, #to_int] val
|
141
|
+
# @param ctx unused
|
142
|
+
# @return [Integer] value of a enum symbol
|
103
143
|
def to_native(val, ctx)
|
104
144
|
@kv_map[val] || if val.is_a?(Integer)
|
105
145
|
val
|
@@ -110,6 +150,8 @@ module FFI
|
|
110
150
|
end
|
111
151
|
end
|
112
152
|
|
153
|
+
# @param val
|
154
|
+
# @return symbol name if it exists for +val+.
|
113
155
|
def from_native(val, ctx)
|
114
156
|
@vk_map[val] || val
|
115
157
|
end
|
data/lib/ffi/errno.rb
CHANGED
@@ -19,10 +19,15 @@
|
|
19
19
|
#
|
20
20
|
|
21
21
|
module FFI
|
22
|
+
# @return (see FFI::LastError.error)
|
23
|
+
# @see FFI::LastError.error
|
22
24
|
def self.errno
|
23
25
|
FFI::LastError.error
|
24
26
|
end
|
27
|
+
# @param error (see FFI::LastError.error=)
|
28
|
+
# @return (see FFI::LastError.error=)
|
29
|
+
# @see FFI::LastError.error=
|
25
30
|
def self.errno=(error)
|
26
31
|
FFI::LastError.error = error
|
27
32
|
end
|
28
|
-
end
|
33
|
+
end
|
data/lib/ffi/ffi.rb
CHANGED
data/lib/ffi/io.rb
CHANGED
@@ -19,15 +19,26 @@
|
|
19
19
|
#
|
20
20
|
|
21
21
|
module FFI
|
22
|
+
|
23
|
+
# This module implements a couple of class methods to play with IO.
|
22
24
|
module IO
|
25
|
+
# @param [Integer] fd file decriptor
|
26
|
+
# @param [String] mode mode string
|
27
|
+
# @return [::IO]
|
28
|
+
# Synonym for IO::for_fd.
|
23
29
|
def self.for_fd(fd, mode = "r")
|
24
30
|
::IO.for_fd(fd, mode)
|
25
31
|
end
|
26
32
|
|
33
|
+
# @param [#read] io io to read from
|
34
|
+
# @param [AbstractMemory] buf destination for data read from +io+
|
35
|
+
# @param [nil, Numeric] len maximul number of bytes to read from +io+. If +nil+,
|
36
|
+
# read until end of file.
|
37
|
+
# @return [Numeric] length really read, in bytes
|
27
38
|
#
|
28
|
-
# A version of IO#read that reads into a native buffer
|
39
|
+
# A version of IO#read that reads data from an IO and put then into a native buffer.
|
29
40
|
#
|
30
|
-
# This will be optimized at some future time to eliminate the double copy
|
41
|
+
# This will be optimized at some future time to eliminate the double copy.
|
31
42
|
#
|
32
43
|
def self.native_read(io, buf, len)
|
33
44
|
tmp = io.read(len)
|
data/lib/ffi/library.rb
CHANGED
@@ -21,6 +21,16 @@
|
|
21
21
|
module FFI
|
22
22
|
CURRENT_PROCESS = USE_THIS_PROCESS_AS_LIBRARY = Object.new
|
23
23
|
|
24
|
+
# @param [#to_s] lib library name
|
25
|
+
# @return [String] library name formatted for current platform
|
26
|
+
# Transform a generic library name to a platform library name
|
27
|
+
# @example
|
28
|
+
# # Linux
|
29
|
+
# FFI.map_library_name 'c' # -> "libc.so.6"
|
30
|
+
# FFI.map_library_name 'jpeg' # -> "libjpeg.so"
|
31
|
+
# # Windows
|
32
|
+
# FFI.map_library_name 'c' # -> "msvcrt.dll"
|
33
|
+
# FFI.map_library_name 'jpeg' # -> "jpeg.dll"
|
24
34
|
def self.map_library_name(lib)
|
25
35
|
# Mangle the library name to reflect the native library naming conventions
|
26
36
|
lib = lib.to_s unless lib.kind_of?(String)
|
@@ -28,27 +38,51 @@ module FFI
|
|
28
38
|
|
29
39
|
if lib && File.basename(lib) == lib
|
30
40
|
lib = Platform::LIBPREFIX + lib unless lib =~ /^#{Platform::LIBPREFIX}/
|
31
|
-
r = Platform::
|
41
|
+
r = Platform::IS_GNU ? "\\.so($|\\.[1234567890]+)" : "\\.#{Platform::LIBSUFFIX}$"
|
32
42
|
lib += ".#{Platform::LIBSUFFIX}" unless lib =~ /#{r}/
|
33
43
|
end
|
34
44
|
|
35
45
|
lib
|
36
46
|
end
|
37
47
|
|
48
|
+
# Exception raised when a function is not found in libraries
|
38
49
|
class NotFoundError < LoadError
|
39
50
|
def initialize(function, *libraries)
|
40
51
|
super("Function '#{function}' not found in [#{libraries[0].nil? ? 'current process' : libraries.join(", ")}]")
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
55
|
+
# This module is the base to use native functions.
|
56
|
+
#
|
57
|
+
# A basic usage may be:
|
58
|
+
# require 'ffi'
|
59
|
+
#
|
60
|
+
# module Hello
|
61
|
+
# extend FFI::Library
|
62
|
+
# ffi_lib FFI::Library::LIBC
|
63
|
+
# attach_function 'puts', [ :string ], :int
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# Hello.puts("Hello, World")
|
67
|
+
#
|
68
|
+
#
|
44
69
|
module Library
|
45
70
|
CURRENT_PROCESS = FFI::CURRENT_PROCESS
|
46
71
|
LIBC = FFI::Platform::LIBC
|
47
72
|
|
73
|
+
# @param mod extended object
|
74
|
+
# @return [nil]
|
75
|
+
# @raise {RuntimeError} if +mod+ is not a Module
|
76
|
+
# Test if extended object is a Module. If not, raise RuntimeError.
|
48
77
|
def self.extended(mod)
|
49
78
|
raise RuntimeError.new("must only be extended by module") unless mod.kind_of?(Module)
|
50
79
|
end
|
51
80
|
|
81
|
+
|
82
|
+
# @param [Array] names names of libraries to load
|
83
|
+
# @return [Array<DynamicLibrary>]
|
84
|
+
# @raise {LoadError} if a library cannot be opened
|
85
|
+
# Load native libraries.
|
52
86
|
def ffi_lib(*names)
|
53
87
|
lib_flags = defined?(@ffi_lib_flags) ? @ffi_lib_flags : FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL
|
54
88
|
ffi_libs = names.map do |name|
|
@@ -67,12 +101,24 @@ module FFI
|
|
67
101
|
break if lib
|
68
102
|
|
69
103
|
rescue Exception => ex
|
70
|
-
|
104
|
+
ldscript = false
|
105
|
+
if ex.message =~ /(([^ \t()])+\.so([^ \t:()])*):([ \t])*invalid ELF header/
|
106
|
+
if File.read($1) =~ /GROUP *\( *([^ \)]+) *\)/
|
107
|
+
libname = $1
|
108
|
+
ldscript = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if ldscript
|
113
|
+
retry
|
114
|
+
else
|
115
|
+
errors[libname] = ex
|
116
|
+
end
|
71
117
|
end
|
72
118
|
end
|
73
119
|
|
74
120
|
if lib.nil?
|
75
|
-
raise LoadError.new(errors.values.join(
|
121
|
+
raise LoadError.new(errors.values.join(".\n"))
|
76
122
|
end
|
77
123
|
|
78
124
|
# return the found lib
|
@@ -83,17 +129,30 @@ module FFI
|
|
83
129
|
@ffi_libs = ffi_libs
|
84
130
|
end
|
85
131
|
|
86
|
-
|
132
|
+
# Set the calling convention for {#attach_function} and {#callback}
|
133
|
+
#
|
134
|
+
# @see http://en.wikipedia.org/wiki/Stdcall#stdcall
|
135
|
+
# @note +:stdcall+ is typically used for attaching Windows API functions
|
136
|
+
#
|
137
|
+
# @param [Symbol] convention one of +:default+, +:stdcall+
|
138
|
+
# @return [Symbol] the new calling convention
|
87
139
|
def ffi_convention(convention)
|
88
140
|
@ffi_convention = convention
|
89
141
|
end
|
90
142
|
|
91
|
-
|
143
|
+
# @see #ffi_lib
|
144
|
+
# @return [Array<FFI::DynamicLibrary>] array of currently loaded FFI libraries
|
145
|
+
# @raise [LoadError] if no libraries have been loaded (using {#ffi_lib})
|
146
|
+
# Get FFI libraries loaded using {#ffi_lib}.
|
92
147
|
def ffi_libraries
|
93
148
|
raise LoadError.new("no library specified") if !defined?(@ffi_libs) || @ffi_libs.empty?
|
94
149
|
@ffi_libs
|
95
150
|
end
|
96
151
|
|
152
|
+
# Flags used in {#ffi_lib}.
|
153
|
+
#
|
154
|
+
# This map allows you to supply symbols to {#ffi_lib_flags} instead of
|
155
|
+
# the actual constants.
|
97
156
|
FlagsMap = {
|
98
157
|
:global => DynamicLibrary::RTLD_GLOBAL,
|
99
158
|
:local => DynamicLibrary::RTLD_LOCAL,
|
@@ -101,15 +160,53 @@ module FFI
|
|
101
160
|
:now => DynamicLibrary::RTLD_NOW
|
102
161
|
}
|
103
162
|
|
163
|
+
# Sets library flags for {#ffi_lib}.
|
164
|
+
#
|
165
|
+
# @example
|
166
|
+
# ffi_lib_flags(:lazy, :local) # => 5
|
167
|
+
#
|
168
|
+
# @param [Symbol, …] flags (see {FlagsMap})
|
169
|
+
# @return [Fixnum] the new value
|
104
170
|
def ffi_lib_flags(*flags)
|
105
171
|
@ffi_lib_flags = flags.inject(0) { |result, f| result | FlagsMap[f] }
|
106
172
|
end
|
107
173
|
|
108
|
-
|
174
|
+
|
109
175
|
##
|
110
|
-
#
|
176
|
+
# @overload attach_function(func, args, returns, options = {})
|
177
|
+
# @example attach function without an explicit name
|
178
|
+
# module Foo
|
179
|
+
# extend FFI::Library
|
180
|
+
# ffi_lib FFI::Library::LIBC
|
181
|
+
# attach_function :malloc, [:size_t], :pointer
|
182
|
+
# end
|
183
|
+
# # now callable via Foo.malloc
|
184
|
+
# @overload attach_function(name, func, args, returns, options = {})
|
185
|
+
# @example attach function with an explicit name
|
186
|
+
# module Bar
|
187
|
+
# extend FFI::Library
|
188
|
+
# ffi_lib FFI::Library::LIBC
|
189
|
+
# attach_function :c_malloc, :malloc, [:size_t], :pointer
|
190
|
+
# end
|
191
|
+
# # now callable via Bar.c_malloc
|
192
|
+
#
|
193
|
+
# Attach C function +func+ to this module.
|
194
|
+
#
|
111
195
|
#
|
112
|
-
|
196
|
+
# @param [#to_s] name name of ruby method to attach as
|
197
|
+
# @param [#to_s] func name of C function to attach
|
198
|
+
# @param [Array<Symbol>] args an array of types
|
199
|
+
# @param [Symbol] returns type of return value
|
200
|
+
# @option options [Boolean] :blocking (@blocking) set to true if the C function is a blocking call
|
201
|
+
# @option options [Symbol] :convention (:default) calling convention (see {#ffi_convention})
|
202
|
+
# @option options [FFI::Enums] :enums
|
203
|
+
# @option options [Hash] :type_map
|
204
|
+
#
|
205
|
+
# @return [FFI::VariadicInvoker]
|
206
|
+
#
|
207
|
+
# @raise [FFI::NotFoundError] if +func+ cannot be found in the attached libraries (see {#ffi_lib})
|
208
|
+
def attach_function(name, func, args, returns = nil, options = nil)
|
209
|
+
mname, a2, a3, a4, a5 = name, func, args, returns, options
|
113
210
|
cname, arg_types, ret_type, opts = (a4 && (a2.is_a?(String) || a2.is_a?(Symbol))) ? [ a2, a3, a4, a5 ] : [ mname.to_s, a2, a3, a4 ]
|
114
211
|
|
115
212
|
# Convert :foo to the native type
|
@@ -120,7 +217,7 @@ module FFI
|
|
120
217
|
:blocking => defined?(@blocking) && @blocking,
|
121
218
|
:enums => defined?(@ffi_enums) ? @ffi_enums : nil,
|
122
219
|
}
|
123
|
-
|
220
|
+
|
124
221
|
@blocking = false
|
125
222
|
options.merge!(opts) if opts && opts.is_a?(Hash)
|
126
223
|
|
@@ -129,7 +226,10 @@ module FFI
|
|
129
226
|
ffi_libraries.each do |lib|
|
130
227
|
if invokers.empty?
|
131
228
|
begin
|
132
|
-
function =
|
229
|
+
function = nil
|
230
|
+
function_names(cname, arg_types).find do |name|
|
231
|
+
function = lib.find_function(name)
|
232
|
+
end
|
133
233
|
raise LoadError unless function
|
134
234
|
|
135
235
|
invokers << if arg_types.length > 0 && arg_types[arg_types.length - 1] == FFI::NativeType::VARARGS
|
@@ -150,7 +250,56 @@ module FFI
|
|
150
250
|
invoker
|
151
251
|
end
|
152
252
|
|
253
|
+
# @param [#to_s] name function name
|
254
|
+
# @param [Array] arg_types function's argument types
|
255
|
+
# @return [Array<String>]
|
256
|
+
# This function returns a list of possible names to lookup.
|
257
|
+
# @note Function names on windows may be decorated if they are using stdcall. See
|
258
|
+
# * http://en.wikipedia.org/wiki/Name_mangling#C_name_decoration_in_Microsoft_Windows
|
259
|
+
# * http://msdn.microsoft.com/en-us/library/zxk0tw93%28v=VS.100%29.aspx
|
260
|
+
# * http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions#STDCALL
|
261
|
+
# Note that decorated names can be overridden via def files. Also note that the
|
262
|
+
# windows api, although using, doesn't have decorated names.
|
263
|
+
def function_names(name, arg_types)
|
264
|
+
result = [name.to_s]
|
265
|
+
if @ffi_convention == :stdcall
|
266
|
+
# Get the size of each parameter
|
267
|
+
size = arg_types.inject(0) do |mem, arg|
|
268
|
+
mem + arg.size
|
269
|
+
end
|
270
|
+
|
271
|
+
# Next, the size must be a multiple of 4
|
272
|
+
size += (4 - size) % 4
|
273
|
+
|
274
|
+
result << "_#{name.to_s}@#{size}" # win32
|
275
|
+
result << "#{name.to_s}@#{size}" # win64
|
276
|
+
end
|
277
|
+
result
|
278
|
+
end
|
153
279
|
|
280
|
+
# @overload attach_variable(mname, cname, type)
|
281
|
+
# @example
|
282
|
+
# module Bar
|
283
|
+
# extend FFI::Library
|
284
|
+
# ffi_lib 'my_lib'
|
285
|
+
# attach_variable :c_myvar, :myvar, :long
|
286
|
+
# end
|
287
|
+
# # now callable via Bar.c_myvar
|
288
|
+
# @overload attach_variable(cname, type)
|
289
|
+
# @example
|
290
|
+
# module Bar
|
291
|
+
# extend FFI::Library
|
292
|
+
# ffi_lib 'my_lib'
|
293
|
+
# attach_variable :myvar, :long
|
294
|
+
# end
|
295
|
+
# # now callable via Bar.myvar
|
296
|
+
# @param [#to_s] mname name of ruby method to attach as
|
297
|
+
# @param [#to_s] cname name of C variable to attach
|
298
|
+
# @param [DataConverter, Struct, Symbol, Type] type C varaible's type
|
299
|
+
# @return [DynamicLibrary::Symbol]
|
300
|
+
# @raise {FFI::NotFoundError} if +cname+ cannot be found in libraries
|
301
|
+
#
|
302
|
+
# Attach C variable +cname+ to this module.
|
154
303
|
def attach_variable(mname, a1, a2 = nil)
|
155
304
|
cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ]
|
156
305
|
address = nil
|
@@ -171,7 +320,7 @@ module FFI
|
|
171
320
|
def self.#{mname}
|
172
321
|
@@ffi_gvar_#{mname}
|
173
322
|
end
|
174
|
-
|
323
|
+
code
|
175
324
|
|
176
325
|
else
|
177
326
|
sc = Class.new(FFI::Struct)
|
@@ -195,6 +344,13 @@ module FFI
|
|
195
344
|
address
|
196
345
|
end
|
197
346
|
|
347
|
+
|
348
|
+
# @overload callback(name, params, ret)
|
349
|
+
# @overload callback(params, ret)
|
350
|
+
# @param name callback name to add to type map
|
351
|
+
# @param [Array] params array of parameters' types
|
352
|
+
# @param [DataConverter, Struct, Symbol, Type] ret callback return type
|
353
|
+
# @return [FFI::CallbackInfo]
|
198
354
|
def callback(*args)
|
199
355
|
raise ArgumentError, "wrong number of arguments" if args.length < 2 || args.length > 3
|
200
356
|
name, params, ret = if args.length == 3
|
@@ -216,6 +372,22 @@ module FFI
|
|
216
372
|
cb
|
217
373
|
end
|
218
374
|
|
375
|
+
# @param [DataConverter, Symbol, Type] old
|
376
|
+
# @param add
|
377
|
+
# @param [] info
|
378
|
+
# @return [FFI::Enum, FFI::Type]
|
379
|
+
# Register or get an already registered type definition.
|
380
|
+
#
|
381
|
+
# To register a new type definition, +old+ should be a {FFI::Type}. +add+
|
382
|
+
# is in this case the type definition.
|
383
|
+
#
|
384
|
+
# If +old+ is a {DataConverter}, a {Type::Mapped} is returned.
|
385
|
+
#
|
386
|
+
# If +old+ is +:enum+
|
387
|
+
# * and +add+ is an +Array+, a call to {#enum} is made with +add+ as single parameter;
|
388
|
+
# * in others cases, +info+ is used to create a named enum.
|
389
|
+
#
|
390
|
+
# If +old+ is a key for type map, #typedef get +old+ type definition.
|
219
391
|
def typedef(old, add, info=nil)
|
220
392
|
@ffi_typedefs = Hash.new unless defined?(@ffi_typedefs)
|
221
393
|
|
@@ -240,13 +412,25 @@ module FFI
|
|
240
412
|
end
|
241
413
|
end
|
242
414
|
|
415
|
+
# @overload enum(name, values)
|
416
|
+
# Create a named enum.
|
417
|
+
# @example
|
418
|
+
# enum :foo, [:zero, :one, :two] # named enum
|
419
|
+
# @param [Symbol] name name for new enum
|
420
|
+
# @param [Array] values values for enum
|
421
|
+
# @overload enum(*args)
|
422
|
+
# Create an unnamed enum.
|
423
|
+
# @example
|
424
|
+
# enum :zero, :one, :two # unnamed enum
|
425
|
+
# @param args values for enum
|
426
|
+
# @overload enum(values)
|
427
|
+
# Create an unnamed enum.
|
428
|
+
# @example
|
429
|
+
# enum [:zero, :one, :two] # unnamed enum, equivalent to above example
|
430
|
+
# @param [Array] values values for enum
|
431
|
+
# @return [FFI::Enum]
|
432
|
+
# Create a new {FFI::Enum}.
|
243
433
|
def enum(*args)
|
244
|
-
#
|
245
|
-
# enum can be called as:
|
246
|
-
# enum :zero, :one, :two # unnamed enum
|
247
|
-
# enum [ :zero, :one, :two ] # equivalent to above
|
248
|
-
# enum :foo, [ :zero, :one, :two ] create an enum named :foo
|
249
|
-
#
|
250
434
|
name, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array)
|
251
435
|
[ args[0], args[1] ]
|
252
436
|
elsif args[0].kind_of?(Array)
|
@@ -262,18 +446,27 @@ module FFI
|
|
262
446
|
e
|
263
447
|
end
|
264
448
|
|
449
|
+
# @param name
|
450
|
+
# @return [FFI::Enum]
|
451
|
+
# Find an enum by name.
|
265
452
|
def enum_type(name)
|
266
453
|
@ffi_enums.find(name) if defined?(@ffi_enums)
|
267
454
|
end
|
268
455
|
|
456
|
+
# @param symbol
|
457
|
+
# @return [FFI::Enum]
|
458
|
+
# Find an enum by a symbol it contains.
|
269
459
|
def enum_value(symbol)
|
270
460
|
@ffi_enums.__map_symbol(symbol)
|
271
461
|
end
|
272
462
|
|
463
|
+
# @param [DataConverter, Type, Struct, Symbol] t type to find
|
464
|
+
# @return [Type]
|
465
|
+
# Find a type definition.
|
273
466
|
def find_type(t)
|
274
467
|
if t.kind_of?(Type)
|
275
468
|
t
|
276
|
-
|
469
|
+
|
277
470
|
elsif defined?(@ffi_typedefs) && @ffi_typedefs.has_key?(t)
|
278
471
|
@ffi_typedefs[t]
|
279
472
|
|
@@ -283,7 +476,7 @@ module FFI
|
|
283
476
|
elsif t.is_a?(DataConverter)
|
284
477
|
# Add a typedef so next time the converter is used, it hits the cache
|
285
478
|
typedef Type::Mapped.new(t), t
|
286
|
-
|
479
|
+
|
287
480
|
end || FFI.find_type(t)
|
288
481
|
end
|
289
482
|
end
|