summaryrefslogtreecommitdiff
path: root/ext/dl
diff options
context:
space:
mode:
authortenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-02-03 01:23:48 +0000
committertenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-02-03 01:23:48 +0000
commitb386fe21eca01e03a5ca447792354632e549c94e (patch)
tree30b480ee3ab580e28053d1ec3f31a5fd5285aa9c /ext/dl
parentb378bda47c3960acd983890efd01ac08794f6fd3 (diff)
Wed Feb 3 10:12:09 2010 Aaron Patterson <tenderlove@ruby-lang.org>
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26545 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/dl')
-rw-r--r--ext/dl/callback/depend15
-rw-r--r--ext/dl/callback/extconf.rb14
-rw-r--r--ext/dl/callback/mkcallback.rb238
-rw-r--r--ext/dl/cfunc.c4
-rw-r--r--ext/dl/closure.c228
-rw-r--r--ext/dl/dl.c17
-rw-r--r--ext/dl/dl.h11
-rw-r--r--ext/dl/dl_conversions.c38
-rw-r--r--ext/dl/dl_conversions.h10
-rw-r--r--ext/dl/extconf.rb22
-rw-r--r--ext/dl/function.c233
-rw-r--r--ext/dl/lib/dl/callback.rb51
-rw-r--r--ext/dl/lib/dl/closure.rb19
-rw-r--r--ext/dl/lib/dl/func.rb62
-rw-r--r--ext/dl/lib/dl/import.rb9
-rw-r--r--ext/dl/lib/dl/value.rb20
16 files changed, 619 insertions, 372 deletions
diff --git a/ext/dl/callback/depend b/ext/dl/callback/depend
deleted file mode 100644
index 7a1dc1ee62..0000000000
--- a/ext/dl/callback/depend
+++ /dev/null
@@ -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
diff --git a/ext/dl/callback/extconf.rb b/ext/dl/callback/extconf.rb
deleted file mode 100644
index 6c3387670d..0000000000
--- a/ext/dl/callback/extconf.rb
+++ /dev/null
@@ -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
diff --git a/ext/dl/callback/mkcallback.rb b/ext/dl/callback/mkcallback.rb
deleted file mode 100644
index d2f9e3f2e1..0000000000
--- a/ext/dl/callback/mkcallback.rb
+++ /dev/null
@@ -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
diff --git a/ext/dl/cfunc.c b/ext/dl/cfunc.c
index 729dbe9ac6..4d10c82ff2 100644
--- a/ext/dl/cfunc.c
+++ b/ext/dl/cfunc.c
@@ -16,7 +16,7 @@ rb_dl_get_last_error(VALUE self)
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 @@ rb_dl_get_win32_last_error(VALUE self)
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);
diff --git a/ext/dl/closure.c b/ext/dl/closure.c
new file mode 100644
index 0000000000..a32b27ebc2
--- /dev/null
+++ b/ext/dl/closure.c
@@ -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 */
diff --git a/ext/dl/dl.c b/ext/dl/dl.c
index 9635794883..8f8212015b 100644
--- a/ext/dl/dl.c
+++ b/ext/dl/dl.c
@@ -77,19 +77,6 @@ rb_dl_value2ptr(VALUE self, VALUE val)
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 @@ Init_dl(void)
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_dl(void)
Init_dlhandle();
Init_dlcfunc();
Init_dlptr();
+ Init_dlfunction();
+ Init_dlclosure();
}
diff --git a/ext/dl/dl.h b/ext/dl/dl.h
index d06cad4e6b..da5695d3fa 100644
--- a/ext/dl/dl.h
+++ b/ext/dl/dl.h
@@ -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_new(void *ptr, long size, freefunc_t func);
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
diff --git a/ext/dl/dl_conversions.c b/ext/dl/dl_conversions.c
new file mode 100644
index 0000000000..01d6218f7d
--- /dev/null
+++ b/ext/dl/dl_conversions.c
@@ -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;
+}
diff --git a/ext/dl/dl_conversions.h b/ext/dl/dl_conversions.h
new file mode 100644
index 0000000000..13c904733d
--- /dev/null
+++ b/ext/dl/dl_conversions.h
@@ -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
diff --git a/ext/dl/extconf.rb b/ext/dl/extconf.rb
index 8317ac35ad..e2b7af6410 100644
--- a/ext/dl/extconf.rb
+++ b/ext/dl/extconf.rb
@@ -8,8 +8,30 @@ $INSTALLFILES = [
["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")
diff --git a/ext/dl/function.c b/ext/dl/function.c
new file mode 100644
index 0000000000..2fa5594e8e
--- /dev/null
+++ b/ext/dl/function.c
@@ -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 */
diff --git a/ext/dl/lib/dl/callback.rb b/ext/dl/lib/dl/callback.rb
index c8daaf6322..53da888d9d 100644
--- a/ext/dl/lib/dl/callback.rb
+++ b/ext/dl/lib/dl/callback.rb
@@ -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 @@ module DL
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)
diff --git a/ext/dl/lib/dl/closure.rb b/ext/dl/lib/dl/closure.rb
new file mode 100644
index 0000000000..eca941dfbc
--- /dev/null
+++ b/ext/dl/lib/dl/closure.rb
@@ -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
diff --git a/ext/dl/lib/dl/func.rb b/ext/dl/lib/dl/func.rb
index 7a8b62e325..0ee2df1f23 100644
--- a/ext/dl/lib/dl/func.rb
+++ b/ext/dl/lib/dl/func.rb
@@ -1,4 +1,5 @@
require 'dl'
+require 'dl/closure'
require 'dl/callback'
require 'dl/stack'
require 'dl/value'
@@ -9,18 +10,17 @@ module DL
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
- end
- if( proc )
- bind(&proc)
+ @cfunc = cfunc
end
+
+ @args = argtypes
+ native_init(@args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi)
end
def to_i()
@@ -32,11 +32,10 @@ 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 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 @@ 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}")
+ @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()
diff --git a/ext/dl/lib/dl/import.rb b/ext/dl/lib/dl/import.rb
index 199354c18e..4c101d0f5c 100644
--- a/ext/dl/lib/dl/import.rb
+++ b/ext/dl/lib/dl/import.rb
@@ -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 @@ 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
+ 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)
diff --git a/ext/dl/lib/dl/value.rb b/ext/dl/lib/dl/value.rb
index 56dfcefa32..c613afe5cf 100644
--- a/ext/dl/lib/dl/value.rb
+++ b/ext/dl/lib/dl/value.rb
@@ -36,16 +36,20 @@ module DL
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