mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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
This commit is contained in:
parent
b378bda47c
commit
b386fe21ec
21 changed files with 802 additions and 378 deletions
27
ChangeLog
27
ChangeLog
|
@ -1,3 +1,30 @@
|
||||||
|
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.
|
||||||
|
|
||||||
Tue Feb 2 18:15:12 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Tue Feb 2 18:15:12 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* ext/socket/socket.c: turn on do_not_reverse_lookup by default,
|
* ext/socket/socket.c: turn on do_not_reverse_lookup by default,
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -16,7 +16,7 @@ rb_dl_get_last_error(VALUE self)
|
||||||
return rb_thread_local_aref(rb_thread_current(), id_last_error);
|
return rb_thread_local_aref(rb_thread_current(), id_last_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
VALUE
|
||||||
rb_dl_set_last_error(VALUE self, VALUE val)
|
rb_dl_set_last_error(VALUE self, VALUE val)
|
||||||
{
|
{
|
||||||
rb_thread_local_aset(rb_thread_current(), id_last_error, 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);
|
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_dl_set_win32_last_error(VALUE self, VALUE val)
|
||||||
{
|
{
|
||||||
rb_thread_local_aset(rb_thread_current(), id_win32_last_error, val);
|
rb_thread_local_aset(rb_thread_current(), id_win32_last_error, val);
|
||||||
|
|
228
ext/dl/closure.c
Normal file
228
ext/dl/closure.c
Normal file
|
@ -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 */
|
17
ext/dl/dl.c
17
ext/dl/dl.c
|
@ -77,19 +77,6 @@ rb_dl_value2ptr(VALUE self, VALUE val)
|
||||||
return PTR2NUM((void*)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
|
void
|
||||||
Init_dl(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, "MAX_CALLBACK", INT2NUM(MAX_CALLBACK));
|
||||||
rb_define_const(rb_mDL, "DLSTACK_SIZE", INT2NUM(DLSTACK_SIZE));
|
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_GLOBAL", INT2NUM(RTLD_GLOBAL));
|
||||||
rb_define_const(rb_mDL, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
|
rb_define_const(rb_mDL, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
|
||||||
rb_define_const(rb_mDL, "RTLD_NOW", INT2NUM(RTLD_NOW));
|
rb_define_const(rb_mDL, "RTLD_NOW", INT2NUM(RTLD_NOW));
|
||||||
|
@ -162,4 +147,6 @@ Init_dl(void)
|
||||||
Init_dlhandle();
|
Init_dlhandle();
|
||||||
Init_dlcfunc();
|
Init_dlcfunc();
|
||||||
Init_dlptr();
|
Init_dlptr();
|
||||||
|
Init_dlfunction();
|
||||||
|
Init_dlclosure();
|
||||||
}
|
}
|
||||||
|
|
11
ext/dl/dl.h
11
ext/dl/dl.h
|
@ -3,6 +3,12 @@
|
||||||
|
|
||||||
#include <ruby.h>
|
#include <ruby.h>
|
||||||
|
|
||||||
|
#ifdef USE_HEADER_HACKS
|
||||||
|
#include <ffi/ffi.h>
|
||||||
|
#else
|
||||||
|
#include <ffi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(FUNC_CDECL)
|
#if !defined(FUNC_CDECL)
|
||||||
# define FUNC_CDECL(x) x
|
# define FUNC_CDECL(x) x
|
||||||
#endif
|
#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_new2(VALUE klass, void *ptr, long size, freefunc_t func);
|
||||||
VALUE rb_dlptr_malloc(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
|
#endif
|
||||||
|
|
38
ext/dl/dl_conversions.c
Normal file
38
ext/dl/dl_conversions.c
Normal file
|
@ -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;
|
||||||
|
}
|
10
ext/dl/dl_conversions.h
Normal file
10
ext/dl/dl_conversions.h
Normal file
|
@ -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
|
|
@ -8,8 +8,30 @@ $INSTALLFILES = [
|
||||||
["dl.h", "$(HDRDIR)"],
|
["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
|
check = true
|
||||||
if( have_header("dlfcn.h") )
|
if( have_header("dlfcn.h") )
|
||||||
|
|
||||||
have_library("dl")
|
have_library("dl")
|
||||||
check &&= have_func("dlopen")
|
check &&= have_func("dlopen")
|
||||||
check &&= have_func("dlclose")
|
check &&= have_func("dlclose")
|
||||||
|
|
233
ext/dl/function.c
Normal file
233
ext/dl/function.c
Normal file
|
@ -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 */
|
|
@ -1,26 +1,21 @@
|
||||||
require 'dl'
|
require 'dl'
|
||||||
|
require 'dl/closure'
|
||||||
require 'thread'
|
require 'thread'
|
||||||
|
|
||||||
module DL
|
module DL
|
||||||
SEM = Mutex.new
|
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 )
|
if( argc < 0 )
|
||||||
raise(ArgumentError, "arity should not be less than 0.")
|
raise(ArgumentError, "arity should not be less than 0.")
|
||||||
end
|
end
|
||||||
addr = nil
|
|
||||||
SEM.synchronize{
|
closure = DL::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp)
|
||||||
ary = proc_entry[ty]
|
proc_entry[closure.to_i] = closure
|
||||||
(0...MAX_CALLBACK).each{|n|
|
closure.to_i
|
||||||
idx = (n * DLSTACK_SIZE) + argc
|
|
||||||
if( ary[idx].nil? )
|
|
||||||
ary[idx] = cbp
|
|
||||||
addr = addr_entry[ty][idx]
|
|
||||||
break
|
|
||||||
end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addr
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_cdecl_callback(ty, argc, &cbp)
|
def set_cdecl_callback(ty, argc, &cbp)
|
||||||
|
@ -28,32 +23,14 @@ module DL
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_stdcall_callback(ty, argc, &cbp)
|
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
|
end
|
||||||
|
|
||||||
def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
|
def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
|
||||||
index = nil
|
addr = addr.to_i
|
||||||
if( ctype )
|
return false unless proc_entry.key?(addr)
|
||||||
addr_entry[ctype].each_with_index{|xaddr, idx|
|
proc_entry.delete(addr)
|
||||||
if( xaddr == addr )
|
true
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_cdecl_callback(addr, ctype = nil)
|
def remove_cdecl_callback(addr, ctype = nil)
|
||||||
|
|
19
ext/dl/lib/dl/closure.rb
Normal file
19
ext/dl/lib/dl/closure.rb
Normal file
|
@ -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
|
|
@ -1,4 +1,5 @@
|
||||||
require 'dl'
|
require 'dl'
|
||||||
|
require 'dl/closure'
|
||||||
require 'dl/callback'
|
require 'dl/callback'
|
||||||
require 'dl/stack'
|
require 'dl/stack'
|
||||||
require 'dl/value'
|
require 'dl/value'
|
||||||
|
@ -9,18 +10,17 @@ module DL
|
||||||
include DL
|
include DL
|
||||||
include ValueUtil
|
include ValueUtil
|
||||||
|
|
||||||
def initialize(cfunc, argtypes, &proc)
|
def initialize cfunc, argtypes, abi = DEFAULT, &block
|
||||||
@cfunc = cfunc
|
if block_given?
|
||||||
@stack = Stack.new(argtypes.collect{|ty| ty.abs})
|
@cfunc = Class.new(DL::Closure) {
|
||||||
if( @cfunc.ctype < 0 )
|
define_method(:call, block)
|
||||||
@cfunc.ctype = @cfunc.ctype.abs
|
}.new(cfunc.ctype, argtypes)
|
||||||
@unsigned = true
|
|
||||||
else
|
else
|
||||||
@unsigned = false
|
@cfunc = cfunc
|
||||||
end
|
|
||||||
if( proc )
|
|
||||||
bind(&proc)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@args = argtypes
|
||||||
|
native_init(@args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_i()
|
def to_i()
|
||||||
|
@ -32,11 +32,10 @@ module DL
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(*args, &block)
|
def call(*args, &block)
|
||||||
funcs = []
|
if block_given?
|
||||||
args = wrap_args(args, @stack.types, funcs, &block)
|
args.find { |a| DL::Function === a }.bind_at_call(&block)
|
||||||
r = @cfunc.call(@stack.pack(args))
|
end
|
||||||
funcs.each{|f| f.unbind_at_call()}
|
native_call(*args)
|
||||||
return wrap_result(r)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def wrap_result(r)
|
def wrap_result(r)
|
||||||
|
@ -52,33 +51,16 @@ module DL
|
||||||
end
|
end
|
||||||
|
|
||||||
def bind(&block)
|
def bind(&block)
|
||||||
if( !block )
|
@cfunc = Class.new(DL::Closure) {
|
||||||
raise(RuntimeError, "block must be given.")
|
def initialize ctype, args, block
|
||||||
end
|
super(ctype, args)
|
||||||
if( @cfunc.ptr == 0 )
|
@block = block
|
||||||
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
|
||||||
|
|
||||||
|
def call *args
|
||||||
|
@block.call(*args)
|
||||||
end
|
end
|
||||||
|
}.new(@cfunc.ctype, @args, block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unbind()
|
def unbind()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require 'dl'
|
require 'dl'
|
||||||
|
require 'dl/closure'
|
||||||
require 'dl/func.rb'
|
require 'dl/func.rb'
|
||||||
require 'dl/struct.rb'
|
require 'dl/struct.rb'
|
||||||
require 'dl/cparser.rb'
|
require 'dl/cparser.rb'
|
||||||
|
@ -211,9 +212,11 @@ module DL
|
||||||
end
|
end
|
||||||
|
|
||||||
def bind_function(name, ctype, argtype, call_type = nil, &block)
|
def bind_function(name, ctype, argtype, call_type = nil, &block)
|
||||||
f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
|
closure = Class.new(DL::Closure) {
|
||||||
f.bind(&block)
|
define_method(:call, block)
|
||||||
f
|
}.new(ctype, argtype)
|
||||||
|
|
||||||
|
Function.new(closure, argtype)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_temp_function(name, ctype, argtype, call_type = nil)
|
def create_temp_function(name, ctype, argtype, call_type = nil)
|
||||||
|
|
|
@ -36,16 +36,20 @@ module DL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def wrap_args(args, tys, funcs, &block)
|
def ruby2ffi arg, type
|
||||||
result = []
|
return arg unless type == TYPE_VOIDP
|
||||||
tys ||= []
|
case arg
|
||||||
args.each_with_index{|arg, idx|
|
when nil
|
||||||
result.push(wrap_arg(arg, tys[idx], funcs, &block))
|
0
|
||||||
}
|
when CPtr
|
||||||
result
|
arg.to_i
|
||||||
|
else
|
||||||
|
CPtr[arg].to_i
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
private :ruby2ffi
|
||||||
|
|
||||||
def wrap_arg(arg, ty, funcs, &block)
|
def wrap_arg(arg, ty, funcs = [], &block)
|
||||||
funcs ||= []
|
funcs ||= []
|
||||||
case arg
|
case arg
|
||||||
when nil
|
when nil
|
||||||
|
|
|
@ -74,7 +74,7 @@ module DL
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_zero(actual)
|
def assert_zero(actual)
|
||||||
assert(actual == 0)
|
assert_equal(0, actual)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_negative(actual)
|
def assert_negative(actual)
|
||||||
|
|
130
test/dl/test_closure.rb
Normal file
130
test/dl/test_closure.rb
Normal file
|
@ -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
|
|
@ -88,14 +88,16 @@ class TestDL < TestBase
|
||||||
assert_in_delta(-0.1, x)
|
assert_in_delta(-0.1, x)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sin()
|
def test_sin
|
||||||
pi_2 = Math::PI/2
|
pi_2 = Math::PI/2
|
||||||
cfunc = CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin')
|
cfunc = Function.new(CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin'),
|
||||||
x = cfunc.call([pi_2].pack("d").unpack("l!*"))
|
[TYPE_DOUBLE])
|
||||||
|
x = cfunc.call(pi_2)
|
||||||
assert_equal(Math.sin(pi_2), x)
|
assert_equal(Math.sin(pi_2), x)
|
||||||
|
|
||||||
cfunc = CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin')
|
cfunc = Function.new(CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin'),
|
||||||
x = cfunc.call([-pi_2].pack("d").unpack("l!*"))
|
[TYPE_DOUBLE])
|
||||||
|
x = cfunc.call(-pi_2)
|
||||||
assert_equal(Math.sin(-pi_2), x)
|
assert_equal(Math.sin(-pi_2), x)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,24 @@ module DL
|
||||||
assert_equal cfunc.to_i, f.to_i
|
assert_equal cfunc.to_i, f.to_i
|
||||||
end
|
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()
|
def test_strcpy()
|
||||||
f = Function.new(CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy'),
|
f = Function.new(CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy'),
|
||||||
[TYPE_VOIDP, TYPE_VOIDP])
|
[TYPE_VOIDP, TYPE_VOIDP])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue