From 4bada8b864e445b6eebe1a341e30cad94dbcaf84 Mon Sep 17 00:00:00 2001 From: tenderlove Date: Thu, 6 May 2010 06:59:24 +0000 Subject: * ext/fiddle/*: Adding fiddle library to wrap libffi * test/fiddle/*: testing fiddle extension * ext/dl/lib/dl.rb: Requiring fiddle if it is available * ext/dl/lib/dl/callback.rb: using Fiddle if it is available * ext/dl/lib/dl/func.rb: ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27640 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/dl/lib/dl.rb | 12 +++++ ext/dl/lib/dl/callback.rb | 83 +++++++++++++++++++++------------ ext/dl/lib/dl/func.rb | 114 ++++++++++++++++++++++++++++++---------------- ext/dl/lib/dl/import.rb | 14 ++++-- ext/dl/lib/dl/value.rb | 2 +- 5 files changed, 154 insertions(+), 71 deletions(-) create mode 100644 ext/dl/lib/dl.rb (limited to 'ext/dl') diff --git a/ext/dl/lib/dl.rb b/ext/dl/lib/dl.rb new file mode 100644 index 0000000000..168a18a55e --- /dev/null +++ b/ext/dl/lib/dl.rb @@ -0,0 +1,12 @@ +require 'dl.so' + +begin + require 'fiddle' +rescue LoadError +end + +module DL + def self.fiddle? + Object.const_defined?(:Fiddle) + end +end diff --git a/ext/dl/lib/dl/callback.rb b/ext/dl/lib/dl/callback.rb index c8daaf6322..0863c70d4d 100644 --- a/ext/dl/lib/dl/callback.rb +++ b/ext/dl/lib/dl/callback.rb @@ -4,22 +4,38 @@ require 'thread' module DL SEM = Mutex.new - def set_callback_internal(proc_entry, addr_entry, argc, ty, &cbp) + if DL.fiddle? + CdeclCallbackProcs = {} + CdeclCallbackAddrs = {} + StdcallCallbackProcs = {} + StdcallCallbackAddrs = {} + end + + def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = nil, &cbp) if( argc < 0 ) raise(ArgumentError, "arity should not be less than 0.") end addr = nil - SEM.synchronize{ - ary = proc_entry[ty] - (0...MAX_CALLBACK).each{|n| - idx = (n * DLSTACK_SIZE) + argc - if( ary[idx].nil? ) - ary[idx] = cbp - addr = addr_entry[ty][idx] - break - end + + if DL.fiddle? + abi ||= Fiddle::Function::DEFAULT + closure = Fiddle::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp) + proc_entry[closure.to_i] = closure + addr = closure.to_i + else + SEM.synchronize{ + ary = proc_entry[ty] + (0...MAX_CALLBACK).each{|n| + idx = (n * DLSTACK_SIZE) + argc + if( ary[idx].nil? ) + ary[idx] = cbp + addr = addr_entry[ty][idx] + break + end + } } - } + end + addr end @@ -28,31 +44,42 @@ module DL end def set_stdcall_callback(ty, argc, &cbp) - set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp) + if DL.fiddle? + set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, Fiddle::Function::STDCALL, &cbp) + else + set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp) + end end def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil) - index = nil - if( ctype ) - addr_entry[ctype].each_with_index{|xaddr, idx| - if( xaddr == addr ) - index = idx - end - } + if DL.fiddle? + addr = addr.to_i + return false unless proc_entry.key?(addr) + proc_entry.delete(addr) + true else - addr_entry.each{|ty,entry| - entry.each_with_index{|xaddr, idx| + index = nil + if( ctype ) + addr_entry[ctype].each_with_index{|xaddr, idx| if( xaddr == addr ) index = idx end } - } - end - if( index and proc_entry[ctype][index] ) - proc_entry[ctype][index] = nil - return true - else - return false + else + addr_entry.each{|ty,entry| + entry.each_with_index{|xaddr, idx| + if( xaddr == addr ) + index = idx + end + } + } + end + if( index and proc_entry[ctype][index] ) + proc_entry[ctype][index] = nil + return true + else + return false + end end end diff --git a/ext/dl/lib/dl/func.rb b/ext/dl/lib/dl/func.rb index 7a8b62e325..4d0db4ee7a 100644 --- a/ext/dl/lib/dl/func.rb +++ b/ext/dl/lib/dl/func.rb @@ -5,21 +5,37 @@ require 'dl/value' require 'thread' module DL - class Function + parent = DL.fiddle? ? Fiddle::Function : Object + + class Function < parent include DL include ValueUtil - def initialize(cfunc, argtypes, &proc) - @cfunc = cfunc - @stack = Stack.new(argtypes.collect{|ty| ty.abs}) - if( @cfunc.ctype < 0 ) - @cfunc.ctype = @cfunc.ctype.abs - @unsigned = true + def initialize cfunc, argtypes, abi = nil, &block + if DL.fiddle? + abi ||= Fiddle::Function::DEFAULT + if block_given? + @cfunc = Class.new(Fiddle::Closure) { + define_method(:call, block) + }.new(cfunc.ctype, argtypes) + else + @cfunc = cfunc + end + + @args = argtypes + super(@cfunc, @args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi) else - @unsigned = false - end - if( proc ) - bind(&proc) + @cfunc = cfunc + @stack = Stack.new(argtypes.collect{|ty| ty.abs}) + if( @cfunc.ctype < 0 ) + @cfunc.ctype = @cfunc.ctype.abs + @unsigned = true + else + @unsigned = false + end + if block_given? + bind(&block) + end end end @@ -32,11 +48,18 @@ module DL end def call(*args, &block) - funcs = [] - args = wrap_args(args, @stack.types, funcs, &block) - r = @cfunc.call(@stack.pack(args)) - funcs.each{|f| f.unbind_at_call()} - return wrap_result(r) + if DL.fiddle? + if block_given? + args.find { |a| DL::Function === a }.bind_at_call(&block) + end + super + else + funcs = [] + args = wrap_args(args, @stack.types, funcs, &block) + r = @cfunc.call(@stack.pack(args)) + funcs.each{|f| f.unbind_at_call()} + return wrap_result(r) + end end def wrap_result(r) @@ -52,31 +75,44 @@ module DL end def bind(&block) - if( !block ) - raise(RuntimeError, "block must be given.") - end - if( @cfunc.ptr == 0 ) - cb = Proc.new{|*args| - ary = @stack.unpack(args) - @stack.types.each_with_index{|ty, idx| - case ty - when TYPE_VOIDP - ary[idx] = CPtr.new(ary[idx]) - end - } - r = block.call(*ary) - wrap_arg(r, @cfunc.ctype, []) - } - case @cfunc.calltype - when :cdecl - @cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb) - when :stdcall - @cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb) - else - raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}") + if DL.fiddle? + @cfunc = Class.new(Fiddle::Closure) { + def initialize ctype, args, block + super(ctype, args) + @block = block + end + + def call *args + @block.call(*args) + end + }.new(@cfunc.ctype, @args, block) + else + if( !block ) + raise(RuntimeError, "block must be given.") end if( @cfunc.ptr == 0 ) - raise(RuntimeException, "can't bind C function.") + cb = Proc.new{|*args| + ary = @stack.unpack(args) + @stack.types.each_with_index{|ty, idx| + case ty + when TYPE_VOIDP + ary[idx] = CPtr.new(ary[idx]) + end + } + r = block.call(*ary) + wrap_arg(r, @cfunc.ctype, []) + } + case @cfunc.calltype + when :cdecl + @cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb) + when :stdcall + @cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb) + else + raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}") + end + if( @cfunc.ptr == 0 ) + raise(RuntimeException, "can't bind C function.") + end end end end diff --git a/ext/dl/lib/dl/import.rb b/ext/dl/lib/dl/import.rb index 199354c18e..fd23bc9676 100644 --- a/ext/dl/lib/dl/import.rb +++ b/ext/dl/lib/dl/import.rb @@ -211,9 +211,17 @@ module DL end def bind_function(name, ctype, argtype, call_type = nil, &block) - f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype) - f.bind(&block) - f + if DL.fiddle? + closure = Class.new(Fiddle::Closure) { + define_method(:call, block) + }.new(ctype, argtype) + + Function.new(closure, argtype) + else + f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype) + f.bind(&block) + f + end end def create_temp_function(name, ctype, argtype, call_type = nil) diff --git a/ext/dl/lib/dl/value.rb b/ext/dl/lib/dl/value.rb index 56dfcefa32..51f96b293b 100644 --- a/ext/dl/lib/dl/value.rb +++ b/ext/dl/lib/dl/value.rb @@ -45,7 +45,7 @@ module DL result end - def wrap_arg(arg, ty, funcs, &block) + def wrap_arg(arg, ty, funcs = [], &block) funcs ||= [] case arg when nil -- cgit v1.2.3