mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* 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
This commit is contained in:
		
							parent
							
								
									ca3c007f05
								
							
						
					
					
						commit
						4bada8b864
					
				
					 22 changed files with 1084 additions and 71 deletions
				
			
		| 
						 | 
				
			
			@ -1,3 +1,11 @@
 | 
			
		|||
Thu May  6 15:56:12 2010  Aaron Patterson <aaron@tenderlovemaking.com>
 | 
			
		||||
 | 
			
		||||
	* 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
 | 
			
		||||
 | 
			
		||||
Thu May  6 15:04:37 2010  NARUSE, Yui  <naruse@ruby-lang.org>
 | 
			
		||||
 | 
			
		||||
	* string.c (rb_str_match_m): add description about optional
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								ext/dl/lib/dl.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								ext/dl/lib/dl.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
require 'dl.so'
 | 
			
		||||
 | 
			
		||||
begin
 | 
			
		||||
  require 'fiddle'
 | 
			
		||||
rescue LoadError
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
module DL
 | 
			
		||||
  def self.fiddle?
 | 
			
		||||
    Object.const_defined?(:Fiddle)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										232
									
								
								ext/fiddle/closure.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								ext/fiddle/closure.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,232 @@
 | 
			
		|||
#include <fiddle.h>
 | 
			
		||||
 | 
			
		||||
VALUE cFiddleClosure;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    void * code;
 | 
			
		||||
    ffi_closure *pcl;
 | 
			
		||||
    ffi_cif * cif;
 | 
			
		||||
    int argc;
 | 
			
		||||
    ffi_type **argv;
 | 
			
		||||
} fiddle_closure;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
dealloc(void * ptr)
 | 
			
		||||
{
 | 
			
		||||
    fiddle_closure * cls = (fiddle_closure *)ptr;
 | 
			
		||||
#ifndef MACOSX
 | 
			
		||||
    ffi_closure_free(cls->pcl);
 | 
			
		||||
#else
 | 
			
		||||
    munmap(cls->pcl, sizeof(cls->pcl));
 | 
			
		||||
#endif
 | 
			
		||||
    xfree(cls->cif);
 | 
			
		||||
    if (cls->argv) xfree(cls->argv);
 | 
			
		||||
    xfree(cls);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t
 | 
			
		||||
closure_memsize(const void * ptr)
 | 
			
		||||
{
 | 
			
		||||
    fiddle_closure * cls = (fiddle_closure *)ptr;
 | 
			
		||||
    size_t size = 0;
 | 
			
		||||
 | 
			
		||||
    if (ptr) {
 | 
			
		||||
	size += sizeof(*cls);
 | 
			
		||||
#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
 | 
			
		||||
	size += ffi_raw_size(cls->cif);
 | 
			
		||||
#endif
 | 
			
		||||
	size += sizeof(*cls->argv);
 | 
			
		||||
	size += sizeof(ffi_closure);
 | 
			
		||||
    }
 | 
			
		||||
    return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const rb_data_type_t closure_data_type = {
 | 
			
		||||
    "fiddle/closure",
 | 
			
		||||
    0, dealloc, closure_memsize,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
callback(ffi_cif *cif, void *resp, void **args, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
    VALUE self      = (VALUE)ctx;
 | 
			
		||||
    VALUE rbargs    = rb_iv_get(self, "@args");
 | 
			
		||||
    VALUE ctype     = rb_iv_get(self, "@ctype");
 | 
			
		||||
    int argc        = RARRAY_LENINT(rbargs);
 | 
			
		||||
    VALUE *params   = xcalloc(argc, sizeof(VALUE *));
 | 
			
		||||
    VALUE ret;
 | 
			
		||||
    VALUE cPointer;
 | 
			
		||||
    int i, type;
 | 
			
		||||
 | 
			
		||||
    cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < argc; i++) {
 | 
			
		||||
        type = NUM2INT(RARRAY_PTR(rbargs)[i]);
 | 
			
		||||
        switch (type) {
 | 
			
		||||
	  case TYPE_VOID:
 | 
			
		||||
	    argc = 0;
 | 
			
		||||
	    break;
 | 
			
		||||
	  case TYPE_INT:
 | 
			
		||||
	    params[i] = INT2NUM(*(int *)args[i]);
 | 
			
		||||
	    break;
 | 
			
		||||
	  case TYPE_VOIDP:
 | 
			
		||||
            params[i] = rb_funcall(cPointer, rb_intern("[]"), 1,
 | 
			
		||||
              PTR2NUM(*(void **)args[i]));
 | 
			
		||||
	    break;
 | 
			
		||||
	  case TYPE_LONG:
 | 
			
		||||
	    params[i] = LONG2NUM(*(long *)args[i]);
 | 
			
		||||
	    break;
 | 
			
		||||
	  case TYPE_CHAR:
 | 
			
		||||
	    params[i] = INT2NUM(*(char *)args[i]);
 | 
			
		||||
	    break;
 | 
			
		||||
	  case TYPE_DOUBLE:
 | 
			
		||||
	    params[i] = rb_float_new(*(double *)args[i]);
 | 
			
		||||
	    break;
 | 
			
		||||
	  case TYPE_FLOAT:
 | 
			
		||||
	    params[i] = rb_float_new(*(float *)args[i]);
 | 
			
		||||
	    break;
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
	  case TYPE_LONG_LONG:
 | 
			
		||||
	    params[i] = rb_ull2inum(*(unsigned LONG_LONG *)args[i]);
 | 
			
		||||
	    break;
 | 
			
		||||
#endif
 | 
			
		||||
	  default:
 | 
			
		||||
	    rb_raise(rb_eRuntimeError, "closure args: %d", type);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = rb_funcall2(self, rb_intern("call"), argc, params);
 | 
			
		||||
 | 
			
		||||
    type = NUM2INT(ctype);
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case TYPE_VOID:
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_LONG:
 | 
			
		||||
	*(long *)resp = NUM2LONG(ret);
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_CHAR:
 | 
			
		||||
	*(char *)resp = NUM2INT(ret);
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_VOIDP:
 | 
			
		||||
	*(void **)resp = NUM2PTR(ret);
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_INT:
 | 
			
		||||
	*(int *)resp = NUM2INT(ret);
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_DOUBLE:
 | 
			
		||||
	*(double *)resp = NUM2DBL(ret);
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_FLOAT:
 | 
			
		||||
	*(float *)resp = (float)NUM2DBL(ret);
 | 
			
		||||
	break;
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
      case TYPE_LONG_LONG:
 | 
			
		||||
	*(unsigned LONG_LONG *)resp = rb_big2ull(ret);
 | 
			
		||||
	break;
 | 
			
		||||
#endif
 | 
			
		||||
      default:
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "closure retval: %d", type);
 | 
			
		||||
    }
 | 
			
		||||
    xfree(params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
allocate(VALUE klass)
 | 
			
		||||
{
 | 
			
		||||
    fiddle_closure * closure;
 | 
			
		||||
 | 
			
		||||
    VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
 | 
			
		||||
	    &closure_data_type, closure);
 | 
			
		||||
 | 
			
		||||
#ifndef MACOSX
 | 
			
		||||
    closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
 | 
			
		||||
#else
 | 
			
		||||
    closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
 | 
			
		||||
        MAP_ANON | MAP_PRIVATE, -1, 0);
 | 
			
		||||
#endif
 | 
			
		||||
    closure->cif = xmalloc(sizeof(ffi_cif));
 | 
			
		||||
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
initialize(int rbargc, VALUE argv[], VALUE self)
 | 
			
		||||
{
 | 
			
		||||
    VALUE ret;
 | 
			
		||||
    VALUE args;
 | 
			
		||||
    VALUE abi;
 | 
			
		||||
    fiddle_closure * cl;
 | 
			
		||||
    ffi_cif * cif;
 | 
			
		||||
    ffi_closure *pcl;
 | 
			
		||||
    ffi_status result;
 | 
			
		||||
    int i, argc;
 | 
			
		||||
 | 
			
		||||
    if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
 | 
			
		||||
	abi = INT2NUM(FFI_DEFAULT_ABI);
 | 
			
		||||
 | 
			
		||||
    Check_Type(args, T_ARRAY);
 | 
			
		||||
 | 
			
		||||
    argc = RARRAY_LENINT(args);
 | 
			
		||||
 | 
			
		||||
    TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
 | 
			
		||||
 | 
			
		||||
    cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < argc; i++) {
 | 
			
		||||
        int type = NUM2INT(RARRAY_PTR(args)[i]);
 | 
			
		||||
        cl->argv[i] = INT2FFI_TYPE(type);
 | 
			
		||||
    }
 | 
			
		||||
    cl->argv[argc] = NULL;
 | 
			
		||||
 | 
			
		||||
    rb_iv_set(self, "@ctype", ret);
 | 
			
		||||
    rb_iv_set(self, "@args", args);
 | 
			
		||||
 | 
			
		||||
    cif = cl->cif;
 | 
			
		||||
    pcl = cl->pcl;
 | 
			
		||||
 | 
			
		||||
    result = ffi_prep_cif(cif, NUM2INT(abi), argc,
 | 
			
		||||
                INT2FFI_TYPE(NUM2INT(ret)),
 | 
			
		||||
		cl->argv);
 | 
			
		||||
 | 
			
		||||
    if (FFI_OK != result)
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
 | 
			
		||||
 | 
			
		||||
#ifndef MACOSX
 | 
			
		||||
    result = ffi_prep_closure_loc(pcl, cif, callback,
 | 
			
		||||
		(void *)self, cl->code);
 | 
			
		||||
#else
 | 
			
		||||
    result = ffi_prep_closure(pcl, cif, callback, (void *)self);
 | 
			
		||||
    cl->code = (void *)pcl;
 | 
			
		||||
    mprotect(pcl, sizeof(pcl), PROT_READ | PROT_EXEC);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (FFI_OK != result)
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
to_i(VALUE self)
 | 
			
		||||
{
 | 
			
		||||
    fiddle_closure * cl;
 | 
			
		||||
    void *code;
 | 
			
		||||
 | 
			
		||||
    TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
 | 
			
		||||
 | 
			
		||||
    code = cl->code;
 | 
			
		||||
 | 
			
		||||
    return PTR2NUM(code);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Init_fiddle_closure()
 | 
			
		||||
{
 | 
			
		||||
    cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
 | 
			
		||||
 | 
			
		||||
    rb_define_alloc_func(cFiddleClosure, allocate);
 | 
			
		||||
 | 
			
		||||
    rb_define_method(cFiddleClosure, "initialize", initialize, -1);
 | 
			
		||||
    rb_define_method(cFiddleClosure, "to_i", to_i, 0);
 | 
			
		||||
}
 | 
			
		||||
/* vim: set noet sw=4 sts=4 */
 | 
			
		||||
							
								
								
									
										8
									
								
								ext/fiddle/closure.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ext/fiddle/closure.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
#ifndef FIDDLE_CLOSURE_H
 | 
			
		||||
#define FIDDLE_CLOSURE_H
 | 
			
		||||
 | 
			
		||||
#include <fiddle.h>
 | 
			
		||||
 | 
			
		||||
void Init_fiddle_closure();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										126
									
								
								ext/fiddle/conversions.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								ext/fiddle/conversions.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,126 @@
 | 
			
		|||
#include <fiddle.h>
 | 
			
		||||
 | 
			
		||||
ffi_type *
 | 
			
		||||
int_to_ffi_type(int type)
 | 
			
		||||
{
 | 
			
		||||
    int signed_p = 1;
 | 
			
		||||
 | 
			
		||||
    if (type < 0) {
 | 
			
		||||
	type = -1 * type;
 | 
			
		||||
	signed_p = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#define rb_ffi_type_of(t) (signed_p ? &ffi_type_s##t : &ffi_type_u##t)
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case TYPE_VOID:
 | 
			
		||||
	return &ffi_type_void;
 | 
			
		||||
      case TYPE_VOIDP:
 | 
			
		||||
	return &ffi_type_pointer;
 | 
			
		||||
      case TYPE_CHAR:
 | 
			
		||||
	return rb_ffi_type_of(char);
 | 
			
		||||
      case TYPE_SHORT:
 | 
			
		||||
	return rb_ffi_type_of(short);
 | 
			
		||||
      case TYPE_INT:
 | 
			
		||||
	return rb_ffi_type_of(int);
 | 
			
		||||
      case TYPE_LONG:
 | 
			
		||||
	return rb_ffi_type_of(long);
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
      case TYPE_LONG_LONG:
 | 
			
		||||
	return rb_ffi_type_of(int64);
 | 
			
		||||
#endif
 | 
			
		||||
      case TYPE_FLOAT:
 | 
			
		||||
	return &ffi_type_float;
 | 
			
		||||
      case TYPE_DOUBLE:
 | 
			
		||||
	return &ffi_type_double;
 | 
			
		||||
      default:
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "unknown type %d", type);
 | 
			
		||||
    }
 | 
			
		||||
    return &ffi_type_pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
value_to_generic(int type, VALUE src, fiddle_generic * dst)
 | 
			
		||||
{
 | 
			
		||||
    int signed_p = 1;
 | 
			
		||||
 | 
			
		||||
    if (type < 0) {
 | 
			
		||||
	type = -1 * type;
 | 
			
		||||
	signed_p = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case TYPE_VOID:
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_VOIDP:
 | 
			
		||||
	dst->pointer = NUM2PTR(rb_Integer(src));
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_CHAR:
 | 
			
		||||
      case TYPE_SHORT:
 | 
			
		||||
      case TYPE_INT:
 | 
			
		||||
	dst->sint = NUM2INT(src);
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_LONG:
 | 
			
		||||
	if (signed_p)
 | 
			
		||||
	    dst->slong = NUM2LONG(src);
 | 
			
		||||
	else
 | 
			
		||||
	    dst->ulong = NUM2LONG(src);
 | 
			
		||||
	break;
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
      case TYPE_LONG_LONG:
 | 
			
		||||
	dst->long_long = rb_big2ull(src);
 | 
			
		||||
	break;
 | 
			
		||||
#endif
 | 
			
		||||
      case TYPE_FLOAT:
 | 
			
		||||
	dst->ffloat = (float)NUM2DBL(src);
 | 
			
		||||
	break;
 | 
			
		||||
      case TYPE_DOUBLE:
 | 
			
		||||
	dst->ddouble = NUM2DBL(src);
 | 
			
		||||
	break;
 | 
			
		||||
      default:
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "unknown type %d", type);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE
 | 
			
		||||
generic_to_value(VALUE rettype, fiddle_generic retval)
 | 
			
		||||
{
 | 
			
		||||
    int signed_p = 1;
 | 
			
		||||
    int type = NUM2INT(rettype);
 | 
			
		||||
    VALUE cPointer;
 | 
			
		||||
 | 
			
		||||
    cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
 | 
			
		||||
 | 
			
		||||
    if (type < 0) {
 | 
			
		||||
	type = -1 * type;
 | 
			
		||||
	signed_p = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case TYPE_VOID:
 | 
			
		||||
	return Qnil;
 | 
			
		||||
      case TYPE_VOIDP:
 | 
			
		||||
        return rb_funcall(cPointer, rb_intern("[]"), 1,
 | 
			
		||||
          PTR2NUM((void *)retval.pointer));
 | 
			
		||||
      case TYPE_CHAR:
 | 
			
		||||
      case TYPE_SHORT:
 | 
			
		||||
      case TYPE_INT:
 | 
			
		||||
	return INT2NUM(retval.sint);
 | 
			
		||||
      case TYPE_LONG:
 | 
			
		||||
	if (signed_p) return LONG2NUM(retval.slong);
 | 
			
		||||
	return ULONG2NUM(retval.ulong);
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
      case TYPE_LONG_LONG:
 | 
			
		||||
	return rb_ll2inum(retval.long_long);
 | 
			
		||||
	break;
 | 
			
		||||
#endif
 | 
			
		||||
      case TYPE_FLOAT:
 | 
			
		||||
	return rb_float_new(retval.ffloat);
 | 
			
		||||
      case TYPE_DOUBLE:
 | 
			
		||||
	return rb_float_new(retval.ddouble);
 | 
			
		||||
      default:
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "unknown type %d", type);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* vim: set noet sw=4 sts=4 */
 | 
			
		||||
							
								
								
									
										41
									
								
								ext/fiddle/conversions.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								ext/fiddle/conversions.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
#ifndef FIDDLE_CONVERSIONS_H
 | 
			
		||||
#define FIDDLE_CONVERSIONS_H
 | 
			
		||||
 | 
			
		||||
#include <fiddle.h>
 | 
			
		||||
 | 
			
		||||
typedef union
 | 
			
		||||
{
 | 
			
		||||
    unsigned char uchar;   /* ffi_type_uchar */
 | 
			
		||||
    signed char   schar;   /* ffi_type_schar */
 | 
			
		||||
    unsigned short ushort; /* ffi_type_sshort */
 | 
			
		||||
    signed short sshort;   /* ffi_type_ushort */
 | 
			
		||||
    unsigned int uint;     /* ffi_type_uint */
 | 
			
		||||
    signed int sint;       /* ffi_type_sint */
 | 
			
		||||
    unsigned long ulong;   /* ffi_type_ulong */
 | 
			
		||||
    signed long slong;     /* ffi_type_slong */
 | 
			
		||||
    float ffloat;          /* ffi_type_float */
 | 
			
		||||
    double ddouble;        /* ffi_type_double */
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
    unsigned LONG_LONG long_long; /* ffi_type_uint64 */
 | 
			
		||||
#endif
 | 
			
		||||
    void * pointer;        /* ffi_type_pointer */
 | 
			
		||||
} fiddle_generic;
 | 
			
		||||
 | 
			
		||||
ffi_type * int_to_ffi_type(int type);
 | 
			
		||||
void value_to_generic(int type, VALUE src, fiddle_generic * dst);
 | 
			
		||||
VALUE generic_to_value(VALUE rettype, fiddle_generic retval);
 | 
			
		||||
 | 
			
		||||
#define VALUE2GENERIC(_type, _src, _dst) value_to_generic(_type, _src, _dst)
 | 
			
		||||
#define INT2FFI_TYPE(_type) int_to_ffi_type(_type)
 | 
			
		||||
#define GENERIC2VALUE(_type, _retval) generic_to_value(_type, _retval)
 | 
			
		||||
 | 
			
		||||
#if SIZEOF_VOIDP == SIZEOF_LONG
 | 
			
		||||
# define PTR2NUM(x)   (ULONG2NUM((unsigned long)(x)))
 | 
			
		||||
# define NUM2PTR(x)   ((void*)(NUM2ULONG(x)))
 | 
			
		||||
#else
 | 
			
		||||
/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */
 | 
			
		||||
# define PTR2NUM(x)   (ULL2NUM((unsigned long long)(x)))
 | 
			
		||||
# define NUM2PTR(x)   ((void*)(NUM2ULL(x)))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										23
									
								
								ext/fiddle/extconf.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								ext/fiddle/extconf.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
require 'mkmf'
 | 
			
		||||
 | 
			
		||||
# :stopdoc:
 | 
			
		||||
 | 
			
		||||
dir_config 'libffi'
 | 
			
		||||
 | 
			
		||||
unless have_header('ffi.h')
 | 
			
		||||
  if have_header('ffi/ffi.h')
 | 
			
		||||
    $defs.push(format('-DUSE_HEADER_HACKS'))
 | 
			
		||||
  else
 | 
			
		||||
    abort "ffi.h is missing. Please install libffi."
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
unless have_library('ffi')
 | 
			
		||||
  abort "libffi is missing. Please install libffi."
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
have_header 'sys/mman.h'
 | 
			
		||||
 | 
			
		||||
create_makefile 'fiddle'
 | 
			
		||||
 | 
			
		||||
# :startdoc:
 | 
			
		||||
							
								
								
									
										30
									
								
								ext/fiddle/fiddle.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								ext/fiddle/fiddle.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
#include <fiddle.h>
 | 
			
		||||
 | 
			
		||||
VALUE mFiddle;
 | 
			
		||||
 | 
			
		||||
void Init_fiddle()
 | 
			
		||||
{
 | 
			
		||||
    mFiddle = rb_define_module("Fiddle");
 | 
			
		||||
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_VOID",      INT2NUM(TYPE_VOID));
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_VOIDP",     INT2NUM(TYPE_VOIDP));
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_CHAR",      INT2NUM(TYPE_CHAR));
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_SHORT",     INT2NUM(TYPE_SHORT));
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_INT",       INT2NUM(TYPE_INT));
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_LONG",      INT2NUM(TYPE_LONG));
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_LONG_LONG", INT2NUM(TYPE_LONG_LONG));
 | 
			
		||||
#endif
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_FLOAT",     INT2NUM(TYPE_FLOAT));
 | 
			
		||||
    rb_define_const(mFiddle, "TYPE_DOUBLE",    INT2NUM(TYPE_DOUBLE));
 | 
			
		||||
 | 
			
		||||
#if defined(HAVE_WINDOWS_H)
 | 
			
		||||
    rb_define_const(mFiddle, "WINDOWS", Qtrue);
 | 
			
		||||
#else
 | 
			
		||||
    rb_define_const(mFiddle, "WINDOWS", Qfalse);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    Init_fiddle_function();
 | 
			
		||||
    Init_fiddle_closure();
 | 
			
		||||
}
 | 
			
		||||
/* vim: set noet sws=4 sw=4: */
 | 
			
		||||
							
								
								
									
										45
									
								
								ext/fiddle/fiddle.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								ext/fiddle/fiddle.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
#ifndef FIDDLE_H
 | 
			
		||||
#define FIDDLE_H
 | 
			
		||||
 | 
			
		||||
#include <ruby.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#if defined(HAVE_WINDOWS_H)
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SYS_MMAN_H
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_HEADER_HACKS
 | 
			
		||||
#include <ffi/ffi.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <ffi.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <closure.h>
 | 
			
		||||
#include <conversions.h>
 | 
			
		||||
#include <function.h>
 | 
			
		||||
 | 
			
		||||
/* FIXME
 | 
			
		||||
 * These constants need to match up with DL. We need to refactor this to use
 | 
			
		||||
 * the DL header files or vice versa.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define TYPE_VOID  0
 | 
			
		||||
#define TYPE_VOIDP 1
 | 
			
		||||
#define TYPE_CHAR  2
 | 
			
		||||
#define TYPE_SHORT 3
 | 
			
		||||
#define TYPE_INT   4
 | 
			
		||||
#define TYPE_LONG  5
 | 
			
		||||
#if HAVE_LONG_LONG
 | 
			
		||||
#define TYPE_LONG_LONG 6
 | 
			
		||||
#endif
 | 
			
		||||
#define TYPE_FLOAT 7
 | 
			
		||||
#define TYPE_DOUBLE 8
 | 
			
		||||
 | 
			
		||||
extern VALUE mFiddle;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
/* vim: set noet sws=4 sw=4: */
 | 
			
		||||
							
								
								
									
										155
									
								
								ext/fiddle/function.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								ext/fiddle/function.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,155 @@
 | 
			
		|||
#include <fiddle.h>
 | 
			
		||||
 | 
			
		||||
VALUE cFiddleFunction;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
deallocate(void *p)
 | 
			
		||||
{
 | 
			
		||||
    ffi_cif *ptr = p;
 | 
			
		||||
    if (ptr->arg_types) xfree(ptr->arg_types);
 | 
			
		||||
    xfree(ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t
 | 
			
		||||
function_memsize(const void *p)
 | 
			
		||||
{
 | 
			
		||||
    /* const */ffi_cif *ptr = (ffi_cif *)p;
 | 
			
		||||
    size_t size = 0;
 | 
			
		||||
 | 
			
		||||
    if (ptr) {
 | 
			
		||||
	size += sizeof(*ptr);
 | 
			
		||||
#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
 | 
			
		||||
	size += ffi_raw_size(ptr);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const rb_data_type_t function_data_type = {
 | 
			
		||||
    "fiddle/function",
 | 
			
		||||
    0, deallocate, function_memsize,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
allocate(VALUE klass)
 | 
			
		||||
{
 | 
			
		||||
    ffi_cif * cif;
 | 
			
		||||
 | 
			
		||||
    return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
initialize(int argc, VALUE argv[], VALUE self)
 | 
			
		||||
{
 | 
			
		||||
    ffi_cif * cif;
 | 
			
		||||
    ffi_type **arg_types;
 | 
			
		||||
    ffi_status result;
 | 
			
		||||
    VALUE ptr, args, ret_type, abi;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    rb_scan_args(argc, argv, "31", &ptr, &args, &ret_type, &abi);
 | 
			
		||||
    if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
 | 
			
		||||
 | 
			
		||||
    Check_Type(args, T_ARRAY);
 | 
			
		||||
 | 
			
		||||
    rb_iv_set(self, "@ptr", ptr);
 | 
			
		||||
    rb_iv_set(self, "@args", args);
 | 
			
		||||
    rb_iv_set(self, "@return_type", ret_type);
 | 
			
		||||
    rb_iv_set(self, "@abi", abi);
 | 
			
		||||
 | 
			
		||||
    TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
 | 
			
		||||
 | 
			
		||||
    arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < RARRAY_LEN(args); i++) {
 | 
			
		||||
	int type = NUM2INT(RARRAY_PTR(args)[i]);
 | 
			
		||||
	arg_types[i] = INT2FFI_TYPE(type);
 | 
			
		||||
    }
 | 
			
		||||
    arg_types[RARRAY_LEN(args)] = NULL;
 | 
			
		||||
 | 
			
		||||
    result = ffi_prep_cif (
 | 
			
		||||
	    cif,
 | 
			
		||||
	    NUM2INT(abi),
 | 
			
		||||
	    RARRAY_LENINT(args),
 | 
			
		||||
	    INT2FFI_TYPE(NUM2INT(ret_type)),
 | 
			
		||||
	    arg_types);
 | 
			
		||||
 | 
			
		||||
    if (result)
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
function_call(int argc, VALUE argv[], VALUE self)
 | 
			
		||||
{
 | 
			
		||||
    ffi_cif * cif;
 | 
			
		||||
    fiddle_generic retval;
 | 
			
		||||
    fiddle_generic *generic_args;
 | 
			
		||||
    void **values;
 | 
			
		||||
    void * fun_ptr;
 | 
			
		||||
    VALUE cfunc, types, cPointer;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    cfunc    = rb_iv_get(self, "@ptr");
 | 
			
		||||
    types    = rb_iv_get(self, "@args");
 | 
			
		||||
    cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
 | 
			
		||||
 | 
			
		||||
    if(argc != RARRAY_LENINT(types)) {
 | 
			
		||||
	rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
 | 
			
		||||
		argc, RARRAY_LENINT(types));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
 | 
			
		||||
 | 
			
		||||
    values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
 | 
			
		||||
    generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < argc; i++) {
 | 
			
		||||
	VALUE type = RARRAY_PTR(types)[i];
 | 
			
		||||
	VALUE src = argv[i];
 | 
			
		||||
 | 
			
		||||
	if(NUM2INT(type) == TYPE_VOIDP) {
 | 
			
		||||
	    if(NIL_P(src)) {
 | 
			
		||||
		src = INT2NUM(0);
 | 
			
		||||
	    } else if(cPointer != CLASS_OF(src)) {
 | 
			
		||||
	        src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
 | 
			
		||||
	    }
 | 
			
		||||
	    src = rb_Integer(src);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
 | 
			
		||||
	values[i] = (void *)&generic_args[i];
 | 
			
		||||
    }
 | 
			
		||||
    values[argc] = NULL;
 | 
			
		||||
 | 
			
		||||
    ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
 | 
			
		||||
 | 
			
		||||
    rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
 | 
			
		||||
#if defined(HAVE_WINDOWS_H)
 | 
			
		||||
    rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    xfree(values);
 | 
			
		||||
    xfree(generic_args);
 | 
			
		||||
 | 
			
		||||
    return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Init_fiddle_function(void)
 | 
			
		||||
{
 | 
			
		||||
    cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
 | 
			
		||||
 | 
			
		||||
    rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
 | 
			
		||||
 | 
			
		||||
#ifdef FFI_STDCALL
 | 
			
		||||
    rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    rb_define_alloc_func(cFiddleFunction, allocate);
 | 
			
		||||
 | 
			
		||||
    rb_define_method(cFiddleFunction, "call", function_call, -1);
 | 
			
		||||
    rb_define_method(cFiddleFunction, "initialize", initialize, -1);
 | 
			
		||||
}
 | 
			
		||||
/* vim: set noet sws=4 sw=4: */
 | 
			
		||||
							
								
								
									
										8
									
								
								ext/fiddle/function.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ext/fiddle/function.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
#ifndef FIDDLE_FUNCTION_H
 | 
			
		||||
#define FIDDLE_FUNCTION_H
 | 
			
		||||
 | 
			
		||||
#include <fiddle.h>
 | 
			
		||||
 | 
			
		||||
void Init_fiddle_function();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										27
									
								
								ext/fiddle/lib/fiddle.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								ext/fiddle/lib/fiddle.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
require 'fiddle.so'
 | 
			
		||||
require 'fiddle/function'
 | 
			
		||||
require 'fiddle/closure'
 | 
			
		||||
require 'dl'
 | 
			
		||||
 | 
			
		||||
module Fiddle
 | 
			
		||||
  Pointer = DL::CPtr
 | 
			
		||||
 | 
			
		||||
  if WINDOWS
 | 
			
		||||
    def self.win32_last_error
 | 
			
		||||
      Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.win32_last_error= error
 | 
			
		||||
      Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.last_error
 | 
			
		||||
    Thread.current[:__FIDDLE_LAST_ERROR__]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.last_error= error
 | 
			
		||||
    Thread.current[:__DL2_LAST_ERROR__] = error
 | 
			
		||||
    Thread.current[:__FIDDLE_LAST_ERROR__] = error
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										17
									
								
								ext/fiddle/lib/fiddle/closure.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								ext/fiddle/lib/fiddle/closure.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
module Fiddle
 | 
			
		||||
  class Closure
 | 
			
		||||
    attr_reader :ctype
 | 
			
		||||
    attr_reader :args
 | 
			
		||||
 | 
			
		||||
    class BlockCaller < Fiddle::Closure
 | 
			
		||||
      def initialize ctype, args, abi = Fiddle::Function::DEFAULT, &block
 | 
			
		||||
        super(ctype, args, abi)
 | 
			
		||||
        @block = block
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def call *args
 | 
			
		||||
        @block.call(*args)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								ext/fiddle/lib/fiddle/function.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ext/fiddle/lib/fiddle/function.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
module Fiddle
 | 
			
		||||
  class Function
 | 
			
		||||
    attr_reader :abi
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										71
									
								
								test/fiddle/helper.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								test/fiddle/helper.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
require 'minitest/autorun'
 | 
			
		||||
require 'dl'
 | 
			
		||||
require 'fiddle'
 | 
			
		||||
 | 
			
		||||
# FIXME: this is stolen from DL and needs to be refactored.
 | 
			
		||||
require_relative '../ruby/envutil'
 | 
			
		||||
 | 
			
		||||
libc_so = libm_so = nil
 | 
			
		||||
 | 
			
		||||
case RUBY_PLATFORM
 | 
			
		||||
when /cygwin/
 | 
			
		||||
  libc_so = "cygwin1.dll"
 | 
			
		||||
  libm_so = "cygwin1.dll"
 | 
			
		||||
when /x86_64-linux/
 | 
			
		||||
  libc_so = "/lib64/libc.so.6"
 | 
			
		||||
  libm_so = "/lib64/libm.so.6"
 | 
			
		||||
when /linux/
 | 
			
		||||
  libdir = '/lib'
 | 
			
		||||
  case [0].pack('L!').size
 | 
			
		||||
  when 4
 | 
			
		||||
    # 32-bit ruby
 | 
			
		||||
    libdir = '/lib32' if File.directory? '/lib32'
 | 
			
		||||
  when 8
 | 
			
		||||
    # 64-bit ruby
 | 
			
		||||
    libdir = '/lib64' if File.directory? '/lib64'
 | 
			
		||||
  end
 | 
			
		||||
  libc_so = File.join(libdir, "libc.so.6")
 | 
			
		||||
  libm_so = File.join(libdir, "libm.so.6")
 | 
			
		||||
when /mingw/, /mswin/
 | 
			
		||||
  require "rbconfig"
 | 
			
		||||
  libc_so = libm_so = RbConfig::CONFIG["RUBY_SO_NAME"].split(/-/, 2)[0] + ".dll"
 | 
			
		||||
when /darwin/
 | 
			
		||||
  libc_so = "/usr/lib/libc.dylib"
 | 
			
		||||
  libm_so = "/usr/lib/libm.dylib"
 | 
			
		||||
when /kfreebsd/
 | 
			
		||||
  libc_so = "/lib/libc.so.0.1"
 | 
			
		||||
  libm_so = "/lib/libm.so.1"
 | 
			
		||||
when /bsd|dragonfly/
 | 
			
		||||
  libc_so = "/usr/lib/libc.so"
 | 
			
		||||
  libm_so = "/usr/lib/libm.so"
 | 
			
		||||
else
 | 
			
		||||
  libc_so = ARGV[0] if ARGV[0] && ARGV[0][0] == ?/
 | 
			
		||||
  libm_so = ARGV[1] if ARGV[1] && ARGV[1][0] == ?/
 | 
			
		||||
  if( !(libc_so && libm_so) )
 | 
			
		||||
    $stderr.puts("libc and libm not found: #{$0} <libc> <libm>")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
libc_so = nil if !libc_so || (libc_so[0] == ?/ && !File.file?(libc_so))
 | 
			
		||||
libm_so = nil if !libm_so || (libm_so[0] == ?/ && !File.file?(libm_so))
 | 
			
		||||
 | 
			
		||||
if !libc_so || !libm_so
 | 
			
		||||
  ruby = EnvUtil.rubybin
 | 
			
		||||
  ldd = `ldd #{ruby}`
 | 
			
		||||
  #puts ldd
 | 
			
		||||
  libc_so = $& if !libc_so && %r{/\S*/libc\.so\S*} =~ ldd
 | 
			
		||||
  libm_so = $& if !libm_so && %r{/\S*/libm\.so\S*} =~ ldd
 | 
			
		||||
  #p [libc_so, libm_so]
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Fiddle::LIBC_SO = libc_so
 | 
			
		||||
Fiddle::LIBM_SO = libm_so
 | 
			
		||||
 | 
			
		||||
module Fiddle
 | 
			
		||||
  class TestCase < MiniTest::Unit::TestCase
 | 
			
		||||
    def setup
 | 
			
		||||
      @libc = DL.dlopen(LIBC_SO)
 | 
			
		||||
      @libm = DL.dlopen(LIBM_SO)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										49
									
								
								test/fiddle/test_closure.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								test/fiddle/test_closure.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
require_relative 'helper'
 | 
			
		||||
 | 
			
		||||
module Fiddle
 | 
			
		||||
  class TestClosure < Fiddle::TestCase
 | 
			
		||||
    def test_argument_errors
 | 
			
		||||
      assert_raises(TypeError) do
 | 
			
		||||
        Closure.new(TYPE_INT, TYPE_INT)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      assert_raises(TypeError) do
 | 
			
		||||
        Closure.new('foo', [TYPE_INT])
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      assert_raises(TypeError) do
 | 
			
		||||
        Closure.new(TYPE_INT, ['meow!'])
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_call
 | 
			
		||||
      closure = Class.new(Closure) {
 | 
			
		||||
        def call
 | 
			
		||||
          10
 | 
			
		||||
        end
 | 
			
		||||
      }.new(TYPE_INT, [])
 | 
			
		||||
 | 
			
		||||
      func = Function.new(closure, [], TYPE_INT)
 | 
			
		||||
      assert_equal 10, func.call
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_returner
 | 
			
		||||
      closure = Class.new(Closure) {
 | 
			
		||||
        def call thing
 | 
			
		||||
          thing
 | 
			
		||||
        end
 | 
			
		||||
      }.new(TYPE_INT, [TYPE_INT])
 | 
			
		||||
 | 
			
		||||
      func = Function.new(closure, [TYPE_INT], TYPE_INT)
 | 
			
		||||
      assert_equal 10, func.call(10)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_block_caller
 | 
			
		||||
      cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
 | 
			
		||||
        one
 | 
			
		||||
      end
 | 
			
		||||
      func = Function.new(cb, [TYPE_INT], TYPE_INT)
 | 
			
		||||
      assert_equal 11, func.call(11)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										19
									
								
								test/fiddle/test_fiddle.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								test/fiddle/test_fiddle.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
require_relative 'helper'
 | 
			
		||||
 | 
			
		||||
class TestFiddle < Fiddle::TestCase
 | 
			
		||||
  def test_constants_match
 | 
			
		||||
    [
 | 
			
		||||
      :TYPE_VOID,
 | 
			
		||||
      :TYPE_VOIDP,
 | 
			
		||||
      :TYPE_CHAR,
 | 
			
		||||
      :TYPE_SHORT,
 | 
			
		||||
      :TYPE_INT,
 | 
			
		||||
      :TYPE_LONG,
 | 
			
		||||
      :TYPE_LONG_LONG,
 | 
			
		||||
      :TYPE_FLOAT,
 | 
			
		||||
      :TYPE_DOUBLE,
 | 
			
		||||
    ].each do |name|
 | 
			
		||||
      assert_equal(DL.const_get(name), Fiddle.const_get(name))
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										66
									
								
								test/fiddle/test_function.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								test/fiddle/test_function.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
require_relative 'helper'
 | 
			
		||||
 | 
			
		||||
module Fiddle
 | 
			
		||||
  class TestFunction < Fiddle::TestCase
 | 
			
		||||
    def setup
 | 
			
		||||
      super
 | 
			
		||||
      Fiddle.last_error = nil
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_default_abi
 | 
			
		||||
      func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
 | 
			
		||||
      assert_equal Function::DEFAULT, func.abi
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_argument_errors
 | 
			
		||||
      assert_raises(TypeError) do
 | 
			
		||||
        Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      assert_raises(TypeError) do
 | 
			
		||||
        Function.new(@libm['sin'], ['foo'], TYPE_DOUBLE)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      assert_raises(TypeError) do
 | 
			
		||||
        Function.new(@libm['sin'], [TYPE_DOUBLE], 'foo')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_call
 | 
			
		||||
      func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
 | 
			
		||||
      assert_in_delta 1.0, func.call(90 * Math::PI / 180), 0.0001
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_argument_count
 | 
			
		||||
      closure = Class.new(Closure) {
 | 
			
		||||
        def call one
 | 
			
		||||
          10 + one
 | 
			
		||||
        end
 | 
			
		||||
      }.new(TYPE_INT, [TYPE_INT])
 | 
			
		||||
      func = Function.new(closure, [TYPE_INT], TYPE_INT)
 | 
			
		||||
 | 
			
		||||
      assert_raises(ArgumentError) do
 | 
			
		||||
        func.call(1,2,3)
 | 
			
		||||
      end
 | 
			
		||||
      assert_raises(ArgumentError) do
 | 
			
		||||
        func.call
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_last_error
 | 
			
		||||
      func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
 | 
			
		||||
 | 
			
		||||
      assert_nil Fiddle.last_error
 | 
			
		||||
      str = func.call("000", "123")
 | 
			
		||||
      refute_nil Fiddle.last_error
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def test_strcpy
 | 
			
		||||
      f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
 | 
			
		||||
      buff = "000"
 | 
			
		||||
      str = f.call(buff, "123")
 | 
			
		||||
      assert_equal("123", buff)
 | 
			
		||||
      assert_equal("123", str.to_s)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue