1require 'dl' 2require 'dl/callback' 3require 'dl/stack' 4require 'dl/value' 5require 'thread' 6 7module DL 8 parent = DL.fiddle? ? Fiddle::Function : Object 9 10 class Function < parent 11 include DL 12 include ValueUtil 13 14 if DL.fiddle? 15 # :stopdoc: 16 CALL_TYPE_TO_ABI = Hash.new { |h, k| 17 raise RuntimeError, "unsupported call type: #{k}" 18 }.merge({ :stdcall => 19 (Fiddle::Function::STDCALL rescue Fiddle::Function::DEFAULT), 20 :cdecl => Fiddle::Function::DEFAULT, 21 nil => Fiddle::Function::DEFAULT 22 }).freeze 23 private_constant :CALL_TYPE_TO_ABI 24 # :startdoc: 25 26 def self.call_type_to_abi(call_type) # :nodoc: 27 CALL_TYPE_TO_ABI[call_type] 28 end 29 private_class_method :call_type_to_abi 30 31 class FiddleClosureCFunc < Fiddle::Closure # :nodoc: all 32 def initialize ctype, arg, abi, name 33 @name = name 34 super(ctype, arg, abi) 35 end 36 def name 37 @name 38 end 39 def ptr 40 to_i 41 end 42 end 43 private_constant :FiddleClosureCFunc 44 45 def self.class_fiddle_closure_cfunc # :nodoc: 46 FiddleClosureCFunc 47 end 48 private_class_method :class_fiddle_closure_cfunc 49 end 50 51 def initialize cfunc, argtypes, abi = nil, &block 52 if DL.fiddle? 53 abi ||= CALL_TYPE_TO_ABI[(cfunc.calltype rescue nil)] 54 if block_given? 55 @cfunc = Class.new(FiddleClosureCFunc) { 56 define_method(:call, block) 57 }.new(cfunc.ctype, argtypes, abi, cfunc.name) 58 else 59 @cfunc = cfunc 60 end 61 62 @args = argtypes 63 super(@cfunc, @args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi) 64 else 65 @cfunc = cfunc 66 @stack = Stack.new(argtypes.collect{|ty| ty.abs}) 67 if( @cfunc.ctype < 0 ) 68 @cfunc.ctype = @cfunc.ctype.abs 69 @unsigned = true 70 else 71 @unsigned = false 72 end 73 if block_given? 74 bind(&block) 75 end 76 end 77 end 78 79 def to_i() 80 @cfunc.to_i 81 end 82 83 def name 84 @cfunc.name 85 end 86 87 def call(*args, &block) 88 if DL.fiddle? 89 if block_given? 90 args.find { |a| DL::Function === a }.bind_at_call(&block) 91 end 92 super 93 else 94 funcs = [] 95 if $SAFE >= 1 && args.any? { |x| x.tainted? } 96 raise SecurityError, "tainted parameter not allowed" 97 end 98 _args = wrap_args(args, @stack.types, funcs, &block) 99 r = @cfunc.call(@stack.pack(_args)) 100 funcs.each{|f| f.unbind_at_call()} 101 return wrap_result(r) 102 end 103 end 104 105 def wrap_result(r) 106 case @cfunc.ctype 107 when TYPE_VOIDP 108 r = CPtr.new(r) 109 else 110 if( @unsigned ) 111 r = unsigned_value(r, @cfunc.ctype) 112 end 113 end 114 r 115 end 116 117 def bind(&block) 118 if DL.fiddle? 119 @cfunc = Class.new(FiddleClosureCFunc) { 120 def initialize ctype, args, abi, name, block 121 super(ctype, args, abi, name) 122 @block = block 123 end 124 125 def call *args 126 @block.call(*args) 127 end 128 }.new(@cfunc.ctype, @args, abi, name, block) 129 @ptr = @cfunc 130 return nil 131 else 132 if( !block ) 133 raise(RuntimeError, "block must be given.") 134 end 135 unless block.lambda? 136 block = Class.new(self.class){define_method(:call, block); def initialize(obj); obj.instance_variables.each{|s| instance_variable_set(s, obj.instance_variable_get(s))}; end}.new(self).method(:call) 137 end 138 if( @cfunc.ptr == 0 ) 139 cb = Proc.new{|*args| 140 ary = @stack.unpack(args) 141 @stack.types.each_with_index{|ty, idx| 142 case ty 143 when TYPE_VOIDP 144 ary[idx] = CPtr.new(ary[idx]) 145 end 146 } 147 r = block.call(*ary) 148 wrap_arg(r, @cfunc.ctype, []) 149 } 150 case @cfunc.calltype 151 when :cdecl 152 @cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb) 153 when :stdcall 154 @cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb) 155 else 156 raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}") 157 end 158 if( @cfunc.ptr == 0 ) 159 raise(RuntimeException, "can't bind C function.") 160 end 161 end 162 end 163 end 164 165 def unbind() 166 if DL.fiddle? then 167 if @cfunc.kind_of?(Fiddle::Closure) and @cfunc.ptr != 0 then 168 call_type = case abi 169 when CALL_TYPE_TO_ABI[nil] 170 nil 171 when CALL_TYPE_TO_ABI[:stdcall] 172 :stdcall 173 else 174 raise(RuntimeError, "unsupported abi: #{abi}") 175 end 176 @cfunc = CFunc.new(0, @cfunc.ctype, name, call_type) 177 return 0 178 elsif @cfunc.ptr != 0 then 179 @cfunc.ptr = 0 180 return 0 181 else 182 return nil 183 end 184 end 185 if( @cfunc.ptr != 0 ) 186 case @cfunc.calltype 187 when :cdecl 188 remove_cdecl_callback(@cfunc.ptr, @cfunc.ctype) 189 when :stdcall 190 remove_stdcall_callback(@cfunc.ptr, @cfunc.ctype) 191 else 192 raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}") 193 end 194 @cfunc.ptr = 0 195 end 196 end 197 198 def bound?() 199 @cfunc.ptr != 0 200 end 201 202 def bind_at_call(&block) 203 bind(&block) 204 end 205 206 def unbind_at_call() 207 end 208 end 209 210 class TempFunction < Function 211 def bind_at_call(&block) 212 bind(&block) 213 end 214 215 def unbind_at_call() 216 unbind() 217 end 218 end 219 220 class CarriedFunction < Function 221 def initialize(cfunc, argtypes, n) 222 super(cfunc, argtypes) 223 @carrier = [] 224 @index = n 225 @mutex = Mutex.new 226 end 227 228 def create_carrier(data) 229 ary = [] 230 userdata = [ary, data] 231 @mutex.lock() 232 @carrier.push(userdata) 233 return dlwrap(userdata) 234 end 235 236 def bind_at_call(&block) 237 userdata = @carrier[-1] 238 userdata[0].push(block) 239 bind{|*args| 240 ptr = args[@index] 241 if( !ptr ) 242 raise(RuntimeError, "The index of userdata should be lower than #{args.size}.") 243 end 244 userdata = dlunwrap(Integer(ptr)) 245 args[@index] = userdata[1] 246 userdata[0][0].call(*args) 247 } 248 @mutex.unlock() 249 end 250 end 251end 252