Permalink
Browse files

FFI API revamp, take 1.

  • Loading branch information...
1 parent dbe6c11 commit 71fdc7551f676fec9ac7df08845c6b0e8e4f0210 Evan Phoenix committed May 29, 2010
View
@@ -165,19 +165,25 @@ def to_a
def to_hash
env = environ()
- env.type_size = FFI.type_size(FFI.find_type :pointer)
+ ptr_size = FFI.type_size FFI.find_type(:pointer)
i = 0
hash = {}
- until env[i].read_pointer.null? do
- entry = env[i].read_pointer.read_string
+ offset = 0
+ cur = env + offset
+
+ until cur.read_pointer.null?
+ entry = cur.read_pointer.read_string
key, value = entry.split '=', 2
- value.taint unless value.nil?
- key.taint unless key.nil?
+ value.taint if value
+ key.taint if key
+
hash[key] = value
- i += 1
+
+ offset += ptr_size
+ cur = env + offset
end
hash
View
@@ -0,0 +1,13 @@
+module FFI
+ module Library
+ def find_type(name)
+ @ffi_callbacks ||= {}
+
+ if val = @ffi_callbacks[name]
+ return val
+ end
+
+ return FFI.find_type(name)
+ end
+ end
+end
@@ -21,3 +21,4 @@ static_scope.rbc
struct.rbc
thread.rbc
codeloader.rbc
+ffi.rbc
View
@@ -14,7 +14,7 @@ class NotFoundError < RuntimeError; end
#
# Use this constant instead of nil directly.
#
- USE_THIS_PROCESS_AS_LIBRARY = nil
+ CURRENT_PROCESS = nil
TypeDefs = Rubinius::LookupTable.new
View
@@ -1,4 +1,14 @@
module FFI
+ def self.generate_function(ptr, name, args, ret)
+ Ruby.primitive :nativefunction_generate
+ raise PrimitiveFailure, "FFI.generate_function failed"
+ end
+
+ def self.generate_trampoline(obj, name, args, ret)
+ Ruby.primitive :nativefunction_generate_tramp
+ raise PrimitiveFailure, "FFI.generate_function_tramp failed"
+ end
+
module Library
# Set which library or libraries +attach_function+ should
@@ -8,57 +18,40 @@ module Library
# The libraries are tried in the order given.
#
def ffi_lib(*names)
- @ffi_lib = names
+ @ffi_lib = names.map do |x|
+ if x == FFI::CURRENT_PROCESS
+ DynamicLibrary::CURRENT_PROCESS
+
+ # When the element is an array, it's an ordered choice,
+ # ie, pick the first library that works.
+ elsif x.kind_of? Array
+ lib = nil
+
+ x.each do |name|
+ begin
+ lib = DynamicLibrary.new(name)
+ break
+ rescue LoadError
+ end
+ end
+
+ # If .new worked, then lib is set and we can use it.
+ unless lib
+ raise LoadError, "Unable to find libary among: #{x.inspect}"
+ end
+
+ lib
+ else
+ DynamicLibrary.new(x)
+ end
+ end
end
def ffi_libraries
- @ffi_lib or [FFI::USE_THIS_PROCESS_AS_LIBRARY]
+ @ffi_lib or [DynamicLibrary::CURRENT_PROCESS]
end
private :ffi_libraries
- def ffi_library_names
- libraries = ffi_libraries
- this_process = "this process"
-
- names = libraries[0] || this_process
-
- i = 1
- size = libraries.size
- while i < size
- names.append ", "
- names.append libraries[i] || this_process
- i += 1
- end
-
- names
- end
- private :ffi_library_names
-
- def create_backend(library, name, args, ret)
- Ruby.primitive :nativefunction_bind
- # Do not raise an exception if this primitive fails to bind an external
- # function because #attach_function may be attempting to bind to a
- # function in more than one library
- raise PrimitiveFailure, "create_backend failed"
- end
- private :create_backend
-
- # Setup the LD_LIBRARY_PATH
- # @todo Not using LTDL currently.
- def setup_ld_library_path(library)
- # If we have a specific reference to the library, we load it here
- specific_library = FFI.config("ld_library_path.#{library}")
- library = specific_library if specific_library
-
- # This adds general paths to the search
- if path = FFI.config("ld_library_path.default")
- ENV['LTDL_LIBRARY_PATH'] = [ENV['LTDL_LIBRARY_PATH'], path].compact.join(":")
- end
-
- library
- end
- private :setup_ld_library_path
-
# Attach a C function to this module. The arguments can have two forms:
#
# attach_function c_name, [c_arg1, c_arg2], ret
@@ -77,36 +70,143 @@ def setup_ld_library_path(library)
def attach_function(name, a3, a4, a5=nil)
if a5
cname = a3.to_s
+ if a3.kind_of? Pointer
+ cname = a3
+ int_name = name.to_sym
+ else
+ cname = a3.to_s
+ int_name = cname.to_sym
+ end
+
args = a4
ret = a5
else
cname = name.to_s
+ int_name = cname.to_sym
args = a3
ret = a4
end
- mname = name.to_sym
- args.map! { |a| FFI.find_type a }
- ret = FFI.find_type ret
+ mname = name.to_sym
ffi_libraries.each do |lib|
- lib = setup_ld_library_path lib if lib
+ if ptr = lib.find_symbol(cname)
+ return pointer_as_function(mname, ptr, args, ret)
+ end
+ end
+
+ raise FFI::NotFoundError, "Unable to find '#{cname}'"
+ end
- if func = create_backend(lib, cname, args, ret)
+ def pointer_as_function(name, ptr, args, ret)
+ args.map! { |a| find_type a }
- # Make it available as a method callable directly..
- Rubinius.object_metaclass(self).method_table.store mname, func, :public
+ if func = FFI.generate_function(ptr, name.to_sym, args, find_type(ret))
- # and expose it as a private method for people who
- # want to include this module.
- method_table.store mname, func, :private
+ # Make it available as a method callable directly..
+ Rubinius.object_metaclass(self).method_table.store name, func, :public
+ # and expose it as a private method for people who
+ # want to include this module.
+ method_table.store name, func, :private
+
+ return func
+ end
+
+ raise FFI::NotFoundError, "Unable to attach pointer"
+ end
+
+ def callback(a1, a2, a3=nil)
+ if a3
+ name, params, ret = a1, a2, a3
+ else
+ name, params, ret = nil, a1, a2
+ end
- return func
+ args = params.map { |x| find_type(x) }
+
+ func, ptr = FFI.generate_trampoline(nil, :ffi_tramp, args, find_type(ret))
+
+ if name
+ @ffi_callbacks ||= {}
+ @ffi_callbacks[name] = func
+ end
+
+ return func
+ end
+
+ def find_type(name)
+ return FFI.find_type(name)
+ end
+ end
+
+ class DynamicLibrary
+ extend FFI::Library
+
+ # Bootstrap dlsym, dlopen, and dlerror
+ pointer_as_function :find_symbol, FFI::Pointer::DLSYM, [:pointer, :string], :pointer
+
+ dlopen = find_symbol(FFI::Pointer::CURRENT_PROCESS, "dlopen")
+
+ pointer_as_function :open_library, dlopen, [:string, :int], :pointer
+
+ dlerror = find_symbol(FFI::Pointer::CURRENT_PROCESS, "dlerror")
+
+ pointer_as_function :last_error, dlerror, [], :string
+
+ RTLD_LAZY = 0x1
+ RTLD_NOW = 0x2
+ RTLD_GLOBAL = 0x4
+ RTLD_LOCAL = 0x8
+
+ class << self
+ alias_method :open, :new
+ end
+
+ def initialize(name, flags=nil)
+ # Accept nil and check for ruby-ffi API compat
+ flags ||= RTLD_LAZY
+
+ if name
+ @name = name
+ @handle = DynamicLibrary.open_library name, flags
+
+ unless @handle
+ orig_error = last_error
+
+ # Try with suffixes
+ FFI::LIB_SUFFIXES.detect do |suffix|
+ @name = "#{name}.#{suffix}"
+ @handle = DynamicLibrary.open_library @name, flags
+ end
+
+ unless @handle
+ # API Compat. LoadError is wrong here.
+ raise LoadError, "Could not open library #{name} - #{orig_error}"
+ end
end
+ else
+ @name = "[current process]"
+ @handle = FFI::Pointer::CURRENT_PROCESS
end
+ end
+
+ attr_reader :name
- raise FFI::NotFoundError, "Unable to find FFI '#{cname}' in: #{ffi_library_names}"
+ def find_symbol(name)
+ ptr = DynamicLibrary.find_symbol @handle, name
+
+ # defined in kernel/platform/pointer.rb
+ FFI::DynamicLibrary::Symbol.new(self, ptr, name)
end
+
+ alias_method :find_function, :find_symbol
+ alias_method :find_variable, :find_symbol
+
+ def last_error
+ DynamicLibrary.last_error
+ end
+
+ CURRENT_PROCESS = DynamicLibrary.new(nil)
end
end
@@ -1,8 +1,8 @@
ffi.rbc
library.rbc
+pointer.rbc
env.rbc
file.rbc
math.rbc
-memorypointer.rbc
posix.rbc
struct.rbc
Oops, something went wrong.

0 comments on commit 71fdc75

Please sign in to comment.