[前][次][番号順一覧][スレッド一覧]

ruby-changes:14691

From: tenderlove <ko1@a...>
Date: Wed, 3 Feb 2010 10:25:17 +0900 (JST)
Subject: [ruby-changes:14691] Ruby:r26545 (trunk): Wed Feb 3 10:12:09 2010 Aaron Patterson <tenderlove@r...>

tenderlove	2010-02-03 10:23:48 +0900 (Wed, 03 Feb 2010)

  New Revision: 26545

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=26545

  Log:
    Wed Feb  3 10:12:09 2010  Aaron Patterson <tenderlove@r...>
    
            * ext/dl/function.c: DL::Function now uses libffi
    
            * ext/dl/cfunc.c (rb_dl_set_last_error): set to non static so errors
              can be exposed.
    
            * ext/dl/closure.c: DL::Closure will now be used in place of
              ext/dl/callback/*.
    
            * ext/dl/dl.c: legacy callbacks removed in favor of libffi
    
            * ext/dl/dl_converions.(c,h): used for converting ruby types to FFI
              types.
    
            * ext/dl/callback/*: replaced by libffi callbacks.
    
            * ext/dl/lib/dl/callback.rb: Converting internal callbacks to use
              DL::Closure
    
            * ext/dl/lib/dl/closure.rb: Ruby parts of the new DL::Closure object
    
            * ext/dl/lib/dl/import.rb: More conversion to use DL::Closure object
    
            * ext/dl/lib/dl/value.rb (ruby2ffi): adding private method for
              DL::CPtr to ffi value conversion.

  Added files:
    trunk/ext/dl/closure.c
    trunk/ext/dl/dl_conversions.c
    trunk/ext/dl/dl_conversions.h
    trunk/ext/dl/function.c
    trunk/ext/dl/lib/dl/closure.rb
    trunk/test/dl/test_closure.rb
  Removed files:
    trunk/ext/dl/callback/depend
    trunk/ext/dl/callback/extconf.rb
    trunk/ext/dl/callback/mkcallback.rb
  Modified files:
    trunk/ChangeLog
    trunk/ext/dl/cfunc.c
    trunk/ext/dl/dl.c
    trunk/ext/dl/dl.h
    trunk/ext/dl/extconf.rb
    trunk/ext/dl/lib/dl/callback.rb
    trunk/ext/dl/lib/dl/func.rb
    trunk/ext/dl/lib/dl/import.rb
    trunk/ext/dl/lib/dl/value.rb
    trunk/test/dl/test_base.rb
    trunk/test/dl/test_dl2.rb
    trunk/test/dl/test_func.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 26544)
+++ ChangeLog	(revision 26545)
@@ -1,3 +1,30 @@
+Wed Feb  3 10:12:09 2010  Aaron Patterson <tenderlove@r...>
+
+	* ext/dl/function.c: DL::Function now uses libffi
+
+	* ext/dl/cfunc.c (rb_dl_set_last_error): set to non static so errors
+	  can be exposed.
+
+	* ext/dl/closure.c: DL::Closure will now be used in place of
+	  ext/dl/callback/*.
+
+	* ext/dl/dl.c: legacy callbacks removed in favor of libffi
+
+	* ext/dl/dl_converions.(c,h): used for converting ruby types to FFI
+	  types.
+
+	* ext/dl/callback/*: replaced by libffi callbacks.
+
+	* ext/dl/lib/dl/callback.rb: Converting internal callbacks to use
+	  DL::Closure
+	
+	* ext/dl/lib/dl/closure.rb: Ruby parts of the new DL::Closure object
+
+	* ext/dl/lib/dl/import.rb: More conversion to use DL::Closure object
+
+	* ext/dl/lib/dl/value.rb (ruby2ffi): adding private method for
+	  DL::CPtr to ffi value conversion.
+
 Tue Feb  2 18:15:12 2010  Nobuyoshi Nakada  <nobu@r...>
 
 	* ext/socket/socket.c: turn on do_not_reverse_lookup by default,
Index: ext/dl/cfunc.c
===================================================================
--- ext/dl/cfunc.c	(revision 26544)
+++ ext/dl/cfunc.c	(revision 26545)
@@ -16,7 +16,7 @@
     return rb_thread_local_aref(rb_thread_current(), id_last_error);
 }
 
-static VALUE
+VALUE
 rb_dl_set_last_error(VALUE self, VALUE val)
 {
     rb_thread_local_aset(rb_thread_current(), id_last_error, val);
@@ -33,7 +33,7 @@
     return rb_thread_local_aref(rb_thread_current(), id_win32_last_error);
 }
 
-static VALUE
+VALUE
 rb_dl_set_win32_last_error(VALUE self, VALUE val)
 {
     rb_thread_local_aset(rb_thread_current(), id_win32_last_error, val);
Index: ext/dl/dl.c
===================================================================
--- ext/dl/dl.c	(revision 26544)
+++ ext/dl/dl.c	(revision 26545)
@@ -77,19 +77,6 @@
     return PTR2NUM((void*)val);
 }
 
-static void
-rb_dl_init_callbacks(VALUE dl)
-{
-    static const char cb[] = "dl/callback.so";
-
-    rb_autoload(dl, rb_intern_const("CdeclCallbackAddrs"), cb);
-    rb_autoload(dl, rb_intern_const("CdeclCallbackProcs"), cb);
-#ifdef FUNC_STDCALL
-    rb_autoload(dl, rb_intern_const("StdcallCallbackAddrs"), cb);
-    rb_autoload(dl, rb_intern_const("StdcallCallbackProcs"), cb);
-#endif
-}
-
 void
 Init_dl(void)
 {
@@ -107,8 +94,6 @@
     rb_define_const(rb_mDL, "MAX_CALLBACK", INT2NUM(MAX_CALLBACK));
     rb_define_const(rb_mDL, "DLSTACK_SIZE", INT2NUM(DLSTACK_SIZE));
 
-    rb_dl_init_callbacks(rb_mDL);
-
     rb_define_const(rb_mDL, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
     rb_define_const(rb_mDL, "RTLD_LAZY",   INT2NUM(RTLD_LAZY));
     rb_define_const(rb_mDL, "RTLD_NOW",    INT2NUM(RTLD_NOW));
@@ -162,4 +147,6 @@
     Init_dlhandle();
     Init_dlcfunc();
     Init_dlptr();
+    Init_dlfunction();
+    Init_dlclosure();
 }
Index: ext/dl/dl.h
===================================================================
--- ext/dl/dl.h	(revision 26544)
+++ ext/dl/dl.h	(revision 26545)
@@ -3,6 +3,12 @@
 
 #include <ruby.h>
 
+#ifdef USE_HEADER_HACKS
+#include <ffi/ffi.h>
+#else
+#include <ffi.h>
+#endif
+
 #if !defined(FUNC_CDECL)
 #  define FUNC_CDECL(x) x
 #endif
@@ -221,4 +227,9 @@
 VALUE rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func);
 VALUE rb_dlptr_malloc(long size, freefunc_t func);
 
+VALUE rb_dl_set_last_error(VALUE self, VALUE val);
+#if defined(HAVE_WINDOWS_H)
+VALUE rb_dl_set_win32_last_error(VALUE self, VALUE val);
 #endif
+
+#endif
Index: ext/dl/function.c
===================================================================
--- ext/dl/function.c	(revision 0)
+++ ext/dl/function.c	(revision 26545)
@@ -0,0 +1,233 @@
+/* -*- C -*-
+ * $Id$
+ */
+
+#include <ruby.h>
+#include <errno.h>
+#include "dl.h"
+#include <dl_conversions.h>
+
+VALUE rb_cDLFunction;
+
+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
+} dl_generic;
+
+static void
+dlfunction_free(ffi_cif *ptr)
+{
+    if(ptr->arg_types) xfree(ptr->arg_types);
+    xfree(ptr);
+}
+
+static size_t
+dlfunction_memsize(ffi_cif *ptr)
+{
+    size_t size = 0;
+    if(ptr) {
+	size += sizeof(*ptr);
+	size += ffi_raw_size(ptr);
+    }
+    return size;
+}
+
+const rb_data_type_t dlfunction_data_type = {
+    "dl/function",
+    0, dlfunction_free, dlfunction_memsize,
+};
+
+static VALUE
+rb_dlfunc_allocate(VALUE klass)
+{
+    ffi_cif * cif;
+
+    return TypedData_Make_Struct(klass, ffi_cif, &dlfunction_data_type, cif);
+}
+
+static VALUE
+rb_dlfunction_native_init(VALUE self, VALUE args, VALUE ret_type, VALUE abi)
+{
+    ffi_cif * cif;
+    ffi_type **arg_types;
+
+    TypedData_Get_Struct(self, ffi_cif, &dlfunction_data_type, cif);
+
+    arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
+
+    int i;
+    for(i = 0; i < RARRAY_LEN(args); i++) {
+	int type = NUM2INT(RARRAY_PTR(args)[i]);
+	arg_types[i] = DL2FFI_TYPE(type);
+    }
+    arg_types[RARRAY_LEN(args)] = NULL;
+
+    ffi_status result = ffi_prep_cif(
+	    cif,
+	    NUM2INT(abi),
+	    RARRAY_LEN(args),
+	    DL2FFI_TYPE(NUM2INT(ret_type)),
+	    arg_types);
+
+    if(result)
+	rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
+
+    return self;
+}
+
+static void
+dl2generic(int dl_type, VALUE src, dl_generic * dst)
+{
+    int signed_p = 1;
+
+    if(dl_type < 0) {
+	dl_type = -1 * dl_type;
+	signed_p = 0;
+    }
+
+    switch(dl_type) {
+	case DLTYPE_VOID:
+	    break;
+	case DLTYPE_VOIDP:
+	    dst->pointer = NUM2PTR(rb_Integer(src));
+	    break;
+	case DLTYPE_CHAR:
+	case DLTYPE_SHORT:
+	case DLTYPE_INT:
+	    dst->sint = NUM2INT(src);
+	    break;
+	case DLTYPE_LONG:
+	    if(signed_p)
+		dst->slong = NUM2LONG(src);
+	    else
+		dst->ulong = NUM2LONG(src);
+	    break;
+#if HAVE_LONG_LONG
+	case DLTYPE_LONG_LONG:
+            dst->long_long = rb_big2ull(src);
+	    break;
+#endif
+	case DLTYPE_FLOAT:
+	    dst->ffloat = NUM2DBL(src);
+	    break;
+	case DLTYPE_DOUBLE:
+	    dst->ddouble = NUM2DBL(src);
+	    break;
+	default:
+	    rb_raise(rb_eRuntimeError, "unknown type %d", dl_type);
+    }
+}
+
+static VALUE
+unwrap_ffi(VALUE rettype, dl_generic retval)
+{
+    int signed_p = 1;
+    int dl_type = NUM2INT(rettype);
+
+    if(dl_type < 0) {
+	dl_type = -1 * dl_type;
+	signed_p = 0;
+    }
+
+    switch(dl_type) {
+	case DLTYPE_VOID:
+	    return Qnil;
+	case DLTYPE_VOIDP:
+	    return rb_dlptr_new((void *)retval.pointer, 0, NULL);
+	case DLTYPE_CHAR:
+	case DLTYPE_SHORT:
+	case DLTYPE_INT:
+	    return INT2NUM(retval.sint);
+	case DLTYPE_LONG:
+	    if(signed_p) return LONG2NUM(retval.slong);
+	    return LONG2NUM(retval.ulong);
+#if HAVE_LONG_LONG
+	case DLTYPE_LONG_LONG:
+	    return rb_ll2inum(retval.long_long);
+	    break;
+#endif
+	case DLTYPE_FLOAT:
+	    return rb_float_new(retval.ffloat);
+	case DLTYPE_DOUBLE:
+	    return rb_float_new(retval.ddouble);
+	default:
+	    rb_raise(rb_eRuntimeError, "unknown type %d", dl_type);
+    }
+}
+
+static VALUE
+rb_dlfunction_call(int argc, VALUE argv[], VALUE self)
+{
+    ffi_cif * cif;
+    dl_generic retval;
+    dl_generic *generic_args;
+    void **values;
+    void * fun_ptr;
+
+    TypedData_Get_Struct(self, ffi_cif, &dlfunction_data_type, cif);
+
+    values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
+    generic_args = xcalloc((size_t)argc, (size_t)sizeof(dl_generic));
+
+    VALUE cfunc = rb_iv_get(self, "@cfunc");
+    VALUE types = rb_iv_get(self, "@args");
+
+    int i;
+    for(i = 0; i < argc; i++) {
+	VALUE dl_type = RARRAY_PTR(types)[i];
+	VALUE src = rb_funcall(self,
+		rb_intern("ruby2ffi"),
+		2,
+		argv[i],
+		dl_type
+		);
+
+	dl2generic(NUM2INT(dl_type), src, &generic_args[i]);
+	values[i] = (void *)&generic_args[i];
+    }
+    values[argc] = NULL;
+
+    ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
+
+    rb_dl_set_last_error(self, INT2NUM(errno));
+#if defined(HAVE_WINDOWS_H)
+    rb_dl_set_win32_last_error(self, INT2NUM(GetLastError()));
+#endif
+
+    xfree(values);
+    xfree(generic_args);
+
+    return unwrap_ffi(rb_funcall(cfunc, rb_intern("ctype"), 0), retval);
+}
+
+void
+Init_dlfunction(void)
+{
+    rb_cDLFunction = rb_define_class_under(rb_mDL, "Function", rb_cObject);
+
+    rb_define_const(rb_cDLFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
+
+#ifdef FFI_STDCALL
+    rb_define_const(rb_cDLFunction, "STDCALL", INT2NUM(FFI_STDCALL));
+#endif
+
+    rb_define_alloc_func(rb_cDLFunction, rb_dlfunc_allocate);
+
+    rb_define_private_method(rb_cDLFunction, "native_call", rb_dlfunction_call, -1);
+
+    rb_define_private_method(rb_cDLFunction, "native_init", rb_dlfunction_native_init, 3);
+}
+/* vim: set noet sw=4 sts=4 */
Index: ext/dl/lib/dl/import.rb
===================================================================
--- ext/dl/lib/dl/import.rb	(revision 26544)
+++ ext/dl/lib/dl/import.rb	(revision 26545)
@@ -1,4 +1,5 @@
 require 'dl'
+require 'dl/closure'
 require 'dl/func.rb'
 require 'dl/struct.rb'
 require 'dl/cparser.rb'
@@ -211,9 +212,11 @@
     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
+      closure = Class.new(DL::Closure) {
+        define_method(:call, block)
+      }.new(ctype, argtype)
+
+      Function.new(closure, argtype)
     end
 
     def create_temp_function(name, ctype, argtype, call_type = nil)
Index: ext/dl/lib/dl/func.rb
===================================================================
--- ext/dl/lib/dl/func.rb	(revision 26544)
+++ ext/dl/lib/dl/func.rb	(revision 26545)
@@ -1,4 +1,5 @@
 require 'dl'
+require 'dl/closure'
 require 'dl/callback'
 require 'dl/stack'
 require 'dl/value'
@@ -9,18 +10,17 @@
     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 = DEFAULT, &block
+      if block_given?
+        @cfunc = Class.new(DL::Closure) {
+          define_method(:call, block)
+        }.new(cfunc.ctype, argtypes)
       else
-        @unsigned = false
+        @cfunc  = cfunc
       end
-      if( proc )
-        bind(&proc)
-      end
+
+      @args   = argtypes
+      native_init(@args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi)
     end
 
     def to_i()
@@ -32,11 +32,10 @@
     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 block_given?
+        args.find { |a| DL::Function === a }.bind_at_call(&block)
+      end
+      native_call(*args)
     end
 
     def wrap_result(r)
@@ -52,33 +51,16 @@
     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}")
+      @cfunc = Class.new(DL::Closure) {
+        def initialize ctype, args, block
+          super(ctype, args)
+          @block = block
         end
-        if( @cfunc.ptr == 0 )
-          raise(RuntimeException, "can't bind C function.")
+
+        def call *args
+          @block.call(*args)
         end
-      end
+      }.new(@cfunc.ctype, @args, block)
     end
 
     def unbind()
Index: ext/dl/lib/dl/callback.rb
===================================================================
--- ext/dl/lib/dl/callback.rb	(revision 26544)
+++ ext/dl/lib/dl/callback.rb	(revision 26545)
@@ -1,26 +1,21 @@
 require 'dl'
+require 'dl/closure'
 require 'thread'
 
 module DL
   SEM = Mutex.new
 
-  def set_callback_internal(proc_entry, addr_entry, argc, ty, &cbp)
+  CdeclCallbackProcs = {}
+  CdeclCallbackAddrs = {}
+
+  def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = DL::Function::DEFAULT, &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
-      }
-    }
-    addr
+
+    closure = DL::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp)
+    proc_entry[closure.to_i] = closure
+    closure.to_i
   end
 
   def set_cdecl_callback(ty, argc, &cbp)
@@ -28,32 +23,14 @@
   end
 
   def set_stdcall_callback(ty, argc, &cbp)
