1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* ext/dl: revert dl with libffi because it can't run on mswin now.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26764 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
naruse 2010-02-25 22:49:20 +00:00
parent 9d818a221e
commit 59e4e93ef7
19 changed files with 394 additions and 662 deletions

View file

@ -1,3 +1,7 @@
Fri Feb 26 07:44:51 2010 NARUSE, Yui <naruse@ruby-lang.org>
* ext/dl: revert dl with libffi because it can't run on mswin now.
Thu Feb 25 22:59:46 2010 Tanaka Akira <akr@fsij.org>
* pack.c: consider DYNAMIC_ENDIAN. refactored.

15
ext/dl/callback/depend Normal file
View file

@ -0,0 +1,15 @@
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

View file

@ -0,0 +1,14 @@
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

View file

@ -0,0 +1,238 @@
#!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

View file

@ -16,7 +16,7 @@ rb_dl_get_last_error(VALUE self)
return rb_thread_local_aref(rb_thread_current(), id_last_error);
}
VALUE
static 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);
}
VALUE
static VALUE
rb_dl_set_win32_last_error(VALUE self, VALUE val)
{
rb_thread_local_aset(rb_thread_current(), id_win32_last_error, val);
@ -147,12 +147,12 @@ rb_dlcfunc_initialize(int argc, VALUE argv[], VALUE self)
struct cfunc_data *data;
void *saddr;
const char *sname;
rb_scan_args(argc, argv, "13", &addr, &type, &name, &calltype);
saddr = (void*)(NUM2PTR(rb_Integer(addr)));
sname = NIL_P(name) ? NULL : StringValuePtr(name);
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, data);
if( data->name ) xfree(data->name);
data->ptr = saddr;
@ -284,11 +284,11 @@ rb_dlcfunc_inspect(VALUE self)
{
VALUE val;
char *str;
size_t str_size;
int str_size;
struct cfunc_data *cfunc;
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
str_size = (cfunc->name ? strlen(cfunc->name) : 0) + 100;
str = ruby_xmalloc(str_size);
snprintf(str, str_size - 1,
@ -339,31 +339,22 @@ rb_dlcfunc_call(VALUE self, VALUE ary)
memset(stack, 0, sizeof(DLSTACK_TYPE) * DLSTACK_SIZE);
Check_Type(ary, T_ARRAY);
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
if( cfunc->ptr == 0 ){
rb_raise(rb_eDLError, "can't call null-function");
return Qnil;
}
if( RARRAY_LEN(ary) >= DLSTACK_SIZE ){
rb_raise(rb_eDLError, "too many arguments (stack overflow)");
}
for( i = 0; i < RARRAY_LEN(ary); i++ ){
unsigned long rb_big2ulong_pack(VALUE x);
VALUE arg = RARRAY_PTR(ary)[i];
rb_check_safe_obj(arg);
if (FIXNUM_P(arg)) {
stack[i] = FIX2LONG(arg);
}
else {
Check_Type(arg, T_BIGNUM);
stack[i] = rb_big2ulong_pack(arg);
if( i >= DLSTACK_SIZE ){
rb_raise(rb_eDLError, "too many arguments (stack overflow)");
}
rb_check_safe_obj(RARRAY_PTR(ary)[i]);
stack[i] = NUM2LONG(RARRAY_PTR(ary)[i]);
}
/* calltype == CFUNC_CDECL */
if( cfunc->calltype == CFUNC_CDECL
#ifndef FUNC_STDCALL

View file

@ -1,230 +0,0 @@
/* -*- 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_LENINT(rbargs);
VALUE *params = xcalloc(argc, sizeof(VALUE *));
VALUE ret;
int i, dl_type;
for (i = 0; i < argc; i++) {
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);
}
}
ret = rb_funcall2(self, rb_intern("call"), argc, params);
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 = (float)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;
ffi_status result;
int i, argc;
if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
abi = INT2NUM(FFI_DEFAULT_ABI);
argc = RARRAY_LENINT(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;
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;
void *code;
TypedData_Get_Struct(self, dl_closure, &dlclosure_data_type, cl);
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 */

View file

@ -104,7 +104,7 @@ rb_dlptr2cptr(VALUE val)
else{
rb_raise(rb_eTypeError, "DL::PtrData was expected");
}
return ptr;
}
@ -186,7 +186,7 @@ static VALUE
rb_dlptr_s_malloc(int argc, VALUE argv[], VALUE klass)
{
VALUE size, sym, obj;
long s;
int s;
freefunc_t f;
switch (rb_scan_args(argc, argv, "11", &size, &sym)) {

View file

@ -1,13 +1,7 @@
cfunc.o: cfunc.c dl.h $(hdrdir)/ruby.h
closure.o: closure.c dl.h dl_conversions.h $(hdrdir)/ruby.h
cptr.o: cptr.c dl.h $(hdrdir)/ruby.h $(hdrdir)/io.h
handle.o: handle.c dl.h $(hdrdir)/ruby.h
method.o: method.c dl.h dl_conversions.h $(hdrdir)/ruby.h
dl.o: dl.c dl.h $(hdrdir)/ruby.h $(hdrdir)/io.h
dl_conversions.o: dl_conversions.c dl_conversions.h

View file

@ -77,6 +77,19 @@ 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)
{
@ -94,6 +107,8 @@ 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));
@ -147,6 +162,4 @@ Init_dl(void)
Init_dlhandle();
Init_dlcfunc();
Init_dlptr();
Init_dlfunction();
Init_dlclosure();
}

View file

@ -3,12 +3,6 @@
#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
@ -136,7 +130,6 @@
extern VALUE rb_mDL;
extern VALUE rb_cDLHandle;
extern VALUE rb_cDLCPtr;
extern VALUE rb_cDLSymbol;
extern VALUE rb_eDLError;
extern VALUE rb_eDLTypeError;
@ -228,9 +221,4 @@ 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

View file

@ -1,40 +0,0 @@
#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;
}
#define rb_ffi_type_of(t) (signed_p ? &ffi_type_s##t : &ffi_type_u##t)
switch (dl_type) {
case DLTYPE_VOID:
return &ffi_type_void;
case DLTYPE_VOIDP:
return &ffi_type_pointer;
case DLTYPE_CHAR:
return rb_ffi_type_of(char);
case DLTYPE_SHORT:
return rb_ffi_type_of(short);
case DLTYPE_INT:
return rb_ffi_type_of(int);
case DLTYPE_LONG:
return rb_ffi_type_of(long);
#if HAVE_LONG_LONG
case DLTYPE_LONG_LONG:
return rb_ffi_type_of(int64);
#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;
}

View file

@ -1,10 +0,0 @@
#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

View file

@ -8,30 +8,8 @@ $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")

View file

@ -1,21 +1,26 @@
require 'dl'
require 'dl/closure'
require 'thread'
module DL
SEM = Mutex.new
CdeclCallbackProcs = {}
CdeclCallbackAddrs = {}
def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = DL::Function::DEFAULT, &cbp)
def set_callback_internal(proc_entry, addr_entry, argc, ty, &cbp)
if( argc < 0 )
raise(ArgumentError, "arity should not be less than 0.")
end
closure = DL::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp)
proc_entry[closure.to_i] = closure
closure.to_i
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
end
def set_cdecl_callback(ty, argc, &cbp)
@ -23,14 +28,32 @@ module DL
end
def set_stdcall_callback(ty, argc, &cbp)
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, DL::Function::STDCALL, &cbp)
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
end
def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
addr = addr.to_i
return false unless proc_entry.key?(addr)
proc_entry.delete(addr)
true
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
end
def remove_cdecl_callback(addr, ctype = nil)

View file

@ -1,19 +0,0 @@
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

View file

@ -1,26 +1,26 @@
require 'dl'
require 'dl/closure'
require 'dl/callback'
require 'dl/stack'
require 'dl/value'
require 'thread'
module DL
class Function < DL::Method
class Function
include DL
include ValueUtil
def initialize cfunc, argtypes, abi = DEFAULT, &block
if block_given?
@cfunc = Class.new(DL::Closure) {
define_method(:call, block)
}.new(cfunc.ctype, argtypes)
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
else
@cfunc = cfunc
@unsigned = false
end
if( proc )
bind(&proc)
end
@args = argtypes
super(@cfunc, @args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi)
end
def to_i()
@ -32,10 +32,11 @@ module DL
end
def call(*args, &block)
if block_given?
args.find { |a| DL::Function === a }.bind_at_call(&block)
end
super
funcs = []
args = wrap_args(args, @stack.types, funcs, &block)
r = @cfunc.call(@stack.pack(args))
funcs.each{|f| f.unbind_at_call()}
return wrap_result(r)
end
def wrap_result(r)
@ -51,16 +52,33 @@ module DL
end
def bind(&block)
@cfunc = Class.new(DL::Closure) {
def initialize ctype, args, block
super(ctype, args)
@block = 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}")
end
def call *args
@block.call(*args)
if( @cfunc.ptr == 0 )
raise(RuntimeException, "can't bind C function.")
end
}.new(@cfunc.ctype, @args, block)
end
end
def unbind()

