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>
|
||||
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
11
ext/dl/dl.h
11
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
|
||||
|
|
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)"],
|
||||
]
|
||||
|
||||
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")
|
||||
|
|
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/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)
|
||||
|
|
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/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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -74,7 +74,7 @@ module DL
|
|||
end
|
||||
|
||||
def assert_zero(actual)
|
||||
assert(actual == 0)
|
||||
assert_equal(0, actual)
|
||||
end
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
|
|
|
@ -15,6 +15,24 @@ module DL
|
|||
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])
|
||||
|
|
Loading…
Add table
Reference in a new issue