-    set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
+    set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, DL::Function::STDCALL, &cbp)
   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
-      }
-    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
+    addr = addr.to_i
+    return false unless proc_entry.key?(addr)
+    proc_entry.delete(addr)
+    true
   end
 
   def remove_cdecl_callback(addr, ctype = nil)
Index: ext/dl/lib/dl/closure.rb
===================================================================
--- ext/dl/lib/dl/closure.rb	(revision 0)
+++ ext/dl/lib/dl/closure.rb	(revision 26545)
@@ -0,0 +1,19 @@
+require 'dl'
+
+module DL
+  class Closure
+    attr_reader :ctype
+    attr_reader :args
+
+    class BlockCaller < DL::Closure
+      def initialize ctype, args, abi = DL::Function::DEFAULT, &block
+        super(ctype, args, abi)
+        @block = block
+      end
+
+      def call *args
+        @block.call(*args)
+      end
+    end
+  end
+end
Index: ext/dl/lib/dl/value.rb
===================================================================
--- ext/dl/lib/dl/value.rb	(revision 26544)
+++ ext/dl/lib/dl/value.rb	(revision 26545)
@@ -36,16 +36,20 @@
       end
     end
 
-    def wrap_args(args, tys, funcs, &block)
-      result = []
-      tys ||= []
-      args.each_with_index{|arg, idx|
-        result.push(wrap_arg(arg, tys[idx], funcs, &block))
-      }
-      result
+    def ruby2ffi arg, type
+      return arg unless type == TYPE_VOIDP
+      case arg
+      when nil
+        0
+      when CPtr
+        arg.to_i
+      else
+        CPtr[arg].to_i
+      end
     end
+    private :ruby2ffi
 
-    def wrap_arg(arg, ty, funcs, &block)
+    def wrap_arg(arg, ty, funcs = [], &block)
         funcs ||= []
         case arg
         when nil
Index: ext/dl/dl_conversions.c
===================================================================
--- ext/dl/dl_conversions.c	(revision 0)
+++ ext/dl/dl_conversions.c	(revision 26545)
@@ -0,0 +1,38 @@
+#include <dl_conversions.h>
+
+ffi_type * rb_dl_type_to_ffi_type(int dl_type)
+{
+    int signed_p = 1;
+
+    if(dl_type < 0) {
+	dl_type = -1 * dl_type;
+	signed_p = 0;
+    }
+
+    switch(dl_type) {
+	case DLTYPE_VOID:
+	    return &ffi_type_void;
+	case DLTYPE_VOIDP:
+	    return &ffi_type_pointer;
+	case DLTYPE_CHAR:
+	    return signed_p ? &ffi_type_schar : &ffi_type_uchar;
+	case DLTYPE_SHORT:
+	    return signed_p ? &ffi_type_sshort : &ffi_type_ushort;
+	case DLTYPE_INT:
+	    return signed_p ? &ffi_type_sint : &ffi_type_uint;
+	case DLTYPE_LONG:
+	    return signed_p ? &ffi_type_slong : &ffi_type_ulong;
+#if HAVE_LONG_LONG
+	case DLTYPE_LONG_LONG:
+            return &ffi_type_uint64;
+	    break;
+#endif
+	case DLTYPE_FLOAT:
+	    return &ffi_type_float;
+	case DLTYPE_DOUBLE:
+	    return &ffi_type_double;
+	default:
+            rb_raise(rb_eRuntimeError, "unknown type %d", dl_type);
+    }
+    return &ffi_type_pointer;
+}
Index: ext/dl/extconf.rb
===================================================================
--- ext/dl/extconf.rb	(revision 26544)
+++ ext/dl/extconf.rb	(revision 26545)
@@ -8,8 +8,30 @@
   ["dl.h", "$(HDRDIR)"],
 ]
 
+if pkg_config("libffi")
+  # libffi closure api must be switched depending on the version
+  if system("pkg-config --atleast-version=3.0.9 libffi")
+    $defs.push(format('-DUSE_NEW_CLOSURE_API'))
+  end
+else
+  dir_config('ffi', '/usr/include', '/usr/lib')
+end
+
+unless have_header('ffi.h')
+  if have_header('ffi/ffi.h')
+    $defs.push(format('-DUSE_HEADER_HACKS'))
+  else
+    abort "ffi is missing"
+  end
+end
+
+unless have_library('ffi')
+  abort "ffi is missing"
+end
+
 check = true
 if( have_header("dlfcn.h") )
+
   have_library("dl")
   check &&= have_func("dlopen")
   check &&= have_func("dlclose")