View file

@ -1,5 +1,4 @@
require 'dl'
require 'dl/closure'
require 'dl/func.rb'
require 'dl/struct.rb'
require 'dl/cparser.rb'
@ -212,11 +211,9 @@ module DL
end
def bind_function(name, ctype, argtype, call_type = nil, &block)
closure = Class.new(DL::Closure) {
define_method(:call, block)
}.new(ctype, argtype)
Function.new(closure, argtype)
f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
f.bind(&block)
f
end
def create_temp_function(name, ctype, argtype, call_type = nil)

View file

@ -36,7 +36,16 @@ module DL
end
end
def wrap_arg(arg, ty, funcs = [], &block)
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
end
def wrap_arg(arg, ty, funcs, &block)
funcs ||= []
case arg
when nil

View file

@ -1,251 +0,0 @@
/* -*- C -*-
* $Id$
*/
#include <ruby.h>
#include <errno.h>
#include "dl.h"
#include <dl_conversions.h>
VALUE rb_cDLMethod;
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(void *p)
{
ffi_cif *ptr = p;
if (ptr->arg_types) xfree(ptr->arg_types);
xfree(ptr);
}
static size_t
dlfunction_memsize(const void *p)
{
/* const */ffi_cif *ptr = (ffi_cif *)p;
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/method",
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_initialize(int argc, VALUE argv[], VALUE self)
{
ffi_cif * cif;
ffi_type **arg_types;
ffi_status result;
VALUE ptr, args, ret_type, abi;
int i;
rb_scan_args(argc, argv, "31", &ptr, &args, &ret_type, &abi);
if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
rb_iv_set(self, "@ptr", ptr);
rb_iv_set(self, "@args", args);
rb_iv_set(self, "@return_type", ret_type);
rb_iv_set(self, "@abi", abi);
TypedData_Get_Struct(self, ffi_cif, &dlfunction_data_type, cif);
arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
for (i = 0; i < RARRAY_LEN(args); i++) {
int type = NUM2INT(RARRAY_PTR(args)[i]);
arg_types[i] = DL2FFI_TYPE(type);
}
arg_types[RARRAY_LEN(args)] = NULL;
result = ffi_prep_cif (
cif,
NUM2INT(abi),
RARRAY_LENINT(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 = (float)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 ULONG2NUM(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;
VALUE cfunc, types;
int i;
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));
cfunc = rb_iv_get(self, "@ptr");
types = rb_iv_get(self, "@args");
for (i = 0; i < argc; i++) {
VALUE dl_type = RARRAY_PTR(types)[i];
VALUE src = argv[i];
if(NUM2INT(dl_type) == DLTYPE_VOIDP) {
if(NIL_P(src)) {
src = INT2NUM(0);
} else if(rb_cDLCPtr != CLASS_OF(src)) {
src = rb_funcall(rb_cDLCPtr, rb_intern("[]"), 1, src);
}
src = rb_Integer(src);
}
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_iv_get(self, "@return_type"), retval);
}
void
Init_dlfunction(void)
{
rb_cDLMethod = rb_define_class_under(rb_mDL, "Method", rb_cObject);
rb_define_const(rb_cDLMethod, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
#ifdef FFI_STDCALL
rb_define_const(rb_cDLMethod, "STDCALL", INT2NUM(FFI_STDCALL));
#endif
rb_define_alloc_func(rb_cDLMethod, rb_dlfunc_allocate);
rb_define_method(rb_cDLMethod, "call", rb_dlfunction_call, -1);
rb_define_method(rb_cDLMethod, "initialize", rb_dlfunction_initialize, -1);
}
/* vim: set noet sw=4 sts=4 */