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.

Files changed (56) hide show
  1. data/Rakefile +4 -4
  2. data/ext/ffi_c/AbstractMemory.c +367 -14
  3. data/ext/ffi_c/AbstractMemory.h +4 -0
  4. data/ext/ffi_c/ArrayType.c +28 -0
  5. data/ext/ffi_c/Buffer.c +101 -25
  6. data/ext/ffi_c/Call.c +8 -5
  7. data/ext/ffi_c/ClosurePool.c +9 -8
  8. data/ext/ffi_c/DataConverter.c +29 -0
  9. data/ext/ffi_c/DynamicLibrary.c +64 -1
  10. data/ext/ffi_c/Function.c +111 -10
  11. data/ext/ffi_c/FunctionInfo.c +13 -1
  12. data/ext/ffi_c/LastError.c +16 -0
  13. data/ext/ffi_c/MappedType.c +22 -0
  14. data/ext/ffi_c/MemoryPointer.c +11 -1
  15. data/ext/ffi_c/MethodHandle.c +18 -11
  16. data/ext/ffi_c/Platform.c +9 -3
  17. data/ext/ffi_c/Pointer.c +98 -0
  18. data/ext/ffi_c/Struct.c +4 -4
  19. data/ext/ffi_c/Struct.h +2 -1
  20. data/ext/ffi_c/StructLayout.c +2 -2
  21. data/ext/ffi_c/Thread.c +124 -1
  22. data/ext/ffi_c/Type.c +108 -17
  23. data/ext/ffi_c/Types.c +9 -2
  24. data/ext/ffi_c/Variadic.c +5 -4
  25. data/ext/ffi_c/compat.h +8 -0
  26. data/ext/ffi_c/endian.h +7 -1
  27. data/ext/ffi_c/extconf.rb +46 -35
  28. data/ext/ffi_c/ffi.c +5 -0
  29. data/ext/ffi_c/libffi.darwin.mk +15 -15
  30. data/ext/ffi_c/libffi.gnu.mk +3 -3
  31. data/ext/ffi_c/libffi.mk +4 -4
  32. data/lib/ffi.rb +13 -9
  33. data/lib/ffi/autopointer.rb +88 -26
  34. data/lib/ffi/enum.rb +42 -0
  35. data/lib/ffi/errno.rb +6 -1
  36. data/lib/ffi/ffi.rb +1 -0
  37. data/lib/ffi/io.rb +13 -2
  38. data/lib/ffi/library.rb +212 -19
  39. data/lib/ffi/memorypointer.rb +1 -33
  40. data/lib/ffi/platform.rb +23 -7
  41. data/lib/ffi/platform/i386-freebsd/types.conf +152 -0
  42. data/lib/ffi/platform/i386-netbsd/types.conf +126 -0
  43. data/lib/ffi/platform/x86_64-freebsd/types.conf +126 -0
  44. data/lib/ffi/platform/x86_64-netbsd/types.conf +126 -0
  45. data/lib/ffi/pointer.rb +44 -0
  46. data/lib/ffi/struct.rb +1 -1
  47. data/lib/ffi/struct_layout_builder.rb +2 -1
  48. data/lib/ffi/tools/const_generator.rb +72 -17
  49. data/lib/ffi/types.rb +21 -1
  50. data/spec/ffi/rbx/memory_pointer_spec.rb +4 -2
  51. data/spec/ffi/struct_spec.rb +10 -0
  52. data/spec/ffi/typedef_spec.rb +11 -0
  53. data/tasks/extension.rake +0 -1
  54. data/tasks/gem.rake +0 -1
  55. data/tasks/yard.rake +11 -0
  56. metadata +15 -8
@@ -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)
@@ -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
- cd $(LIBFFI_BUILD_DIR) && $(MAKE)
13
+ $(MAKE) -C "$(LIBFFI_BUILD_DIR)"
data/lib/ffi.rb CHANGED
@@ -1,11 +1,15 @@
1
- begin
2
- if RUBY_VERSION =~ /1.8/
3
- require '1.8/ffi_c'
4
- elsif RUBY_VERSION =~ /1.9/
5
- require '1.9/ffi_c'
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
@@ -23,17 +23,31 @@ module FFI
23
23
  class AutoPointer < Pointer
24
24
  extend DataConverter
25
25
 
26
- # call-seq:
27
- # AutoPointer.new(pointer, method) => the passed Method will be invoked at GC time
28
- # AutoPointer.new(pointer, proc) => the passed Proc will be invoked at GC time (SEE WARNING BELOW!)
29
- # AutoPointer.new(pointer) { |p| ... } => the passed block will be invoked at GC time (SEE WARNING BELOW!)
30
- # AutoPointer.new(pointer) => the pointer's release() class method will be invoked at GC time
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
- # WARNING: passing a proc _may_ cause your pointer to never be GC'd, unless you're careful to avoid trapping a reference to the pointer in the proc. See the test specs for examples.
33
- # WARNING: passing a block will cause your pointer to never be GC'd. This is bad.
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
- # The above code will cause PointerHelper#release to be invoked at GC time.
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
- raise RuntimeError.new("pointer already freed") unless @ptr
87
- @autorelease = false
88
- @ptr = nil
89
- @proc = nil
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
- raise RuntimeError.new("pointer already freed") unless @ptr
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
- def call(*args)
101
- @proc.release(@ptr) if @autorelease && @ptr
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
- def call(*args)
107
- @proc.call(@ptr) if @autorelease && @ptr
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
@@ -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
@@ -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
@@ -21,6 +21,7 @@ require 'ffi/platform'
21
21
  require 'ffi/types'
22
22
  require 'ffi/library'
23
23
  require 'ffi/errno'
24
+ require 'ffi/pointer'
24
25
  require 'ffi/memorypointer'
25
26
  require 'ffi/struct'
26
27
  require 'ffi/union'
@@ -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)
@@ -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::IS_LINUX ? "\\.so($|\\.[1234567890]+)" : "\\.#{Platform::LIBSUFFIX}$"
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
- errors[libname] = ex
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
- # Attach C function to this module.
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
- def attach_function(mname, a2, a3, a4=nil, a5 = nil)
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 = lib.find_function(cname.to_s)
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
- code
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