Index: ext/dl/callback/depend
===================================================================
--- ext/dl/callback/depend	(revision 26544)
+++ ext/dl/callback/depend	(revision 26545)
@@ -1,15 +0,0 @@
-src: callback.c \
-     callback-0.c callback-1.c callback-2.c \
-     callback-3.c callback-4.c callback-5.c \
-     callback-6.c callback-7.c callback-8.c
-
-$(OBJS): $(hdrdir)/ruby.h
-
-callback-0.c callback-1.c callback-2.c \
-callback-3.c callback-4.c callback-5.c \
-callback-6.c callback-7.c callback-8.c \
-	: callback.c
-
-callback.c: $(srcdir)/mkcallback.rb $(srcdir)/../dl.h
-	@echo "generating callback.c"
-	@$(RUBY) $(srcdir)/mkcallback.rb -output=callback $(srcdir)/../dl.h
Index: ext/dl/callback/mkcallback.rb
===================================================================
--- ext/dl/callback/mkcallback.rb	(revision 26544)
+++ ext/dl/callback/mkcallback.rb	(revision 26545)
@@ -1,238 +0,0 @@
-#!ruby -s
-$output ||= "callback"
-$out = open("#{$output}.c", "w")
-
-$dl_h = ARGV[0] || "dl.h"
-
-# import DLSTACK_SIZE, DLSTACK_ARGS and so on
-File.open($dl_h){|f|
-  pre = ""
-  f.each{|line|
-    line.chop!
-    if( line[-1] == ?\\ )
-      line.chop!
-      line.concat(" ")
-      pre += line
-      next
-    end
-    if( pre.size > 0 )
-      line = pre + line
-      pre  = ""
-    end
-    case line
-    when /#define\s+DLSTACK_SIZE\s+\(?(\d+)\)?/
-      DLSTACK_SIZE = $1.to_i
-    when /#define\s+DLSTACK_ARGS\s+(.+)/
-      DLSTACK_ARGS = $1.to_i
-    when /#define\s+DLTYPE_([A-Z_]+)\s+\(?(\d+)\)?/
-      eval("#{$1} = #{$2}")
-    when /#define\s+MAX_DLTYPE\s+\(?(\d+)\)?/
-      MAX_DLTYPE  = $1.to_i
-    when /#define\s+MAX_CALLBACK\s+\(?(\d+)\)?/
-      MAX_CALLBACK = $1.to_i
-    end
-  }
-}
-
-CDECL = "cdecl"
-STDCALL = "stdcall"
-
-CALLTYPES = [CDECL, STDCALL]
-
-DLTYPE = {
-  VOID => {
-    :name => 'void',
-    :type => 'void',
-    :conv => nil,
-  },
-  CHAR => {
-    :name => 'char',
-    :type => 'char',
-    :conv => 'NUM2CHR(%s)'
-  },
-  SHORT => {
-    :name => 'short',
-    :type => 'short',
-    :conv => 'NUM2INT(%s)',
-  },
-  INT => {
-    :name => 'int',
-    :type => 'int',
-    :conv => 'NUM2INT(%s)',
-  },
-  LONG  => {
-    :name => 'long',
-    :type => 'long',
-    :conv => 'NUM2LONG(%s)',
-  },
-  LONG_LONG => {
-    :name => 'long_long',
-    :type => 'LONG_LONG',
-    :conv => 'NUM2LL(%s)',
-  },
-  FLOAT => {
-    :name => 'float',
-    :type => 'float',
-    :conv => '(float)RFLOAT_VALUE(%s)',
-  },
-  DOUBLE => {
-    :name => 'double',
-    :type => 'double',
-    :conv => 'RFLOAT_VALUE(%s)',
-  },
-  VOIDP => {
-    :name => 'ptr',
-    :type => 'void *',
-    :conv => 'NUM2PTR(%s)',
-  },
-}
-
-
-def func_name(ty, argc, n, calltype)
-  "rb_dl_callback_#{DLTYPE[ty][:name]}_#{argc}_#{n}_#{calltype}"
-end
-
-$out << (<<EOS)
-#include "ruby.h"
-
-VALUE rb_DLCdeclCallbackAddrs, rb_DLCdeclCallbackProcs;
-#ifdef FUNC_STDCALL
-VALUE rb_DLStdcallCallbackAddrs, rb_DLStdcallCallbackProcs;
-#endif
-/*static void *cdecl_callbacks[MAX_DLTYPE][MAX_CALLBACK];*/
-#ifdef FUNC_STDCALL
-/*static void *stdcall_callbacks[MAX_DLTYPE][MAX_CALLBACK];*/
-#endif
-ID   rb_dl_cb_call;
-EOS
-
-def foreach_proc_entry
-  for calltype in CALLTYPES
-    case calltype
-    when CDECL
-      proc_entry = "rb_DLCdeclCallbackProcs"
-    when STDCALL
-      proc_entry = "rb_DLStdcallCallbackProcs"
-    else
-      raise "unknown calltype: #{calltype}"
-    end
-    yield calltype, proc_entry
-  end
-end
-
-def gencallback(ty, calltype, proc_entry, argc, n)
-  <<-EOS
-#{calltype == STDCALL ? "\n#ifdef FUNC_STDCALL" : ""}
-static #{DLTYPE[ty][:type]}
-FUNC_#{calltype.upcase}(#{func_name(ty,argc,n,calltype)})(#{(0...argc).collect{|i| "DLSTACK_TYPE stack" + i.to_s}.join(", ")})
-{
-    VALUE ret, cb#{argc > 0 ? ", args[#{argc}]" : ""};
-#{
-      (0...argc).collect{|i|
-	"    args[%d] = LONG2NUM(stack%d);" % [i,i]
-      }.join("\n")
-}
-    cb = rb_ary_entry(rb_ary_entry(#{proc_entry}, #{ty}), #{(n * DLSTACK_SIZE) + argc});
-    ret = rb_funcall2(cb, rb_dl_cb_call, #{argc}, #{argc > 0 ? 'args' : 'NULL'});
-    return #{DLTYPE[ty][:conv] ? DLTYPE[ty][:conv] % "ret" : ""};
-}
-#{calltype == STDCALL ? "#endif\n" : ""}
-  EOS
-end
-
-def gen_push_proc_ary(ty, aryname)
-  sprintf("    rb_ary_push(#{aryname}, rb_ary_new3(%d,%s));",
-          MAX_CALLBACK * DLSTACK_SIZE,
-          (0...MAX_CALLBACK).collect{
-            (0...DLSTACK_SIZE).collect{ "Qnil" }.join(",")
-          }.join(","))
-end
-
-def gen_push_addr_ary(ty, aryname, calltype)
-  sprintf("    rb_ary_push(#{aryname}, rb_ary_new3(%d,%s));",
-          MAX_CALLBACK * DLSTACK_SIZE,
-          (0...MAX_CALLBACK).collect{|i|
-            (0...DLSTACK_SIZE).collect{|argc|
-              "PTR2NUM(%s)" % func_name(ty,argc,i,calltype)
-            }.join(",")
-          }.join(","))
-end
-
-def gen_callback_file(ty)
-  filename = "#{$output}-#{ty}.c"
-  initname = "rb_dl_init_callbacks_#{ty}"
-  body = <<-EOS
-#include "dl.h"
-
-extern VALUE rb_DLCdeclCallbackAddrs, rb_DLCdeclCallbackProcs;
-#ifdef FUNC_STDCALL
-extern VALUE rb_DLStdcallCallbackAddrs, rb_DLStdcallCallbackProcs;
-#endif
-extern ID   rb_dl_cb_call;
-    EOS
-  yield body
-  body << <<-EOS
-void
-#{initname}()
-{
-#{gen_push_proc_ary(ty, "rb_DLCdeclCallbackProcs")}
-#{gen_push_addr_ary(ty, "rb_DLCdeclCallbackAddrs", CDECL)}
-#ifdef FUNC_STDCALL
-#{gen_push_proc_ary(ty, "rb_DLStdcallCallbackProcs")}
-#{gen_push_addr_ary(ty, "rb_DLStdcallCallbackAddrs", STDCALL)}
-#endif
-}
-    EOS
-  [filename, initname, body]
-end
-
-callbacks = []
-for ty in 0...MAX_DLTYPE
-  filename, initname, body = gen_callback_file(ty) {|f|
-    foreach_proc_entry do |calltype, proc_entry|
-      for argc in 0...DLSTACK_SIZE
-        for n in 0...MAX_CALLBACK
-          f << gencallback(ty, calltype, proc_entry, argc, n)
-        end
-      end
-    end
-  }
-  $out << "void #{initname}();\n"
-  callbacks << [filename, body]
-end
-
-$out << (<<EOS)
-void
-Init_callback(void)
-{
-    VALUE tmp;
-    VALUE rb_mDL = rb_path2class("DL");
-
-    rb_dl_cb_call = rb_intern("call");
-
-    tmp = rb_DLCdeclCallbackProcs = rb_ary_new();
-    rb_define_const(rb_mDL, "CdeclCallbackProcs", tmp);
-
-    tmp = rb_DLCdeclCallbackAddrs = rb_ary_new();
-    rb_define_const(rb_mDL, "CdeclCallbackAddrs", tmp);
-
-#ifdef FUNC_STDCALL
-    tmp = rb_DLStdcallCallbackProcs = rb_ary_new();
-    rb_define_const(rb_mDL, "StdcallCallbackProcs", tmp);
-
-    tmp = rb_DLStdcallCallbackAddrs = rb_ary_new();
-    rb_define_const(rb_mDL, "StdcallCallbackAddrs", tmp);
-#endif
-
-#{
-    (0...MAX_DLTYPE).collect{|ty|
-      "    rb_dl_init_callbacks_#{ty}();"
-    }.join("\n")
-}
-}
-EOS
-$out.close
-
-for filename, body in callbacks
-  open(filename, "wb") {|f| f.puts body}
-end
Index: ext/dl/callback/extconf.rb
===================================================================
--- ext/dl/callback/extconf.rb	(revision 26544)
+++ ext/dl/callback/extconf.rb	(revision 26545)
@@ -1,14 +0,0 @@
-require 'mkmf'
-
-if compiled?("dl")
-  callbacks = (0..8).map{|i| "callback-#{i}"}.unshift("callback")
-  callback_srcs = callbacks.map{|basename| "#{basename}.c"}
-  callback_objs = callbacks.map{|basename| "#{basename}.o"}
-
-  $distcleanfiles << '$(SRCS)'
-  $srcs = callback_srcs
-  $objs = callback_objs
-  $INCFLAGS << " -I$(srcdir)/.."
-
-  create_makefile("dl/callback")
-end
Index: ext/dl/closure.c
===================================================================
--- ext/dl/closure.c	(revision 0)
+++ ext/dl/closure.c	(revision 26545)
@@ -0,0 +1,228 @@
+/* -*- C -*-
+ * $Id$
+ */
+
+#include <ruby.h>
+#include "dl.h"
+#include <sys/mman.h>
+#include <dl_conversions.h>
+
+VALUE rb_cDLClosure;
+
+typedef struct {
+    void * code;
+    ffi_closure *pcl;
+    ffi_cif * cif;
+    int argc;
+    ffi_type **argv;
+} dl_closure;
+
+static void
+dlclosure_free(void * ptr)
+{
+    dl_closure * cls = (dl_closure *)ptr;
+#ifdef USE_NEW_CLOSURE_API
+    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
+dlclosure_memsize(const void * ptr)
+{
+    dl_closure * cls = (dl_closure *)ptr;
+
+    size_t size = 0;
+    if(ptr) {
+	size += sizeof(*cls);
+	size += ffi_raw_size(cls->cif);
+	size += sizeof(*cls->argv);
+	size += sizeof(ffi_closure);
+    }
+    return size;
+}
+
+const rb_data_type_t dlclosure_data_type = {
+    "dl/closure",
+    0, dlclosure_free, dlclosure_memsize,
+};
+
+void
+dlc_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_LEN(rbargs);
+    VALUE *params   = xcalloc(argc, sizeof(VALUE *));
+
+    int i;
+    for(i = 0; i < argc; i++) {
+        int dl_type = NUM2INT(RARRAY_PTR(rbargs)[i]);
+        switch(dl_type) {
+            case DLTYPE_VOID:
+                argc = 0;
+                break;
+            case DLTYPE_INT:
+                params[i] = INT2NUM(*(int *)args[i]);
+                break;
+            case DLTYPE_VOIDP:
+                params[i] = rb_dlptr_new(*(void **)args[i], 0, NULL);
+                break;
+            case DLTYPE_LONG:
+                params[i] = LONG2NUM(*(long *)args[i]);
+                break;
+            case DLTYPE_CHAR:
+                params[i] = INT2NUM(*(char *)args[i]);
+                break;
+            case DLTYPE_DOUBLE:
+                params[i] = rb_float_new(*(double *)args[i]);
+                break;
+            case DLTYPE_FLOAT:
+                params[i] = rb_float_new(*(float *)args[i]);
+                break;
+#if HAVE_LONG_LONG
+            case DLTYPE_LONG_LONG:
+                params[i] = rb_ull2inum(*(unsigned LONG_LONG *)args[i]);
+                break;
+#endif
+            default:
+	        rb_raise(rb_eRuntimeError, "closure args: %d", dl_type);
+        }
+    }
+
+    VALUE ret = rb_funcall2(self, rb_intern("call"), argc, params);
+
+    int dl_type = NUM2INT(ctype);
+    switch(dl_type) {
+        case DLTYPE_VOID:
+            break;
+        case DLTYPE_LONG:
+            *(long *)resp = NUM2LONG(ret);
+            break;
+        case DLTYPE_CHAR:
+            *(char *)resp = NUM2INT(ret);
+            break;
+        case DLTYPE_VOIDP:
+            *(void **)resp = NUM2PTR(ret);
+            break;
+        case DLTYPE_INT:
+            *(int *)resp = NUM2INT(ret);
+            break;
+        case DLTYPE_DOUBLE:
+            *(double *)resp = NUM2DBL(ret);
+            break;
+        case DLTYPE_FLOAT:
+            *(float *)resp = NUM2DBL(ret);
+            break;
+#if HAVE_LONG_LONG
+        case DLTYPE_LONG_LONG:
+            *(unsigned LONG_LONG *)resp = rb_big2ull(ret);
+            break;
+#endif
+        default:
+            rb_raise(rb_eRuntimeError, "closure retval: %d", dl_type);
+    }
+    xfree(params);
+}
+
+static VALUE
+rb_dlclosure_allocate(VALUE klass)
+{
+    dl_closure * closure;
+
+    VALUE i = TypedData_Make_Struct(klass, dl_closure,
+	    &dlclosure_data_type, closure);
+
+#ifdef USE_NEW_CLOSURE_API
+    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
+rb_dlclosure_init(int rbargc, VALUE argv[], VALUE self)
+{
+    VALUE ret;
+    VALUE args;
+    VALUE abi;
+
+    dl_closure * cl;
+    ffi_cif * cif;
+    ffi_closure *pcl;
+
+    if(2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
+	abi = INT2NUM(FFI_DEFAULT_ABI);
+
+    int i;
+    int argc = RARRAY_LEN(args);
+
+    TypedData_Get_Struct(self, dl_closure, &dlclosure_data_type, cl);
+
+    cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
+
+    for(i = 0; i < argc; i++) {
+        int dltype = NUM2INT(RARRAY_PTR(args)[i]);
+        cl->argv[i] = DL2FFI_TYPE(dltype);
+    }
+    cl->argv[argc] = NULL;
+
+    rb_iv_set(self, "@ctype", ret);
+    rb_iv_set(self, "@args", args);
+
+    cif = cl->cif;
+    pcl = cl->pcl;
+
+    ffi_status result = ffi_prep_cif(cif, NUM2INT(abi), argc,
+                DL2FFI_TYPE(NUM2INT(ret)),
+		cl->argv);
+
+    if(FFI_OK != result)
+	rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
+
+#ifdef USE_NEW_CLOSURE_API
+    result = ffi_prep_closure_loc(pcl, cif, dlc_callback,
+		(void *)self, cl->code);
+#else
+    result = ffi_prep_closure(pcl, cif, dlc_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
+rb_dlclosure_to_i(VALUE self)
+{
+    dl_closure * cl;
+
+    TypedData_Get_Struct(self, dl_closure, &dlclosure_data_type, cl);
+
+    void * code = cl->code;
+
+    return PTR2NUM(code);
+}
+
+void
+Init_dlclosure(void)
+{
+    rb_cDLClosure = rb_define_class_under(rb_mDL, "Closure", rb_cObject);
+    rb_define_alloc_func(rb_cDLClosure, rb_dlclosure_allocate);
+
+    rb_define_method(rb_cDLClosure, "initialize", rb_dlclosure_init, -1);
+    rb_define_method(rb_cDLClosure, "to_i", rb_dlclosure_to_i, 0);
+}
+/* vim: set noet sw=4 sts=4 */
Index: ext/dl/dl_conversions.h
===================================================================
--- ext/dl/dl_conversions.h	(revision 0)
+++ ext/dl/dl_conversions.h	(revision 26545)
@@ -0,0 +1,10 @@
+#ifndef DL_CONVERSIONS
+#define DL_CONVERSIONS
+
+#include <dl.h>
+
+#define DL2FFI_TYPE(a) rb_dl_type_to_ffi_type(a)
+
+ffi_type * rb_dl_type_to_ffi_type(int dl_type);
+
+#endif
Index: test/dl/test_dl2.rb
===================================================================
--- test/dl/test_dl2.rb	(revision 26544)
+++ test/dl/test_dl2.rb	(revision 26545)
@@ -88,14 +88,16 @@
     assert_in_delta(-0.1, x)
   end
 
-  def test_sin()
+  def test_sin
     pi_2 = Math::PI/2
-    cfunc = CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin')
-    x = cfunc.call([pi_2].pack("d").unpack("l!*"))
+    cfunc = Function.new(CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin'),
+                        [TYPE_DOUBLE])
+    x = cfunc.call(pi_2)
     assert_equal(Math.sin(pi_2), x)
 
-    cfunc = CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin')
-    x = cfunc.call([-pi_2].pack("d").unpack("l!*"))
+    cfunc = Function.new(CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin'),
+                        [TYPE_DOUBLE])
+    x = cfunc.call(-pi_2)
     assert_equal(Math.sin(-pi_2), x)
   end
 
Index: test/dl/test_base.rb
===================================================================
--- test/dl/test_base.rb	(revision 26544)
+++ test/dl/test_base.rb	(revision 26545)
@@ -74,7 +74,7 @@
     end
 
     def assert_zero(actual)
-      assert(actual == 0)
+      assert_equal(0, actual)
     end
 
     def assert_negative(actual)
Index: test/dl/test_func.rb
===================================================================
--- test/dl/test_func.rb	(revision 26544)
+++ test/dl/test_func.rb	(revision 26545)
@@ -15,6 +15,24 @@
       assert_equal cfunc.to_i, f.to_i
     end
 
+    def test_random
+      f = Function.new(CFunc.new(@libc['srand'], TYPE_VOID, 'srand'),
+                       [-TYPE_LONG])
+      assert_nil f.call(10)
+    end
+
+    def test_sinf
+      f = Function.new(CFunc.new(@libm['sinf'], TYPE_FLOAT, 'sinf'),
+                       [TYPE_FLOAT])
+      assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
+    end
+
+    def test_sin
+      f = Function.new(CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin'),
+                       [TYPE_DOUBLE])
+      assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
+    end
+
     def test_strcpy()
       f = Function.new(CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy'),
                        [TYPE_VOIDP, TYPE_VOIDP])
Index: test/dl/test_closure.rb
===================================================================
--- test/dl/test_closure.rb	(revision 0)
+++ test/dl/test_closure.rb	(revision 26545)
@@ -0,0 +1,130 @@
+require_relative 'test_base'
+require 'dl/func'
+require 'dl/closure'
+
+module DL
+  class TestClosure < Test::Unit::TestCase
+    class Returner < DL::Closure
+      attr_accessor :called
+      attr_accessor :called_with
+      def call *args
+        @called = true
+        @called_with = args
+        a = args.first
+        DL::CPtr === a ? a.to_i : a
+      end
+    end
+
+    if defined?(TYPE_LONG_LONG)
+      def test_long_long
+        type = TYPE_LONG_LONG
+        addr = Returner.new(type, [type]) do |num|
+          called = true
+          called_with = num
+        end
+        func = DL::Function.new(addr, [type])
+        assert_equal(9223372036854775807, func.call(9223372036854775807))
+      end
+    end
+
+    def test_with_abi
+      called = false
+      addr = DL::Closure::BlockCaller.new(
+          TYPE_INT,
+          [TYPE_INT],
+          DL::Function::DEFAULT
+      ) do |num|
+        called = true
+	num
+      end
+      func = DL::Function.new(addr, [TYPE_INT])
+      func.call(50)
+      assert called
+    end
+
+    def test_block_caller
+      called = false
+      called_with = nil
+      addr = DL::Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |num|
+        called = true
+        called_with = num
+      end
+      func = DL::Function.new(addr, [TYPE_INT])
+      func.call(50)
+      assert called, 'function was called'
+      assert_equal 50, called_with
+    end
+
+    def test_multival
+      adder = Class.new(DL::Closure) {
+        def call a, b
+          a + b
+        end
+      }.new(TYPE_INT, [TYPE_INT, TYPE_INT])
+
+      assert_equal [TYPE_INT, TYPE_INT], adder.args
+      func = DL::Function.new(adder, adder.args)
+      assert_equal 70, func.call(50, 20)
+    end
+
+    def test_call
+      closure = Class.new(DL::Closure) {
+        attr_accessor :called_with
+        def call num
+          @called_with = num
+        end
+      }.new(TYPE_INT, [TYPE_INT])
+
+      func = DL::Function.new(closure, [TYPE_INT])
+      func.call(50)
+
+      assert_equal 50, closure.called_with
+    end
+
+    def test_return_value
+      closure = Returner.new(TYPE_INT, [TYPE_INT])
+
+      func = DL::Function.new(closure, [TYPE_INT])
+      assert_equal 50, func.call(50)
+    end
+
+    def test_float
+      closure = Returner.new(TYPE_FLOAT, [TYPE_FLOAT])
+      func = DL::Function.new(closure, [TYPE_FLOAT])
+      assert_equal 2.0, func.call(2.0)
+    end
+
+    def test_char
+      closure = Returner.new(TYPE_CHAR, [TYPE_CHAR])
+      func = DL::Function.new(closure, [TYPE_CHAR])
+      assert_equal 60, func.call(60)
+    end
+
+    def test_long
+      closure = Returner.new(TYPE_LONG, [TYPE_LONG])
+      func = DL::Function.new(closure, [TYPE_LONG])
+      assert_equal 60, func.call(60)
+    end
+
+    def test_double
+      closure = Returner.new(TYPE_DOUBLE, [TYPE_DOUBLE])
+      func = DL::Function.new(closure, [TYPE_DOUBLE])
+      assert_equal 60, func.call(60)
+    end
+
+    def test_voidp
+      closure = Returner.new(TYPE_VOIDP, [TYPE_VOIDP])
+      func = DL::Function.new(closure, [TYPE_VOIDP])
+
+      voidp = CPtr['foo']
+      assert_equal voidp, func.call(voidp)
+    end
+
+    def test_void
+      closure = Returner.new(TYPE_VOID, [TYPE_VOID])
+      func = DL::Function.new(closure, [TYPE_VOID])
+      func.call()
+      assert closure.called
+    end
+  end
+end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]