mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/dl/*: remove DL as it is replaced by Fiddle.
[Feature #5458] Thanks to Jonan Scheffler <jonanscheffler@gmail.com> for this patch * test/dl/*: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48217 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
88326272bc
commit
07308c4d30
48 changed files with 33 additions and 5810 deletions
|
@ -1,3 +1,11 @@
|
|||
Fri Oct 31 13:55:28 2014 Aaron Patterson <aaron@tenderlovemaking.com>
|
||||
|
||||
* ext/dl/*: remove DL as it is replaced by Fiddle.
|
||||
[Feature #5458] Thanks to Jonan Scheffler <jonanscheffler@gmail.com>
|
||||
for this patch
|
||||
|
||||
* test/dl/*: ditto.
|
||||
|
||||
Fri Oct 31 15:26:02 2014 Charles Oliver Nutter <headius@headius.com>
|
||||
|
||||
* test/openssl/test_ssl.rb: Add certificate verification chain
|
||||
|
|
3
NEWS
3
NEWS
|
@ -135,6 +135,9 @@ with all sufficient information, see the ChangeLog file.
|
|||
directly referencing Digest::*.
|
||||
* Digest::HMAC has been removed just as previously noticed.
|
||||
|
||||
* DL
|
||||
* DL has been removed from stdlib. Please use Fiddle instead!
|
||||
|
||||
* Etc
|
||||
* New methods:
|
||||
* Etc.uname
|
||||
|
|
|
@ -96,7 +96,6 @@ Date:: A subclass of Object includes Comparable module for handling dates
|
|||
DateTime:: Subclass of Date to handling dates, hours, minutes, seconds, offsets
|
||||
DBM:: Provides a wrapper for the UNIX-style Database Manager Library
|
||||
Digest:: Provides a framework for message digest libraries
|
||||
DL:: Provides a wrapper for the UNIX dlopen() library
|
||||
Etc:: Provides access to information typically stored in UNIX /etc directory
|
||||
Fcntl:: Loads constants defined in the OS fcntl.h C header file
|
||||
Fiddle:: A libffi wrapper for Ruby
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#digest/rmd160
|
||||
#digest/sha1
|
||||
#digest/sha2
|
||||
#dl
|
||||
#etc
|
||||
#fcntl
|
||||
#gdbm
|
||||
|
|
|
@ -8,7 +8,6 @@ digest/md5
|
|||
digest/rmd160
|
||||
digest/sha1
|
||||
digest/sha2
|
||||
dl
|
||||
enumerator
|
||||
etc
|
||||
fcntl
|
||||
|
|
|
@ -10,7 +10,6 @@ digest/md5
|
|||
digest/rmd160
|
||||
digest/sha1
|
||||
digest/sha2
|
||||
#dl
|
||||
enumerator
|
||||
etc
|
||||
fcntl
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
# digest/rmd160
|
||||
# digest/sha1
|
||||
# digest/sha2
|
||||
# dl
|
||||
# dl/callback
|
||||
# #dl/win32
|
||||
# etc
|
||||
# fcntl
|
||||
# fiber
|
||||
|
|
|
@ -9,7 +9,6 @@ digest/md5
|
|||
digest/rmd160
|
||||
digest/sha1
|
||||
digest/sha2
|
||||
dl
|
||||
enumerator
|
||||
etc
|
||||
fcntl
|
||||
|
|
|
@ -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): $(srcdir)/../dl.h $(HDRS) $(ruby_headers)
|
||||
|
||||
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'
|
||||
|
||||
begin
|
||||
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,247 +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)
|
||||
dltype = DLTYPE[ty]
|
||||
ret = dltype[:conv]
|
||||
if argc == 0
|
||||
args = "void"
|
||||
else
|
||||
args = (0...argc).collect{|i| "DLSTACK_TYPE stack#{i}"}.join(", ")
|
||||
end
|
||||
src = <<-EOS
|
||||
#{calltype == STDCALL ? "\n#ifdef FUNC_STDCALL" : ""}
|
||||
static #{dltype[:type]}
|
||||
FUNC_#{calltype.upcase}(#{func_name(ty,argc,n,calltype)})(#{args})
|
||||
{
|
||||
VALUE #{ret ? "ret, " : ""}cb#{argc > 0 ? ", args[#{argc}]" : ""};
|
||||
#{
|
||||
(0...argc).collect{|i|
|
||||
"\n args[#{i}] = PTR2NUM(stack#{i});"
|
||||
}.join("")
|
||||
}
|
||||
cb = rb_ary_entry(rb_ary_entry(#{proc_entry}, #{ty}), #{(n * DLSTACK_SIZE) + argc});
|
||||
#{ret ? "ret = " : ""}rb_funcall2(cb, rb_dl_cb_call, #{argc}, #{argc > 0 ? 'args' : 'NULL'});
|
||||
EOS
|
||||
src << " return #{ret % "ret"};\n" if ret
|
||||
src << <<-EOS
|
||||
}
|
||||
#{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}(void)
|
||||
{
|
||||
#{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}(void);\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
|
677
ext/dl/cfunc.c
677
ext/dl/cfunc.c
|
@ -1,677 +0,0 @@
|
|||
/* -*- C -*-
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
#include <ruby/util.h>
|
||||
#include <errno.h>
|
||||
#include "dl.h"
|
||||
|
||||
VALUE rb_cDLCFunc;
|
||||
|
||||
static ID id_last_error;
|
||||
|
||||
static VALUE
|
||||
rb_dl_get_last_error(VALUE self)
|
||||
{
|
||||
return rb_thread_local_aref(rb_thread_current(), id_last_error);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rb_dl_set_last_error(VALUE self, VALUE val)
|
||||
{
|
||||
rb_thread_local_aset(rb_thread_current(), id_last_error, val);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
static ID id_win32_last_error;
|
||||
|
||||
static VALUE
|
||||
rb_dl_get_win32_last_error(VALUE self)
|
||||
{
|
||||
return rb_thread_local_aref(rb_thread_current(), id_win32_last_error);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rb_dl_set_win32_last_error(VALUE self, VALUE val)
|
||||
{
|
||||
rb_thread_local_aset(rb_thread_current(), id_win32_last_error, val);
|
||||
return Qnil;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
dlcfunc_mark(void *ptr)
|
||||
{
|
||||
struct cfunc_data *data = ptr;
|
||||
if (data->wrap) {
|
||||
rb_gc_mark(data->wrap);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dlcfunc_free(void *ptr)
|
||||
{
|
||||
struct cfunc_data *data = ptr;
|
||||
if( data->name ){
|
||||
xfree(data->name);
|
||||
}
|
||||
xfree(data);
|
||||
}
|
||||
|
||||
static size_t
|
||||
dlcfunc_memsize(const void *ptr)
|
||||
{
|
||||
const struct cfunc_data *data = ptr;
|
||||
size_t size = 0;
|
||||
if( data ){
|
||||
size += sizeof(*data);
|
||||
if( data->name ){
|
||||
size += strlen(data->name) + 1;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
const rb_data_type_t dlcfunc_data_type = {
|
||||
"dl/cfunc",
|
||||
{dlcfunc_mark, dlcfunc_free, dlcfunc_memsize,},
|
||||
};
|
||||
|
||||
VALUE
|
||||
rb_dlcfunc_new(void (*func)(), int type, const char *name, ID calltype)
|
||||
{
|
||||
VALUE val;
|
||||
struct cfunc_data *data;
|
||||
|
||||
if( func ){
|
||||
val = TypedData_Make_Struct(rb_cDLCFunc, struct cfunc_data, &dlcfunc_data_type, data);
|
||||
data->ptr = (void *)(VALUE)func;
|
||||
data->name = name ? strdup(name) : NULL;
|
||||
data->type = type;
|
||||
data->calltype = calltype;
|
||||
}
|
||||
else{
|
||||
val = Qnil;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void *
|
||||
rb_dlcfunc2ptr(VALUE val)
|
||||
{
|
||||
struct cfunc_data *data;
|
||||
void * func;
|
||||
|
||||
if( rb_typeddata_is_kind_of(val, &dlcfunc_data_type) ){
|
||||
data = DATA_PTR(val);
|
||||
func = data->ptr;
|
||||
}
|
||||
else if( val == Qnil ){
|
||||
func = NULL;
|
||||
}
|
||||
else{
|
||||
rb_raise(rb_eTypeError, "DL::CFunc was expected");
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rb_dlcfunc_s_allocate(VALUE klass)
|
||||
{
|
||||
VALUE obj;
|
||||
struct cfunc_data *data;
|
||||
|
||||
obj = TypedData_Make_Struct(klass, struct cfunc_data, &dlcfunc_data_type, data);
|
||||
data->ptr = 0;
|
||||
data->name = 0;
|
||||
data->type = 0;
|
||||
data->calltype = CFUNC_CDECL;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
int
|
||||
rb_dlcfunc_kind_p(VALUE func)
|
||||
{
|
||||
return rb_typeddata_is_kind_of(func, &dlcfunc_data_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* DL::CFunc.new(address, type=DL::TYPE_VOID, name=nil, calltype=:cdecl)
|
||||
*
|
||||
* Create a new function that points to +address+ with an optional return type
|
||||
* of +type+, a name of +name+ and a calltype of +calltype+.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_initialize(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
VALUE addr, name, type, calltype, addrnum;
|
||||
struct cfunc_data *data;
|
||||
void *saddr;
|
||||
const char *sname;
|
||||
|
||||
rb_scan_args(argc, argv, "13", &addr, &type, &name, &calltype);
|
||||
|
||||
addrnum = rb_Integer(addr);
|
||||
saddr = (void*)(NUM2PTR(addrnum));
|
||||
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;
|
||||
data->name = sname ? strdup(sname) : 0;
|
||||
data->type = NIL_P(type) ? DLTYPE_VOID : NUM2INT(type);
|
||||
data->calltype = NIL_P(calltype) ? CFUNC_CDECL : SYM2ID(calltype);
|
||||
data->wrap = (addrnum == addr) ? 0 : addr;
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* name => str
|
||||
*
|
||||
* Get the name of this function
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_name(VALUE self)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
return cfunc->name ? rb_tainted_str_new2(cfunc->name) : Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* cfunc.ctype => num
|
||||
*
|
||||
* Get the C function return value type. See DL for a list of constants
|
||||
* corresponding to this method's return value.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_ctype(VALUE self)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
return INT2NUM(cfunc->type);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* cfunc.ctype = type
|
||||
*
|
||||
* Set the C function return value type to +type+.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_set_ctype(VALUE self, VALUE ctype)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
cfunc->type = NUM2INT(ctype);
|
||||
return ctype;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* cfunc.calltype => symbol
|
||||
*
|
||||
* Get the call type of this function.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_calltype(VALUE self)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
return ID2SYM(cfunc->calltype);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* cfunc.calltype = symbol
|
||||
*
|
||||
* Set the call type for this function.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_set_calltype(VALUE self, VALUE sym)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
cfunc->calltype = SYM2ID(sym);
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* cfunc.ptr
|
||||
*
|
||||
* Get the underlying function pointer as a DL::CPtr object.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_ptr(VALUE self)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
return PTR2NUM(cfunc->ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* cfunc.ptr = pointer
|
||||
*
|
||||
* Set the underlying function pointer to a DL::CPtr named +pointer+.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_set_ptr(VALUE self, VALUE addr)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
cfunc->ptr = NUM2PTR(addr);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* inspect
|
||||
* to_s
|
||||
*
|
||||
* Returns a string formatted with an easily readable representation of the
|
||||
* internal state of the DL::CFunc
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_inspect(VALUE self)
|
||||
{
|
||||
VALUE val;
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
|
||||
val = rb_sprintf("#<DL::CFunc:%p ptr=%p type=%d name='%s'>",
|
||||
cfunc,
|
||||
cfunc->ptr,
|
||||
cfunc->type,
|
||||
cfunc->name ? cfunc->name : "");
|
||||
OBJ_TAINT(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
# define DECL_FUNC_CDECL(f,ret,args,val) \
|
||||
ret (FUNC_CDECL(*(f)))(args) = (ret (FUNC_CDECL(*))(args))(VALUE)(val)
|
||||
#ifdef FUNC_STDCALL
|
||||
# define DECL_FUNC_STDCALL(f,ret,args,val) \
|
||||
ret (FUNC_STDCALL(*(f)))(args) = (ret (FUNC_STDCALL(*))(args))(VALUE)(val)
|
||||
#endif
|
||||
|
||||
#define CALL_CASE switch( RARRAY_LEN(ary) ){ \
|
||||
CASE(0); break; \
|
||||
CASE(1); break; CASE(2); break; CASE(3); break; CASE(4); break; CASE(5); break; \
|
||||
CASE(6); break; CASE(7); break; CASE(8); break; CASE(9); break; CASE(10);break; \
|
||||
CASE(11);break; CASE(12);break; CASE(13);break; CASE(14);break; CASE(15);break; \
|
||||
CASE(16);break; CASE(17);break; CASE(18);break; CASE(19);break; CASE(20);break; \
|
||||
default: rb_raise(rb_eArgError, "too many arguments"); \
|
||||
}
|
||||
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64) && _MSC_VER >= 1400 && _MSC_VER < 1600
|
||||
# pragma optimize("", off)
|
||||
#endif
|
||||
/*
|
||||
* call-seq:
|
||||
* dlcfunc.call(ary) => some_value
|
||||
* dlcfunc[ary] => some_value
|
||||
*
|
||||
* Calls the function pointer passing in +ary+ as values to the underlying
|
||||
* C function. The return value depends on the ctype.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_call(VALUE self, VALUE ary)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
int i;
|
||||
DLSTACK_TYPE stack[DLSTACK_SIZE];
|
||||
VALUE result = Qnil;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for( i = 0; i < RARRAY_LEN(ary); i++ ){
|
||||
VALUE arg;
|
||||
if( i >= DLSTACK_SIZE ){
|
||||
rb_raise(rb_eDLError, "too many arguments (stack overflow)");
|
||||
}
|
||||
arg = rb_to_int(RARRAY_PTR(ary)[i]);
|
||||
rb_check_safe_obj(arg);
|
||||
if (FIXNUM_P(arg)) {
|
||||
stack[i] = (DLSTACK_TYPE)FIX2LONG(arg);
|
||||
}
|
||||
else if (RB_TYPE_P(arg, T_BIGNUM)) {
|
||||
unsigned long ls[(sizeof(DLSTACK_TYPE) + sizeof(long) - 1)/sizeof(long)];
|
||||
DLSTACK_TYPE d;
|
||||
int j;
|
||||
rb_big_pack(arg, ls, sizeof(ls)/sizeof(*ls));
|
||||
d = 0;
|
||||
for (j = 0; j < (int)(sizeof(ls)/sizeof(*ls)); j++)
|
||||
d |= (DLSTACK_TYPE)ls[j] << (j * sizeof(long) * CHAR_BIT);
|
||||
stack[i] = d;
|
||||
}
|
||||
else {
|
||||
Check_Type(arg, T_FIXNUM);
|
||||
}
|
||||
}
|
||||
|
||||
/* calltype == CFUNC_CDECL */
|
||||
if( cfunc->calltype == CFUNC_CDECL
|
||||
#ifndef FUNC_STDCALL
|
||||
|| cfunc->calltype == CFUNC_STDCALL
|
||||
#endif
|
||||
){
|
||||
switch( cfunc->type ){
|
||||
case DLTYPE_VOID:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,void,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
f(DLSTACK_ARGS##n(stack)); \
|
||||
result = Qnil; \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_VOIDP:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,void*,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
void * ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = PTR2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_CHAR:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,char,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
char ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = CHR2FIX(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_SHORT:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,short,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
short ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = INT2NUM((int)ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_INT:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,int,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
int ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = INT2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_LONG:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,long,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
long ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = LONG2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
#if HAVE_LONG_LONG /* used in ruby.h */
|
||||
case DLTYPE_LONG_LONG:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,LONG_LONG,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
LONG_LONG ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = LL2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
#endif
|
||||
case DLTYPE_FLOAT:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,float,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
float ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = rb_float_new(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_DOUBLE:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n,cfunc->ptr); \
|
||||
double ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = rb_float_new(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type);
|
||||
}
|
||||
}
|
||||
#ifdef FUNC_STDCALL
|
||||
else if( cfunc->calltype == CFUNC_STDCALL ){
|
||||
/* calltype == CFUNC_STDCALL */
|
||||
switch( cfunc->type ){
|
||||
case DLTYPE_VOID:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,void,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
f(DLSTACK_ARGS##n(stack)); \
|
||||
result = Qnil; \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_VOIDP:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,void*,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
void * ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = PTR2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_CHAR:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,char,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
char ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = CHR2FIX(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_SHORT:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,short,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
short ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = INT2NUM((int)ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_INT:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,int,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
int ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = INT2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_LONG:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,long,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
long ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = LONG2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
#if HAVE_LONG_LONG /* used in ruby.h */
|
||||
case DLTYPE_LONG_LONG:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,LONG_LONG,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
LONG_LONG ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = LL2NUM(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
#endif
|
||||
case DLTYPE_FLOAT:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,float,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
float ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = rb_float_new(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
case DLTYPE_DOUBLE:
|
||||
#define CASE(n) case n: { \
|
||||
DECL_FUNC_STDCALL(f,double,DLSTACK_PROTO##n##_,cfunc->ptr); \
|
||||
double ret; \
|
||||
ret = f(DLSTACK_ARGS##n(stack)); \
|
||||
result = rb_float_new(ret); \
|
||||
}
|
||||
CALL_CASE;
|
||||
#undef CASE
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else{
|
||||
const char *name = rb_id2name(cfunc->calltype);
|
||||
if( name ){
|
||||
rb_raise(rb_eDLError, "unsupported call type: %s",
|
||||
name);
|
||||
}
|
||||
else{
|
||||
rb_raise(rb_eDLError, "unsupported call type: %"PRIxVALUE,
|
||||
cfunc->calltype);
|
||||
}
|
||||
}
|
||||
|
||||
rb_dl_set_last_error(self, INT2NUM(errno));
|
||||
#if defined(_WIN32)
|
||||
rb_dl_set_win32_last_error(self, INT2NUM(GetLastError()));
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64) && _MSC_VER >= 1400 && _MSC_VER < 1600
|
||||
# pragma optimize("", on)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* dlfunc.to_i => integer
|
||||
*
|
||||
* Returns the memory location of this function pointer as an integer.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlcfunc_to_i(VALUE self)
|
||||
{
|
||||
struct cfunc_data *cfunc;
|
||||
|
||||
TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
|
||||
return PTR2NUM(cfunc->ptr);
|
||||
}
|
||||
|
||||
void
|
||||
Init_dlcfunc(void)
|
||||
{
|
||||
id_last_error = rb_intern("__DL2_LAST_ERROR__");
|
||||
#if defined(_WIN32)
|
||||
id_win32_last_error = rb_intern("__DL2_WIN32_LAST_ERROR__");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Document-class: DL::CFunc
|
||||
*
|
||||
* A direct accessor to a function in a C library
|
||||
*
|
||||
* == Example
|
||||
*
|
||||
* libc_so = "/lib64/libc.so.6"
|
||||
* => "/lib64/libc.so.6"
|
||||
* libc = DL::dlopen(libc_so)
|
||||
* => #<DL::Handle:0x00000000e05b00>
|
||||
* @cfunc = DL::CFunc.new(libc['strcpy'], DL::TYPE_VOIDP, 'strcpy')
|
||||
* => #<DL::CFunc:0x000000012daec0 ptr=0x007f62ca5a8300 type=1 name='strcpy'>
|
||||
*
|
||||
*/
|
||||
rb_cDLCFunc = rb_define_class_under(rb_mDL, "CFunc", rb_cObject);
|
||||
rb_define_alloc_func(rb_cDLCFunc, rb_dlcfunc_s_allocate);
|
||||
|
||||
/*
|
||||
* Document-method: last_error
|
||||
*
|
||||
* Returns the last error for the current executing thread
|
||||
*/
|
||||
rb_define_module_function(rb_cDLCFunc, "last_error", rb_dl_get_last_error, 0);
|
||||
#if defined(_WIN32)
|
||||
|
||||
/*
|
||||
* Document-method: win32_last_error
|
||||
*
|
||||
* Returns the last win32 error for the current executing thread
|
||||
*/
|
||||
rb_define_module_function(rb_cDLCFunc, "win32_last_error", rb_dl_get_win32_last_error, 0);
|
||||
#endif
|
||||
rb_define_method(rb_cDLCFunc, "initialize", rb_dlcfunc_initialize, -1);
|
||||
rb_define_method(rb_cDLCFunc, "call", rb_dlcfunc_call, 1);
|
||||
rb_define_method(rb_cDLCFunc, "[]", rb_dlcfunc_call, 1);
|
||||
rb_define_method(rb_cDLCFunc, "name", rb_dlcfunc_name, 0);
|
||||
rb_define_method(rb_cDLCFunc, "ctype", rb_dlcfunc_ctype, 0);
|
||||
rb_define_method(rb_cDLCFunc, "ctype=", rb_dlcfunc_set_ctype, 1);
|
||||
rb_define_method(rb_cDLCFunc, "calltype", rb_dlcfunc_calltype, 0);
|
||||
rb_define_method(rb_cDLCFunc, "calltype=", rb_dlcfunc_set_calltype, 1);
|
||||
rb_define_method(rb_cDLCFunc, "ptr", rb_dlcfunc_ptr, 0);
|
||||
rb_define_method(rb_cDLCFunc, "ptr=", rb_dlcfunc_set_ptr, 1);
|
||||
rb_define_method(rb_cDLCFunc, "inspect", rb_dlcfunc_inspect, 0);
|
||||
rb_define_method(rb_cDLCFunc, "to_s", rb_dlcfunc_inspect, 0);
|
||||
rb_define_method(rb_cDLCFunc, "to_i", rb_dlcfunc_to_i, 0);
|
||||
}
|
669
ext/dl/cptr.c
669
ext/dl/cptr.c
|
@ -1,669 +0,0 @@
|
|||
/* -*- C -*-
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
#include <ruby/io.h>
|
||||
#include <ctype.h>
|
||||
#include "dl.h"
|
||||
|
||||
VALUE rb_cDLCPtr;
|
||||
|
||||
static inline freefunc_t
|
||||
get_freefunc(VALUE func, volatile VALUE *wrap)
|
||||
{
|
||||
VALUE addrnum;
|
||||
if (NIL_P(func)) {
|
||||
*wrap = 0;
|
||||
return NULL;
|
||||
}
|
||||
if (rb_dlcfunc_kind_p(func)) {
|
||||
*wrap = func;
|
||||
return (freefunc_t)(VALUE)RCFUNC_DATA(func)->ptr;
|
||||
}
|
||||
addrnum = rb_Integer(func);
|
||||
*wrap = (addrnum != func) ? func : 0;
|
||||
return (freefunc_t)(VALUE)NUM2PTR(addrnum);
|
||||
}
|
||||
|
||||
static ID id_to_ptr;
|
||||
|
||||
static void
|
||||
dlptr_mark(void *ptr)
|
||||
{
|
||||
struct ptr_data *data = ptr;
|
||||
if (data->wrap[0]) {
|
||||
rb_gc_mark(data->wrap[0]);
|
||||
}
|
||||
if (data->wrap[1]) {
|
||||
rb_gc_mark(data->wrap[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dlptr_free(void *ptr)
|
||||
{
|
||||
struct ptr_data *data = ptr;
|
||||
if (data->ptr) {
|
||||
if (data->free) {
|
||||
(*(data->free))(data->ptr);
|
||||
}
|
||||
}
|
||||
xfree(ptr);
|
||||
}
|
||||
|
||||
static size_t
|
||||
dlptr_memsize(const void *ptr)
|
||||
{
|
||||
const struct ptr_data *data = ptr;
|
||||
return data ? sizeof(*data) + data->size : 0;
|
||||
}
|
||||
|
||||
static const rb_data_type_t dlptr_data_type = {
|
||||
"dl/ptr",
|
||||
{dlptr_mark, dlptr_free, dlptr_memsize,},
|
||||
};
|
||||
|
||||
VALUE
|
||||
rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
VALUE val;
|
||||
|
||||
val = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data);
|
||||
data->ptr = ptr;
|
||||
data->free = func;
|
||||
data->size = size;
|
||||
OBJ_TAINT(val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_dlptr_new(void *ptr, long size, freefunc_t func)
|
||||
{
|
||||
return rb_dlptr_new2(rb_cDLCPtr, ptr, size, func);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_dlptr_malloc(long size, freefunc_t func)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = ruby_xmalloc((size_t)size);
|
||||
memset(ptr,0,(size_t)size);
|
||||
return rb_dlptr_new(ptr, size, func);
|
||||
}
|
||||
|
||||
void *
|
||||
rb_dlptr2cptr(VALUE val)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
void *ptr;
|
||||
|
||||
if (rb_obj_is_kind_of(val, rb_cDLCPtr)) {
|
||||
TypedData_Get_Struct(val, struct ptr_data, &dlptr_data_type, data);
|
||||
ptr = data->ptr;
|
||||
}
|
||||
else if (val == Qnil) {
|
||||
ptr = NULL;
|
||||
}
|
||||
else{
|
||||
rb_raise(rb_eTypeError, "DL::PtrData was expected");
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rb_dlptr_s_allocate(VALUE klass)
|
||||
{
|
||||
VALUE obj;
|
||||
struct ptr_data *data;
|
||||
|
||||
obj = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data);
|
||||
data->ptr = 0;
|
||||
data->size = 0;
|
||||
data->free = 0;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* DL::CPtr.new(address) => dl_cptr
|
||||
* DL::CPtr.new(address, size) => dl_cptr
|
||||
* DL::CPtr.new(address, size, freefunc) => dl_cptr
|
||||
*
|
||||
* Create a new pointer to +address+ with an optional +size+ and +freefunc+.
|
||||
* +freefunc+ will be called when the instance is garbage collected.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_initialize(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
VALUE ptr, sym, size, wrap = 0, funcwrap = 0;
|
||||
struct ptr_data *data;
|
||||
void *p = NULL;
|
||||
freefunc_t f = NULL;
|
||||
long s = 0;
|
||||
|
||||
if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) {
|
||||
VALUE addrnum = rb_Integer(ptr);
|
||||
if (addrnum != ptr) wrap = ptr;
|
||||
p = NUM2PTR(addrnum);
|
||||
}
|
||||
if (argc >= 2) {
|
||||
s = NUM2LONG(size);
|
||||
}
|
||||
if (argc >= 3) {
|
||||
f = get_freefunc(sym, &funcwrap);
|
||||
}
|
||||
|
||||
if (p) {
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
if (data->ptr && data->free) {
|
||||
/* Free previous memory. Use of inappropriate initialize may cause SEGV. */
|
||||
(*(data->free))(data->ptr);
|
||||
}
|
||||
data->wrap[0] = wrap;
|
||||
data->wrap[1] = funcwrap;
|
||||
data->ptr = p;
|
||||
data->size = s;
|
||||
data->free = f;
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
*
|
||||
* DL::CPtr.malloc(size, freefunc = nil) => dl cptr instance
|
||||
*
|
||||
* Allocate +size+ bytes of memory and associate it with an optional
|
||||
* +freefunc+ that will be called when the pointer is garbage collected.
|
||||
* +freefunc+ must be an address pointing to a function or an instance of
|
||||
* DL::CFunc
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_s_malloc(int argc, VALUE argv[], VALUE klass)
|
||||
{
|
||||
VALUE size, sym, obj, wrap = 0;
|
||||
long s;
|
||||
freefunc_t f;
|
||||
|
||||
switch (rb_scan_args(argc, argv, "11", &size, &sym)) {
|
||||
case 1:
|
||||
s = NUM2LONG(size);
|
||||
f = NULL;
|
||||
break;
|
||||
case 2:
|
||||
s = NUM2LONG(size);
|
||||
f = get_freefunc(sym, &wrap);
|
||||
break;
|
||||
default:
|
||||
rb_bug("rb_dlptr_s_malloc");
|
||||
}
|
||||
|
||||
obj = rb_dlptr_malloc(s,f);
|
||||
if (wrap) RPTR_DATA(obj)->wrap[1] = wrap;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_i
|
||||
*
|
||||
* Returns the integer memory location of this DL::CPtr.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_to_i(VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
return PTR2NUM(data->ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_value
|
||||
*
|
||||
* Cast this CPtr to a ruby object.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_to_value(VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
return (VALUE)(data->ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: ptr
|
||||
*
|
||||
* Returns a DL::CPtr that is a dereferenced pointer for this DL::CPtr.
|
||||
* Analogous to the star operator in C.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlptr_ptr(VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
return rb_dlptr_new(*((void**)(data->ptr)),0,0);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: ref
|
||||
*
|
||||
* Returns a DL::CPtr that is a reference pointer for this DL::CPtr.
|
||||
* Analogous to the ampersand operator in C.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlptr_ref(VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
return rb_dlptr_new(&(data->ptr),0,0);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: null?
|
||||
*
|
||||
* Returns true if this is a null pointer.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlptr_null_p(VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
return data->ptr ? Qfalse : Qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: free=(function)
|
||||
*
|
||||
* Set the free function for this pointer to the DL::CFunc in +function+.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_free_set(VALUE self, VALUE val)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
data->free = get_freefunc(val, &data->wrap[1]);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: free
|
||||
*
|
||||
* Get the free function for this pointer. Returns DL::CFunc or nil.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_free_get(VALUE self)
|
||||
{
|
||||
struct ptr_data *pdata;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, pdata);
|
||||
|
||||
return rb_dlcfunc_new(pdata->free, DLTYPE_VOID, "free<anonymous>", CFUNC_CDECL);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
*
|
||||
* ptr.to_s => string
|
||||
* ptr.to_s(len) => string
|
||||
*
|
||||
* Returns the pointer contents as a string. When called with no arguments,
|
||||
* this method will return the contents until the first NULL byte. When
|
||||
* called with +len+, a string of +len+ bytes will be returned.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_to_s(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
VALUE arg1, val;
|
||||
int len;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
switch (rb_scan_args(argc, argv, "01", &arg1)) {
|
||||
case 0:
|
||||
val = rb_tainted_str_new2((char*)(data->ptr));
|
||||
break;
|
||||
case 1:
|
||||
len = NUM2INT(arg1);
|
||||
val = rb_tainted_str_new((char*)(data->ptr), len);
|
||||
break;
|
||||
default:
|
||||
rb_bug("rb_dlptr_to_s");
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
*
|
||||
* ptr.to_str => string
|
||||
* ptr.to_str(len) => string
|
||||
*
|
||||
* Returns the pointer contents as a string. When called with no arguments,
|
||||
* this method will return the contents with the length of this pointer's
|
||||
* +size+. When called with +len+, a string of +len+ bytes will be returned.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_to_str(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
VALUE arg1, val;
|
||||
int len;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
switch (rb_scan_args(argc, argv, "01", &arg1)) {
|
||||
case 0:
|
||||
val = rb_tainted_str_new((char*)(data->ptr),data->size);
|
||||
break;
|
||||
case 1:
|
||||
len = NUM2INT(arg1);
|
||||
val = rb_tainted_str_new((char*)(data->ptr), len);
|
||||
break;
|
||||
default:
|
||||
rb_bug("rb_dlptr_to_str");
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: inspect
|
||||
*
|
||||
* Returns a string formatted with an easily readable representation of the
|
||||
* internal state of the DL::CPtr
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_inspect(VALUE self)
|
||||
{
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>",
|
||||
rb_obj_class(self), data, data->ptr, data->size, data->free);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ptr == other => true or false
|
||||
* ptr.eql?(other) => true or false
|
||||
*
|
||||
* Returns true if +other+ wraps the same pointer, otherwise returns
|
||||
* false.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlptr_eql(VALUE self, VALUE other)
|
||||
{
|
||||
void *ptr1, *ptr2;
|
||||
|
||||
if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qfalse;
|
||||
|
||||
ptr1 = rb_dlptr2cptr(self);
|
||||
ptr2 = rb_dlptr2cptr(other);
|
||||
|
||||
return ptr1 == ptr2 ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ptr <=> other => -1, 0, 1, or nil
|
||||
*
|
||||
* Returns -1 if less than, 0 if equal to, 1 if greater than +other+. Returns
|
||||
* nil if +ptr+ cannot be compared to +other+.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_cmp(VALUE self, VALUE other)
|
||||
{
|
||||
void *ptr1, *ptr2;
|
||||
SIGNED_VALUE diff;
|
||||
|
||||
if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qnil;
|
||||
|
||||
ptr1 = rb_dlptr2cptr(self);
|
||||
ptr2 = rb_dlptr2cptr(other);
|
||||
diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2;
|
||||
if (!diff) return INT2FIX(0);
|
||||
return diff > 0 ? INT2NUM(1) : INT2NUM(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ptr + n => new cptr
|
||||
*
|
||||
* Returns a new DL::CPtr that has been advanced +n+ bytes.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_plus(VALUE self, VALUE other)
|
||||
{
|
||||
void *ptr;
|
||||
long num, size;
|
||||
|
||||
ptr = rb_dlptr2cptr(self);
|
||||
size = RPTR_DATA(self)->size;
|
||||
num = NUM2LONG(other);
|
||||
return rb_dlptr_new((char *)ptr + num, size - num, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ptr - n => new cptr
|
||||
*
|
||||
* Returns a new DL::CPtr that has been moved back +n+ bytes.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_minus(VALUE self, VALUE other)
|
||||
{
|
||||
void *ptr;
|
||||
long num, size;
|
||||
|
||||
ptr = rb_dlptr2cptr(self);
|
||||
size = RPTR_DATA(self)->size;
|
||||
num = NUM2LONG(other);
|
||||
return rb_dlptr_new((char *)ptr - num, size + num, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ptr[index] -> an_integer
|
||||
* ptr[start, length] -> a_string
|
||||
*
|
||||
* Returns integer stored at _index_. If _start_ and _length_ are given,
|
||||
* a string containing the bytes from _start_ of length _length_ will be
|
||||
* returned.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
VALUE arg0, arg1;
|
||||
VALUE retval = Qnil;
|
||||
size_t offset, len;
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference");
|
||||
switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
|
||||
case 1:
|
||||
offset = NUM2ULONG(arg0);
|
||||
retval = INT2NUM(*((char *)data->ptr + offset));
|
||||
break;
|
||||
case 2:
|
||||
offset = NUM2ULONG(arg0);
|
||||
len = NUM2ULONG(arg1);
|
||||
retval = rb_tainted_str_new((char *)data->ptr + offset, len);
|
||||
break;
|
||||
default:
|
||||
rb_bug("rb_dlptr_aref()");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ptr[index] = int -> int
|
||||
* ptr[start, length] = string or cptr or addr -> string or dl_cptr or addr
|
||||
*
|
||||
* Set the value at +index+ to +int+. Or, set the memory at +start+ until
|
||||
* +length+ with the contents of +string+, the memory from +dl_cptr+, or the
|
||||
* memory pointed at by the memory address +addr+.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
VALUE arg0, arg1, arg2;
|
||||
VALUE retval = Qnil;
|
||||
size_t offset, len;
|
||||
void *mem;
|
||||
struct ptr_data *data;
|
||||
|
||||
TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
|
||||
if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference");
|
||||
switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
|
||||
case 2:
|
||||
offset = NUM2ULONG(arg0);
|
||||
((char*)data->ptr)[offset] = NUM2UINT(arg1);
|
||||
retval = arg1;
|
||||
break;
|
||||
case 3:
|
||||
offset = NUM2ULONG(arg0);
|
||||
len = NUM2ULONG(arg1);
|
||||
if (RB_TYPE_P(arg2, T_STRING)) {
|
||||
mem = StringValuePtr(arg2);
|
||||
}
|
||||
else if( rb_obj_is_kind_of(arg2, rb_cDLCPtr) ){
|
||||
mem = rb_dlptr2cptr(arg2);
|
||||
}
|
||||
else{
|
||||
mem = NUM2PTR(arg2);
|
||||
}
|
||||
memcpy((char *)data->ptr + offset, mem, len);
|
||||
retval = arg2;
|
||||
break;
|
||||
default:
|
||||
rb_bug("rb_dlptr_aset()");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: size=(size)
|
||||
*
|
||||
* Set the size of this pointer to +size+
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_size_set(VALUE self, VALUE size)
|
||||
{
|
||||
RPTR_DATA(self)->size = NUM2LONG(size);
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: size
|
||||
*
|
||||
* Get the size of this pointer.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_size_get(VALUE self)
|
||||
{
|
||||
return LONG2NUM(RPTR_DATA(self)->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* DL::CPtr.to_ptr(val) => cptr
|
||||
* DL::CPtr[val] => cptr
|
||||
*
|
||||
* Get the underlying pointer for ruby object +val+ and return it as a
|
||||
* DL::CPtr object.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlptr_s_to_ptr(VALUE self, VALUE val)
|
||||
{
|
||||
VALUE ptr, wrap = val, vptr;
|
||||
|
||||
if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){
|
||||
rb_io_t *fptr;
|
||||
FILE *fp;
|
||||
GetOpenFile(val, fptr);
|
||||
fp = rb_io_stdio_file(fptr);
|
||||
ptr = rb_dlptr_new(fp, 0, NULL);
|
||||
}
|
||||
else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){
|
||||
char *str = StringValuePtr(val);
|
||||
ptr = rb_dlptr_new(str, RSTRING_LEN(val), NULL);
|
||||
}
|
||||
else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){
|
||||
if (rb_obj_is_kind_of(vptr, rb_cDLCPtr)){
|
||||
ptr = vptr;
|
||||
wrap = 0;
|
||||
}
|
||||
else{
|
||||
rb_raise(rb_eDLError, "to_ptr should return a CPtr object");
|
||||
}
|
||||
}
|
||||
else{
|
||||
VALUE num = rb_Integer(val);
|
||||
if (num == val) wrap = 0;
|
||||
ptr = rb_dlptr_new(NUM2PTR(num), 0, NULL);
|
||||
}
|
||||
OBJ_INFECT(ptr, val);
|
||||
if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void
|
||||
Init_dlptr(void)
|
||||
{
|
||||
id_to_ptr = rb_intern("to_ptr");
|
||||
|
||||
/* Document-class: DL::CPtr
|
||||
*
|
||||
* CPtr is a class to handle C pointers
|
||||
*
|
||||
*/
|
||||
rb_cDLCPtr = rb_define_class_under(rb_mDL, "CPtr", rb_cObject);
|
||||
rb_define_alloc_func(rb_cDLCPtr, rb_dlptr_s_allocate);
|
||||
rb_define_singleton_method(rb_cDLCPtr, "malloc", rb_dlptr_s_malloc, -1);
|
||||
rb_define_singleton_method(rb_cDLCPtr, "to_ptr", rb_dlptr_s_to_ptr, 1);
|
||||
rb_define_singleton_method(rb_cDLCPtr, "[]", rb_dlptr_s_to_ptr, 1);
|
||||
rb_define_method(rb_cDLCPtr, "initialize", rb_dlptr_initialize, -1);
|
||||
rb_define_method(rb_cDLCPtr, "free=", rb_dlptr_free_set, 1);
|
||||
rb_define_method(rb_cDLCPtr, "free", rb_dlptr_free_get, 0);
|
||||
rb_define_method(rb_cDLCPtr, "to_i", rb_dlptr_to_i, 0);
|
||||
rb_define_method(rb_cDLCPtr, "to_int", rb_dlptr_to_i, 0);
|
||||
rb_define_method(rb_cDLCPtr, "to_value", rb_dlptr_to_value, 0);
|
||||
rb_define_method(rb_cDLCPtr, "ptr", rb_dlptr_ptr, 0);
|
||||
rb_define_method(rb_cDLCPtr, "+@", rb_dlptr_ptr, 0);
|
||||
rb_define_method(rb_cDLCPtr, "ref", rb_dlptr_ref, 0);
|
||||
rb_define_method(rb_cDLCPtr, "-@", rb_dlptr_ref, 0);
|
||||
rb_define_method(rb_cDLCPtr, "null?", rb_dlptr_null_p, 0);
|
||||
rb_define_method(rb_cDLCPtr, "to_s", rb_dlptr_to_s, -1);
|
||||
rb_define_method(rb_cDLCPtr, "to_str", rb_dlptr_to_str, -1);
|
||||
rb_define_method(rb_cDLCPtr, "inspect", rb_dlptr_inspect, 0);
|
||||
rb_define_method(rb_cDLCPtr, "<=>", rb_dlptr_cmp, 1);
|
||||
rb_define_method(rb_cDLCPtr, "==", rb_dlptr_eql, 1);
|
||||
rb_define_method(rb_cDLCPtr, "eql?", rb_dlptr_eql, 1);
|
||||
rb_define_method(rb_cDLCPtr, "+", rb_dlptr_plus, 1);
|
||||
rb_define_method(rb_cDLCPtr, "-", rb_dlptr_minus, 1);
|
||||
rb_define_method(rb_cDLCPtr, "[]", rb_dlptr_aref, -1);
|
||||
rb_define_method(rb_cDLCPtr, "[]=", rb_dlptr_aset, -1);
|
||||
rb_define_method(rb_cDLCPtr, "size", rb_dlptr_size_get, 0);
|
||||
rb_define_method(rb_cDLCPtr, "size=", rb_dlptr_size_set, 1);
|
||||
|
||||
/* Document-const: NULL
|
||||
*
|
||||
* A NULL pointer
|
||||
*/
|
||||
rb_define_const(rb_mDL, "NULL", rb_dlptr_new(0, 0, 0));
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
cfunc.o: cfunc.c dl.h $(HDRS) $(ruby_headers) \
|
||||
$(hdrdir)/ruby/util.h
|
||||
|
||||
cptr.o: cptr.c dl.h $(HDRS) $(ruby_headers) \
|
||||
$(hdrdir)/ruby/io.h \
|
||||
$(hdrdir)/ruby/encoding.h \
|
||||
$(hdrdir)/ruby/oniguruma.h
|
||||
|
||||
handle.o: handle.c dl.h $(HDRS) $(ruby_headers)
|
||||
|
||||
dl.o: dl.c dl.h $(HDRS) $(ruby_headers) \
|
||||
$(hdrdir)/ruby/io.h \
|
||||
$(hdrdir)/ruby/encoding.h \
|
||||
$(hdrdir)/ruby/oniguruma.h
|
569
ext/dl/dl.c
569
ext/dl/dl.c
|
@ -1,569 +0,0 @@
|
|||
/*
|
||||
* ext/dl/dl.c
|
||||
*
|
||||
* documentation:
|
||||
* - Vincent Batts (vbatts@hashbangbash.com)
|
||||
*
|
||||
*/
|
||||
#include <ruby/ruby.h>
|
||||
#include <ruby/io.h>
|
||||
#include <ctype.h>
|
||||
#include "dl.h"
|
||||
|
||||
VALUE rb_mDL;
|
||||
VALUE rb_eDLError;
|
||||
VALUE rb_eDLTypeError;
|
||||
|
||||
ID rbdl_id_cdecl;
|
||||
ID rbdl_id_stdcall;
|
||||
|
||||
#ifndef DLTYPE_SSIZE_T
|
||||
# if SIZEOF_SIZE_T == SIZEOF_INT
|
||||
# define DLTYPE_SSIZE_T DLTYPE_INT
|
||||
# elif SIZEOF_SIZE_T == SIZEOF_LONG
|
||||
# define DLTYPE_SSIZE_T DLTYPE_LONG
|
||||
# elif defined HAVE_LONG_LONG && SIZEOF_SIZE_T == SIZEOF_LONG_LONG
|
||||
# define DLTYPE_SSIZE_T DLTYPE_LONG_LONG
|
||||
# endif
|
||||
#endif
|
||||
#define DLTYPE_SIZE_T (-1*SIGNEDNESS_OF_SIZE_T*DLTYPE_SSIZE_T)
|
||||
|
||||
#ifndef DLTYPE_PTRDIFF_T
|
||||
# if SIZEOF_PTRDIFF_T == SIZEOF_INT
|
||||
# define DLTYPE_PTRDIFF_T DLTYPE_INT
|
||||
# elif SIZEOF_PTRDIFF_T == SIZEOF_LONG
|
||||
# define DLTYPE_PTRDIFF_T DLTYPE_LONG
|
||||
# elif defined HAVE_LONG_LONG && SIZEOF_PTRDIFF_T == SIZEOF_LONG_LONG
|
||||
# define DLTYPE_PTRDIFF_T DLTYPE_LONG_LONG
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef DLTYPE_INTPTR_T
|
||||
# if SIZEOF_INTPTR_T == SIZEOF_INT
|
||||
# define DLTYPE_INTPTR_T DLTYPE_INT
|
||||
# elif SIZEOF_INTPTR_T == SIZEOF_LONG
|
||||
# define DLTYPE_INTPTR_T DLTYPE_LONG
|
||||
# elif defined HAVE_LONG_LONG && SIZEOF_INTPTR_T == SIZEOF_LONG_LONG
|
||||
# define DLTYPE_INTPTR_T DLTYPE_LONG_LONG
|
||||
# endif
|
||||
#endif
|
||||
#define DLTYPE_UINTPTR_T (-DLTYPE_INTPTR_T)
|
||||
|
||||
/*
|
||||
* call-seq: DL.dlopen(so_lib)
|
||||
*
|
||||
* An interface to the dynamic linking loader
|
||||
*
|
||||
* This is a shortcut to DL::Handle.new and takes the same arguments.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* libc_so = "/lib64/libc.so.6"
|
||||
* => "/lib64/libc.so.6"
|
||||
*
|
||||
* libc = DL.dlopen(libc_so)
|
||||
* => #<DL::Handle:0x00000000e05b00>
|
||||
*/
|
||||
VALUE
|
||||
rb_dl_dlopen(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
return rb_class_new_instance(argc, argv, rb_cDLHandle);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: DL.malloc(size)
|
||||
*
|
||||
* Allocate +size+ bytes of memory and return the integer memory address
|
||||
* for the allocated memory.
|
||||
*/
|
||||
VALUE
|
||||
rb_dl_malloc(VALUE self, VALUE size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = (void*)ruby_xmalloc(NUM2SIZET(size));
|
||||
return PTR2NUM(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: DL.realloc(addr, size)
|
||||
*
|
||||
* Change the size of the memory allocated at the memory location +addr+ to
|
||||
* +size+ bytes. Returns the memory address of the reallocated memory, which
|
||||
* may be different than the address passed in.
|
||||
*/
|
||||
VALUE
|
||||
rb_dl_realloc(VALUE self, VALUE addr, VALUE size)
|
||||
{
|
||||
void *ptr = NUM2PTR(addr);
|
||||
|
||||
ptr = (void*)ruby_xrealloc(ptr, NUM2SIZET(size));
|
||||
return PTR2NUM(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: DL.free(addr)
|
||||
*
|
||||
* Free the memory at address +addr+
|
||||
*/
|
||||
VALUE
|
||||
rb_dl_free(VALUE self, VALUE addr)
|
||||
{
|
||||
void *ptr = NUM2PTR(addr);
|
||||
|
||||
ruby_xfree(ptr);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: DL.dlunwrap(addr)
|
||||
*
|
||||
* Returns the hexadecimal representation of a memory pointer address +addr+
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* lib = DL.dlopen('/lib64/libc-2.15.so')
|
||||
* => #<DL::Handle:0x00000001342460>
|
||||
*
|
||||
* lib['strcpy'].to_s(16)
|
||||
* => "7f59de6dd240"
|
||||
*
|
||||
* DL.dlunwrap(DL.dlwrap(lib['strcpy'].to_s(16)))
|
||||
* => "7f59de6dd240"
|
||||
*/
|
||||
VALUE
|
||||
rb_dl_ptr2value(VALUE self, VALUE addr)
|
||||
{
|
||||
return (VALUE)NUM2PTR(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: DL.dlwrap(val)
|
||||
*
|
||||
* Returns a memory pointer of a function's hexadecimal address location +val+
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* lib = DL.dlopen('/lib64/libc-2.15.so')
|
||||
* => #<DL::Handle:0x00000001342460>
|
||||
*
|
||||
* DL.dlwrap(lib['strcpy'].to_s(16))
|
||||
* => 25522520
|
||||
*/
|
||||
VALUE
|
||||
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)
|
||||
{
|
||||
void Init_dlhandle(void);
|
||||
void Init_dlcfunc(void);
|
||||
void Init_dlptr(void);
|
||||
|
||||
rbdl_id_cdecl = rb_intern_const("cdecl");
|
||||
rbdl_id_stdcall = rb_intern_const("stdcall");
|
||||
|
||||
/* Document-module: DL
|
||||
*
|
||||
* A bridge to the dlopen() or dynamic library linker function.
|
||||
*
|
||||
* == Example
|
||||
*
|
||||
* bash $> cat > sum.c <<EOF
|
||||
* double sum(double *arry, int len)
|
||||
* {
|
||||
* double ret = 0;
|
||||
* int i;
|
||||
* for(i = 0; i < len; i++){
|
||||
* ret = ret + arry[i];
|
||||
* }
|
||||
* return ret;
|
||||
* }
|
||||
*
|
||||
* double split(double num)
|
||||
* {
|
||||
* double ret = 0;
|
||||
* ret = num / 2;
|
||||
* return ret;
|
||||
* }
|
||||
* EOF
|
||||
* bash $> gcc -o libsum.so -shared sum.c
|
||||
* bash $> cat > sum.rb <<EOF
|
||||
* require 'dl'
|
||||
* require 'dl/import'
|
||||
*
|
||||
* module LibSum
|
||||
* extend DL::Importer
|
||||
* dlload './libsum.so'
|
||||
* extern 'double sum(double*, int)'
|
||||
* extern 'double split(double)'
|
||||
* end
|
||||
*
|
||||
* a = [2.0, 3.0, 4.0]
|
||||
*
|
||||
* sum = LibSum.sum(a.pack("d*"), a.count)
|
||||
* p LibSum.split(sum)
|
||||
* EOF
|
||||
* bash $> ruby sum.rb
|
||||
* 4.5
|
||||
*
|
||||
* WIN! :-)
|
||||
*/
|
||||
rb_mDL = rb_define_module("DL");
|
||||
|
||||
/*
|
||||
* Document-class: DL::DLError
|
||||
*
|
||||
* standard dynamic load exception
|
||||
*/
|
||||
rb_eDLError = rb_define_class_under(rb_mDL, "DLError", rb_eStandardError);
|
||||
|
||||
/*
|
||||
* Document-class: DL::DLTypeError
|
||||
*
|
||||
* dynamic load incorrect type exception
|
||||
*/
|
||||
rb_eDLTypeError = rb_define_class_under(rb_mDL, "DLTypeError", rb_eDLError);
|
||||
|
||||
/* Document-const: MAX_CALLBACK
|
||||
*
|
||||
* Maximum number of callbacks
|
||||
*/
|
||||
rb_define_const(rb_mDL, "MAX_CALLBACK", INT2NUM(MAX_CALLBACK));
|
||||
|
||||
/* Document-const: DLSTACK_SIZE
|
||||
*
|
||||
* Dynamic linker stack size
|
||||
*/
|
||||
rb_define_const(rb_mDL, "DLSTACK_SIZE", INT2NUM(DLSTACK_SIZE));
|
||||
|
||||
rb_dl_init_callbacks(rb_mDL);
|
||||
|
||||
/* Document-const: RTLD_GLOBAL
|
||||
*
|
||||
* rtld DL::Handle flag.
|
||||
*
|
||||
* The symbols defined by this library will be made available for symbol
|
||||
* resolution of subsequently loaded libraries.
|
||||
*/
|
||||
rb_define_const(rb_mDL, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
|
||||
|
||||
/* Document-const: RTLD_LAZY
|
||||
*
|
||||
* rtld DL::Handle flag.
|
||||
*
|
||||
* Perform lazy binding. Only resolve symbols as the code that references
|
||||
* them is executed. If the symbol is never referenced, then it is never
|
||||
* resolved. (Lazy binding is only performed for function references;
|
||||
* references to variables are always immediately bound when the library
|
||||
* is loaded.)
|
||||
*/
|
||||
rb_define_const(rb_mDL, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
|
||||
|
||||
/* Document-const: RTLD_NOW
|
||||
*
|
||||
* rtld DL::Handle flag.
|
||||
*
|
||||
* If this value is specified or the environment variable LD_BIND_NOW is
|
||||
* set to a nonempty string, all undefined symbols in the library are
|
||||
* resolved before dlopen() returns. If this cannot be done an error is
|
||||
* returned.
|
||||
*/
|
||||
rb_define_const(rb_mDL, "RTLD_NOW", INT2NUM(RTLD_NOW));
|
||||
|
||||
/* Document-const: TYPE_VOID
|
||||
*
|
||||
* DL::CFunc type - void
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_VOID", INT2NUM(DLTYPE_VOID));
|
||||
|
||||
/* Document-const: TYPE_VOIDP
|
||||
*
|
||||
* DL::CFunc type - void*
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_VOIDP", INT2NUM(DLTYPE_VOIDP));
|
||||
|
||||
/* Document-const: TYPE_CHAR
|
||||
*
|
||||
* DL::CFunc type - char
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_CHAR", INT2NUM(DLTYPE_CHAR));
|
||||
|
||||
/* Document-const: TYPE_SHORT
|
||||
*
|
||||
* DL::CFunc type - short
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_SHORT", INT2NUM(DLTYPE_SHORT));
|
||||
|
||||
/* Document-const: TYPE_INT
|
||||
*
|
||||
* DL::CFunc type - int
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_INT", INT2NUM(DLTYPE_INT));
|
||||
|
||||
/* Document-const: TYPE_LONG
|
||||
*
|
||||
* DL::CFunc type - long
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_LONG", INT2NUM(DLTYPE_LONG));
|
||||
|
||||
#if HAVE_LONG_LONG
|
||||
/* Document-const: TYPE_LONG_LONG
|
||||
*
|
||||
* DL::CFunc type - long long
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_LONG_LONG", INT2NUM(DLTYPE_LONG_LONG));
|
||||
#endif
|
||||
|
||||
/* Document-const: TYPE_FLOAT
|
||||
*
|
||||
* DL::CFunc type - float
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_FLOAT", INT2NUM(DLTYPE_FLOAT));
|
||||
|
||||
/* Document-const: TYPE_DOUBLE
|
||||
*
|
||||
* DL::CFunc type - double
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_DOUBLE", INT2NUM(DLTYPE_DOUBLE));
|
||||
|
||||
/* Document-const: TYPE_SIZE_T
|
||||
*
|
||||
* DL::CFunc type - size_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_SIZE_T", INT2NUM(DLTYPE_SIZE_T));
|
||||
|
||||
/* Document-const: TYPE_SSIZE_T
|
||||
*
|
||||
* DL::CFunc type - ssize_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_SSIZE_T", INT2NUM(DLTYPE_SSIZE_T));
|
||||
|
||||
/* Document-const: TYPE_PTRDIFF_T
|
||||
*
|
||||
* DL::CFunc type - ptrdiff_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_PTRDIFF_T", INT2NUM(DLTYPE_PTRDIFF_T));
|
||||
|
||||
/* Document-const: TYPE_INTPTR_T
|
||||
*
|
||||
* DL::CFunc type - intptr_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_INTPTR_T", INT2NUM(DLTYPE_INTPTR_T));
|
||||
|
||||
/* Document-const: TYPE_UINTPTR_T
|
||||
*
|
||||
* DL::CFunc type - uintptr_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "TYPE_UINTPTR_T", INT2NUM(DLTYPE_UINTPTR_T));
|
||||
|
||||
/* Document-const: ALIGN_VOIDP
|
||||
*
|
||||
* The alignment size of a void*
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_VOIDP", INT2NUM(ALIGN_VOIDP));
|
||||
|
||||
/* Document-const: ALIGN_CHAR
|
||||
*
|
||||
* The alignment size of a char
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_CHAR", INT2NUM(ALIGN_CHAR));
|
||||
|
||||
/* Document-const: ALIGN_SHORT
|
||||
*
|
||||
* The alignment size of a short
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_SHORT", INT2NUM(ALIGN_SHORT));
|
||||
|
||||
/* Document-const: ALIGN_INT
|
||||
*
|
||||
* The alignment size of an int
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_INT", INT2NUM(ALIGN_INT));
|
||||
|
||||
/* Document-const: ALIGN_LONG
|
||||
*
|
||||
* The alignment size of a long
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_LONG", INT2NUM(ALIGN_LONG));
|
||||
|
||||
#if HAVE_LONG_LONG
|
||||
/* Document-const: ALIGN_LONG_LONG
|
||||
*
|
||||
* The alignment size of a long long
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_LONG_LONG", INT2NUM(ALIGN_LONG_LONG));
|
||||
#endif
|
||||
|
||||
/* Document-const: ALIGN_FLOAT
|
||||
*
|
||||
* The alignment size of a float
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_FLOAT", INT2NUM(ALIGN_FLOAT));
|
||||
|
||||
/* Document-const: ALIGN_DOUBLE
|
||||
*
|
||||
* The alignment size of a double
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_DOUBLE",INT2NUM(ALIGN_DOUBLE));
|
||||
|
||||
/* Document-const: ALIGN_SIZE_T
|
||||
*
|
||||
* The alignment size of a size_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_SIZE_T", INT2NUM(ALIGN_OF(size_t)));
|
||||
|
||||
/* Document-const: ALIGN_SSIZE_T
|
||||
*
|
||||
* The alignment size of a ssize_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_SSIZE_T", INT2NUM(ALIGN_OF(size_t))); /* same as size_t */
|
||||
|
||||
/* Document-const: ALIGN_PTRDIFF_T
|
||||
*
|
||||
* The alignment size of a ptrdiff_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_PTRDIFF_T", INT2NUM(ALIGN_OF(ptrdiff_t)));
|
||||
|
||||
/* Document-const: ALIGN_INTPTR_T
|
||||
*
|
||||
* The alignment size of a intptr_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_INTPTR_T", INT2NUM(ALIGN_OF(intptr_t)));
|
||||
|
||||
/* Document-const: ALIGN_UINTPTR_T
|
||||
*
|
||||
* The alignment size of a uintptr_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "ALIGN_UINTPTR_T", INT2NUM(ALIGN_OF(uintptr_t)));
|
||||
|
||||
/* Document-const: SIZEOF_VOIDP
|
||||
*
|
||||
* size of a void*
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_VOIDP", INT2NUM(sizeof(void*)));
|
||||
|
||||
/* Document-const: SIZEOF_CHAR
|
||||
*
|
||||
* size of a char
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_CHAR", INT2NUM(sizeof(char)));
|
||||
|
||||
/* Document-const: SIZEOF_SHORT
|
||||
*
|
||||
* size of a short
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_SHORT", INT2NUM(sizeof(short)));
|
||||
|
||||
/* Document-const: SIZEOF_INT
|
||||
*
|
||||
* size of an int
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_INT", INT2NUM(sizeof(int)));
|
||||
|
||||
/* Document-const: SIZEOF_LONG
|
||||
*
|
||||
* size of a long
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_LONG", INT2NUM(sizeof(long)));
|
||||
|
||||
#if HAVE_LONG_LONG
|
||||
/* Document-const: SIZEOF_LONG_LONG
|
||||
*
|
||||
* size of a long long
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_LONG_LONG", INT2NUM(sizeof(LONG_LONG)));
|
||||
#endif
|
||||
|
||||
/* Document-const: SIZEOF_FLOAT
|
||||
*
|
||||
* size of a float
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_FLOAT", INT2NUM(sizeof(float)));
|
||||
|
||||
/* Document-const: SIZEOF_DOUBLE
|
||||
*
|
||||
* size of a double
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_DOUBLE",INT2NUM(sizeof(double)));
|
||||
|
||||
/* Document-const: SIZEOF_SIZE_T
|
||||
*
|
||||
* size of a size_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_SIZE_T", INT2NUM(sizeof(size_t)));
|
||||
|
||||
/* Document-const: SIZEOF_SSIZE_T
|
||||
*
|
||||
* size of a ssize_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_SSIZE_T", INT2NUM(sizeof(size_t))); /* same as size_t */
|
||||
|
||||
/* Document-const: SIZEOF_PTRDIFF_T
|
||||
*
|
||||
* size of a ptrdiff_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_PTRDIFF_T", INT2NUM(sizeof(ptrdiff_t)));
|
||||
|
||||
/* Document-const: SIZEOF_INTPTR_T
|
||||
*
|
||||
* size of a intptr_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_INTPTR_T", INT2NUM(sizeof(intptr_t)));
|
||||
|
||||
/* Document-const: SIZEOF_UINTPTR_T
|
||||
*
|
||||
* size of a uintptr_t
|
||||
*/
|
||||
rb_define_const(rb_mDL, "SIZEOF_UINTPTR_T", INT2NUM(sizeof(uintptr_t)));
|
||||
|
||||
rb_define_module_function(rb_mDL, "dlwrap", rb_dl_value2ptr, 1);
|
||||
rb_define_module_function(rb_mDL, "dlunwrap", rb_dl_ptr2value, 1);
|
||||
|
||||
rb_define_module_function(rb_mDL, "dlopen", rb_dl_dlopen, -1);
|
||||
rb_define_module_function(rb_mDL, "malloc", rb_dl_malloc, 1);
|
||||
rb_define_module_function(rb_mDL, "realloc", rb_dl_realloc, 2);
|
||||
rb_define_module_function(rb_mDL, "free", rb_dl_free, 1);
|
||||
|
||||
/* Document-const: RUBY_FREE
|
||||
*
|
||||
* Address of the ruby_xfree() function
|
||||
*/
|
||||
rb_define_const(rb_mDL, "RUBY_FREE", PTR2NUM(ruby_xfree));
|
||||
|
||||
/* Document-const: BUILD_RUBY_PLATFORM
|
||||
*
|
||||
* Platform built against (i.e. "x86_64-linux", etc.)
|
||||
*
|
||||
* See also RUBY_PLATFORM
|
||||
*/
|
||||
rb_define_const(rb_mDL, "BUILD_RUBY_PLATFORM", rb_str_new2(RUBY_PLATFORM));
|
||||
|
||||
/* Document-const: BUILD_RUBY_VERSION
|
||||
*
|
||||
* Ruby Version built. (i.e. "1.9.3")
|
||||
*
|
||||
* See also RUBY_VERSION
|
||||
*/
|
||||
rb_define_const(rb_mDL, "BUILD_RUBY_VERSION", rb_str_new2(RUBY_VERSION));
|
||||
|
||||
Init_dlhandle();
|
||||
Init_dlcfunc();
|
||||
Init_dlptr();
|
||||
}
|
217
ext/dl/dl.h
217
ext/dl/dl.h
|
@ -1,217 +0,0 @@
|
|||
#ifndef RUBY_DL_H
|
||||
#define RUBY_DL_H
|
||||
|
||||
#include <ruby.h>
|
||||
|
||||
#if !defined(FUNC_CDECL)
|
||||
# define FUNC_CDECL(x) x
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DLFCN_H)
|
||||
# include <dlfcn.h>
|
||||
# /* some stranger systems may not define all of these */
|
||||
#ifndef RTLD_LAZY
|
||||
#define RTLD_LAZY 0
|
||||
#endif
|
||||
#ifndef RTLD_GLOBAL
|
||||
#define RTLD_GLOBAL 0
|
||||
#endif
|
||||
#ifndef RTLD_NOW
|
||||
#define RTLD_NOW 0
|
||||
#endif
|
||||
#else
|
||||
# if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
# define dlopen(name,flag) ((void)(flag),(void*)LoadLibrary(name))
|
||||
# define dlerror() strerror(rb_w32_map_errno(GetLastError()))
|
||||
# define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
|
||||
# define RTLD_LAZY -1
|
||||
# define RTLD_NOW -1
|
||||
# define RTLD_GLOBAL -1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define MAX_CALLBACK 5
|
||||
#define DLSTACK_TYPE SIGNED_VALUE
|
||||
#define DLSTACK_SIZE (20)
|
||||
#define DLSTACK_PROTO \
|
||||
DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,\
|
||||
DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,\
|
||||
DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,\
|
||||
DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE
|
||||
#define DLSTACK_ARGS(stack) \
|
||||
(stack)[0],(stack)[1],(stack)[2],(stack)[3],(stack)[4],\
|
||||
(stack)[5],(stack)[6],(stack)[7],(stack)[8],(stack)[9],\
|
||||
(stack)[10],(stack)[11],(stack)[12],(stack)[13],(stack)[14],\
|
||||
(stack)[15],(stack)[16],(stack)[17],(stack)[18],(stack)[19]
|
||||
|
||||
#define DLSTACK_PROTO0_ void
|
||||
#define DLSTACK_PROTO1_ DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO2_ DLSTACK_PROTO1_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO3_ DLSTACK_PROTO2_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO4_ DLSTACK_PROTO3_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO4_ DLSTACK_PROTO3_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO5_ DLSTACK_PROTO4_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO6_ DLSTACK_PROTO5_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO7_ DLSTACK_PROTO6_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO8_ DLSTACK_PROTO7_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO9_ DLSTACK_PROTO8_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO10_ DLSTACK_PROTO9_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO11_ DLSTACK_PROTO10_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO12_ DLSTACK_PROTO11_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO13_ DLSTACK_PROTO12_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO14_ DLSTACK_PROTO13_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO14_ DLSTACK_PROTO13_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO15_ DLSTACK_PROTO14_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO16_ DLSTACK_PROTO15_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO17_ DLSTACK_PROTO16_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO18_ DLSTACK_PROTO17_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO19_ DLSTACK_PROTO18_, DLSTACK_TYPE
|
||||
#define DLSTACK_PROTO20_ DLSTACK_PROTO19_, DLSTACK_TYPE
|
||||
|
||||
/*
|
||||
* Add ",..." as the last argument.
|
||||
* This is required for variable argument functions such
|
||||
* as fprintf() on x86_64-linux.
|
||||
*
|
||||
* http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf
|
||||
* page 19:
|
||||
*
|
||||
* For calls that may call functions that use varargs or stdargs
|
||||
* (prototype-less calls or calls to functions containing ellipsis
|
||||
* (...) in the declaration) %al is used as hidden argument to
|
||||
* specify the number of SSE registers used.
|
||||
*/
|
||||
#define DLSTACK_PROTO0 void
|
||||
#define DLSTACK_PROTO1 DLSTACK_PROTO1_, ...
|
||||
#define DLSTACK_PROTO2 DLSTACK_PROTO2_, ...
|
||||
#define DLSTACK_PROTO3 DLSTACK_PROTO3_, ...
|
||||
#define DLSTACK_PROTO4 DLSTACK_PROTO4_, ...
|
||||
#define DLSTACK_PROTO4 DLSTACK_PROTO4_, ...
|
||||
#define DLSTACK_PROTO5 DLSTACK_PROTO5_, ...
|
||||
#define DLSTACK_PROTO6 DLSTACK_PROTO6_, ...
|
||||
#define DLSTACK_PROTO7 DLSTACK_PROTO7_, ...
|
||||
#define DLSTACK_PROTO8 DLSTACK_PROTO8_, ...
|
||||
#define DLSTACK_PROTO9 DLSTACK_PROTO9_, ...
|
||||
#define DLSTACK_PROTO10 DLSTACK_PROTO10_, ...
|
||||
#define DLSTACK_PROTO11 DLSTACK_PROTO11_, ...
|
||||
#define DLSTACK_PROTO12 DLSTACK_PROTO12_, ...
|
||||
#define DLSTACK_PROTO13 DLSTACK_PROTO13_, ...
|
||||
#define DLSTACK_PROTO14 DLSTACK_PROTO14_, ...
|
||||
#define DLSTACK_PROTO14 DLSTACK_PROTO14_, ...
|
||||
#define DLSTACK_PROTO15 DLSTACK_PROTO15_, ...
|
||||
#define DLSTACK_PROTO16 DLSTACK_PROTO16_, ...
|
||||
#define DLSTACK_PROTO17 DLSTACK_PROTO17_, ...
|
||||
#define DLSTACK_PROTO18 DLSTACK_PROTO18_, ...
|
||||
#define DLSTACK_PROTO19 DLSTACK_PROTO19_, ...
|
||||
#define DLSTACK_PROTO20 DLSTACK_PROTO20_, ...
|
||||
|
||||
#define DLSTACK_ARGS0(stack)
|
||||
#define DLSTACK_ARGS1(stack) (stack)[0]
|
||||
#define DLSTACK_ARGS2(stack) DLSTACK_ARGS1(stack), (stack)[1]
|
||||
#define DLSTACK_ARGS3(stack) DLSTACK_ARGS2(stack), (stack)[2]
|
||||
#define DLSTACK_ARGS4(stack) DLSTACK_ARGS3(stack), (stack)[3]
|
||||
#define DLSTACK_ARGS5(stack) DLSTACK_ARGS4(stack), (stack)[4]
|
||||
#define DLSTACK_ARGS6(stack) DLSTACK_ARGS5(stack), (stack)[5]
|
||||
#define DLSTACK_ARGS7(stack) DLSTACK_ARGS6(stack), (stack)[6]
|
||||
#define DLSTACK_ARGS8(stack) DLSTACK_ARGS7(stack), (stack)[7]
|
||||
#define DLSTACK_ARGS9(stack) DLSTACK_ARGS8(stack), (stack)[8]
|
||||
#define DLSTACK_ARGS10(stack) DLSTACK_ARGS9(stack), (stack)[9]
|
||||
#define DLSTACK_ARGS11(stack) DLSTACK_ARGS10(stack), (stack)[10]
|
||||
#define DLSTACK_ARGS12(stack) DLSTACK_ARGS11(stack), (stack)[11]
|
||||
#define DLSTACK_ARGS13(stack) DLSTACK_ARGS12(stack), (stack)[12]
|
||||
#define DLSTACK_ARGS14(stack) DLSTACK_ARGS13(stack), (stack)[13]
|
||||
#define DLSTACK_ARGS15(stack) DLSTACK_ARGS14(stack), (stack)[14]
|
||||
#define DLSTACK_ARGS16(stack) DLSTACK_ARGS15(stack), (stack)[15]
|
||||
#define DLSTACK_ARGS17(stack) DLSTACK_ARGS16(stack), (stack)[16]
|
||||
#define DLSTACK_ARGS18(stack) DLSTACK_ARGS17(stack), (stack)[17]
|
||||
#define DLSTACK_ARGS19(stack) DLSTACK_ARGS18(stack), (stack)[18]
|
||||
#define DLSTACK_ARGS20(stack) DLSTACK_ARGS19(stack), (stack)[19]
|
||||
|
||||
extern VALUE rb_mDL;
|
||||
extern VALUE rb_cDLHandle;
|
||||
extern VALUE rb_cDLSymbol;
|
||||
extern VALUE rb_eDLError;
|
||||
extern VALUE rb_eDLTypeError;
|
||||
|
||||
#define ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x)
|
||||
|
||||
#define ALIGN_VOIDP ALIGN_OF(void*)
|
||||
#define ALIGN_SHORT ALIGN_OF(short)
|
||||
#define ALIGN_CHAR ALIGN_OF(char)
|
||||
#define ALIGN_INT ALIGN_OF(int)
|
||||
#define ALIGN_LONG ALIGN_OF(long)
|
||||
#if HAVE_LONG_LONG
|
||||
#define ALIGN_LONG_LONG ALIGN_OF(LONG_LONG)
|
||||
#endif
|
||||
#define ALIGN_FLOAT ALIGN_OF(float)
|
||||
#define ALIGN_DOUBLE ALIGN_OF(double)
|
||||
|
||||
#define DLALIGN(ptr,offset,align) \
|
||||
((offset) += ((align) - ((uintptr_t)((char *)(ptr) + (offset))) % (align)) % (align))
|
||||
|
||||
|
||||
#define DLTYPE_VOID 0
|
||||
#define DLTYPE_VOIDP 1
|
||||
#define DLTYPE_CHAR 2
|
||||
#define DLTYPE_SHORT 3
|
||||
#define DLTYPE_INT 4
|
||||
#define DLTYPE_LONG 5
|
||||
#if HAVE_LONG_LONG
|
||||
#define DLTYPE_LONG_LONG 6
|
||||
#endif
|
||||
#define DLTYPE_FLOAT 7
|
||||
#define DLTYPE_DOUBLE 8
|
||||
#define MAX_DLTYPE 9
|
||||
|
||||
#if SIZEOF_VOIDP == SIZEOF_LONG
|
||||
# define PTR2NUM(x) (ULONG2NUM((unsigned long)(x)))
|
||||
# define NUM2PTR(x) ((void*)(NUM2ULONG(x)))
|
||||
#else
|
||||
/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */
|
||||
# define PTR2NUM(x) (ULL2NUM((unsigned long long)(x)))
|
||||
# define NUM2PTR(x) ((void*)(NUM2ULL(x)))
|
||||
#endif
|
||||
|
||||
#define BOOL2INT(x) (((x) == Qtrue)?1:0)
|
||||
#define INT2BOOL(x) ((x)?Qtrue:Qfalse)
|
||||
|
||||
typedef void (*freefunc_t)(void*);
|
||||
|
||||
struct dl_handle {
|
||||
void *ptr;
|
||||
int open;
|
||||
int enable_close;
|
||||
};
|
||||
|
||||
|
||||
struct cfunc_data {
|
||||
void *ptr;
|
||||
char *name;
|
||||
int type;
|
||||
ID calltype;
|
||||
VALUE wrap;
|
||||
};
|
||||
extern ID rbdl_id_cdecl;
|
||||
extern ID rbdl_id_stdcall;
|
||||
#define CFUNC_CDECL (rbdl_id_cdecl)
|
||||
#define CFUNC_STDCALL (rbdl_id_stdcall)
|
||||
|
||||
struct ptr_data {
|
||||
void *ptr;
|
||||
long size;
|
||||
freefunc_t free;
|
||||
VALUE wrap[2];
|
||||
};
|
||||
|
||||
#define RDL_HANDLE(obj) ((struct dl_handle *)(DATA_PTR(obj)))
|
||||
#define RCFUNC_DATA(obj) ((struct cfunc_data *)(DATA_PTR(obj)))
|
||||
#define RPTR_DATA(obj) ((struct ptr_data *)(DATA_PTR(obj)))
|
||||
|
||||
VALUE rb_dlcfunc_new(void (*func)(), int dltype, const char * name, ID calltype);
|
||||
int rb_dlcfunc_kind_p(VALUE func);
|
||||
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);
|
||||
|
||||
#endif
|
|
@ -1,48 +0,0 @@
|
|||
require 'mkmf'
|
||||
|
||||
if RbConfig::CONFIG['GCC'] == 'yes'
|
||||
flag = " -fno-defer-pop"
|
||||
if have_macro("__clang__")
|
||||
$LDFLAGS << flag if try_ldflags(flag)
|
||||
else
|
||||
$CFLAGS << flag
|
||||
end
|
||||
$CFLAGS << " -fno-omit-frame-pointer"
|
||||
end
|
||||
|
||||
$INSTALLFILES = [
|
||||
["dl.h", "$(HDRDIR)"],
|
||||
]
|
||||
|
||||
check = true
|
||||
if( have_header("dlfcn.h") )
|
||||
have_library("dl")
|
||||
check &&= have_func("dlopen")
|
||||
check &&= have_func("dlclose")
|
||||
check &&= have_func("dlsym")
|
||||
have_func("dlerror")
|
||||
elsif( have_header("windows.h") )
|
||||
check &&= have_func("LoadLibrary")
|
||||
check &&= have_func("FreeLibrary")
|
||||
check &&= have_func("GetProcAddress")
|
||||
else
|
||||
check = false
|
||||
end
|
||||
|
||||
if check
|
||||
config = File.read(RbConfig.expand(File.join($arch_hdrdir, "ruby/config.h")))
|
||||
types = {"SIZE_T"=>"SSIZE_T", "PTRDIFF_T"=>nil, "INTPTR_T"=>nil}
|
||||
types.each do |type, signed|
|
||||
if /^\#define\s+SIZEOF_#{type}\s+(SIZEOF_(.+)|\d+)/ =~ config
|
||||
if size = $2 and size != 'VOIDP'
|
||||
size = types.fetch(size) {size}
|
||||
$defs << format("-DDLTYPE_%s=DLTYPE_%s", signed||type, size)
|
||||
end
|
||||
if signed
|
||||
check_signedness(type.downcase, "stddef.h")
|
||||
end
|
||||
end
|
||||
end
|
||||
$defs << %[-DRUBY_VERSION=\\"#{RUBY_VERSION}\\"]
|
||||
create_makefile("dl")
|
||||
end
|
431
ext/dl/handle.c
431
ext/dl/handle.c
|
@ -1,431 +0,0 @@
|
|||
/* -*- C -*-
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <ruby.h>
|
||||
#include "dl.h"
|
||||
|
||||
VALUE rb_cDLHandle;
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef _WIN32_WCE
|
||||
static void *
|
||||
w32_coredll(void)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
|
||||
return m.AllocationBase;
|
||||
}
|
||||
# endif
|
||||
|
||||
static int
|
||||
w32_dlclose(void *ptr)
|
||||
{
|
||||
# ifndef _WIN32_WCE
|
||||
if( ptr == w32_coredll() ) return 0;
|
||||
# endif
|
||||
if( FreeLibrary((HMODULE)ptr) ) return 0;
|
||||
return errno = rb_w32_map_errno(GetLastError());
|
||||
}
|
||||
#define dlclose(ptr) w32_dlclose(ptr)
|
||||
#endif
|
||||
|
||||
static void
|
||||
dlhandle_free(void *ptr)
|
||||
{
|
||||
struct dl_handle *dlhandle = ptr;
|
||||
if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
|
||||
dlclose(dlhandle->ptr);
|
||||
}
|
||||
xfree(ptr);
|
||||
}
|
||||
|
||||
static size_t
|
||||
dlhandle_memsize(const void *ptr)
|
||||
{
|
||||
return ptr ? sizeof(struct dl_handle) : 0;
|
||||
}
|
||||
|
||||
static const rb_data_type_t dlhandle_data_type = {
|
||||
"dl/handle",
|
||||
{0, dlhandle_free, dlhandle_memsize,},
|
||||
};
|
||||
|
||||
/*
|
||||
* call-seq: close
|
||||
*
|
||||
* Close this DL::Handle. Calling close more than once will raise a
|
||||
* DL::DLError exception.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlhandle_close(VALUE self)
|
||||
{
|
||||
struct dl_handle *dlhandle;
|
||||
|
||||
TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
if(dlhandle->open) {
|
||||
int ret = dlclose(dlhandle->ptr);
|
||||
dlhandle->open = 0;
|
||||
|
||||
/* Check dlclose for successful return value */
|
||||
if(ret) {
|
||||
#if defined(HAVE_DLERROR)
|
||||
rb_raise(rb_eDLError, "%s", dlerror());
|
||||
#else
|
||||
rb_raise(rb_eDLError, "could not close handle");
|
||||
#endif
|
||||
}
|
||||
return INT2NUM(ret);
|
||||
}
|
||||
rb_raise(rb_eDLError, "dlclose() called too many times");
|
||||
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_dlhandle_s_allocate(VALUE klass)
|
||||
{
|
||||
VALUE obj;
|
||||
struct dl_handle *dlhandle;
|
||||
|
||||
obj = TypedData_Make_Struct(rb_cDLHandle, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
dlhandle->ptr = 0;
|
||||
dlhandle->open = 0;
|
||||
dlhandle->enable_close = 0;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
predefined_dlhandle(void *handle)
|
||||
{
|
||||
VALUE obj = rb_dlhandle_s_allocate(rb_cDLHandle);
|
||||
struct dl_handle *dlhandle = DATA_PTR(obj);
|
||||
|
||||
dlhandle->ptr = handle;
|
||||
dlhandle->open = 1;
|
||||
OBJ_FREEZE(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* initialize(lib = nil, flags = DL::RTLD_LAZY | DL::RTLD_GLOBAL)
|
||||
*
|
||||
* Create a new handler that opens library named +lib+ with +flags+. If no
|
||||
* library is specified, RTLD_DEFAULT is used.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlhandle_initialize(int argc, VALUE argv[], VALUE self)
|
||||
{
|
||||
void *ptr;
|
||||
struct dl_handle *dlhandle;
|
||||
VALUE lib, flag;
|
||||
char *clib;
|
||||
int cflag;
|
||||
const char *err;
|
||||
|
||||
switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
|
||||
case 0:
|
||||
clib = NULL;
|
||||
cflag = RTLD_LAZY | RTLD_GLOBAL;
|
||||
break;
|
||||
case 1:
|
||||
clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
|
||||
cflag = RTLD_LAZY | RTLD_GLOBAL;
|
||||
break;
|
||||
case 2:
|
||||
clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
|
||||
cflag = NUM2INT(flag);
|
||||
break;
|
||||
default:
|
||||
rb_bug("rb_dlhandle_new");
|
||||
}
|
||||
|
||||
rb_secure(2);
|
||||
|
||||
#if defined(_WIN32)
|
||||
if( !clib ){
|
||||
HANDLE rb_libruby_handle(void);
|
||||
ptr = rb_libruby_handle();
|
||||
}
|
||||
else if( STRCASECMP(clib, "libc") == 0
|
||||
# ifdef RUBY_COREDLL
|
||||
|| STRCASECMP(clib, RUBY_COREDLL) == 0
|
||||
|| STRCASECMP(clib, RUBY_COREDLL".dll") == 0
|
||||
# endif
|
||||
){
|
||||
# ifdef _WIN32_WCE
|
||||
ptr = dlopen("coredll.dll", cflag);
|
||||
# else
|
||||
ptr = w32_coredll();
|
||||
# endif
|
||||
}
|
||||
else
|
||||
#endif
|
||||
ptr = dlopen(clib, cflag);
|
||||
#if defined(HAVE_DLERROR)
|
||||
if( !ptr && (err = dlerror()) ){
|
||||
rb_raise(rb_eDLError, "%s", err);
|
||||
}
|
||||
#else
|
||||
if( !ptr ){
|
||||
err = dlerror();
|
||||
rb_raise(rb_eDLError, "%s", err);
|
||||
}
|
||||
#endif
|
||||
TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
|
||||
dlclose(dlhandle->ptr);
|
||||
}
|
||||
dlhandle->ptr = ptr;
|
||||
dlhandle->open = 1;
|
||||
dlhandle->enable_close = 0;
|
||||
|
||||
if( rb_block_given_p() ){
|
||||
rb_ensure(rb_yield, self, rb_dlhandle_close, self);
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: enable_close
|
||||
*
|
||||
* Enable a call to dlclose() when this DL::Handle is garbage collected.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlhandle_enable_close(VALUE self)
|
||||
{
|
||||
struct dl_handle *dlhandle;
|
||||
|
||||
TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
dlhandle->enable_close = 1;
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: disable_close
|
||||
*
|
||||
* Disable a call to dlclose() when this DL::Handle is garbage collected.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlhandle_disable_close(VALUE self)
|
||||
{
|
||||
struct dl_handle *dlhandle;
|
||||
|
||||
TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
dlhandle->enable_close = 0;
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: close_enabled?
|
||||
*
|
||||
* Returns +true+ if dlclose() will be called when this DL::Handle is
|
||||
* garbage collected.
|
||||
*/
|
||||
static VALUE
|
||||
rb_dlhandle_close_enabled_p(VALUE self)
|
||||
{
|
||||
struct dl_handle *dlhandle;
|
||||
|
||||
TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
|
||||
if(dlhandle->enable_close) return Qtrue;
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_i
|
||||
*
|
||||
* Returns the memory address for this handle.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlhandle_to_i(VALUE self)
|
||||
{
|
||||
struct dl_handle *dlhandle;
|
||||
|
||||
TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
return PTR2NUM(dlhandle);
|
||||
}
|
||||
|
||||
static VALUE dlhandle_sym(void *handle, const char *symbol);
|
||||
|
||||
/*
|
||||
* Document-method: sym
|
||||
* Document-method: []
|
||||
*
|
||||
* call-seq: sym(name)
|
||||
*
|
||||
* Get the address as an Integer for the function named +name+.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlhandle_sym(VALUE self, VALUE sym)
|
||||
{
|
||||
struct dl_handle *dlhandle;
|
||||
|
||||
TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
|
||||
if( ! dlhandle->open ){
|
||||
rb_raise(rb_eDLError, "closed handle");
|
||||
}
|
||||
|
||||
return dlhandle_sym(dlhandle->ptr, StringValueCStr(sym));
|
||||
}
|
||||
|
||||
#ifndef RTLD_NEXT
|
||||
#define RTLD_NEXT NULL
|
||||
#endif
|
||||
#ifndef RTLD_DEFAULT
|
||||
#define RTLD_DEFAULT NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Document-method: sym
|
||||
* Document-method: []
|
||||
*
|
||||
* call-seq: sym(name)
|
||||
*
|
||||
* Get the address as an Integer for the function named +name+. The function
|
||||
* is searched via dlsym on RTLD_NEXT. See man(3) dlsym() for more info.
|
||||
*/
|
||||
VALUE
|
||||
rb_dlhandle_s_sym(VALUE self, VALUE sym)
|
||||
{
|
||||
return dlhandle_sym(RTLD_NEXT, StringValueCStr(sym));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
dlhandle_sym(void *handle, const char *name)
|
||||
{
|
||||
#if defined(HAVE_DLERROR)
|
||||
const char *err;
|
||||
# define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
|
||||
#else
|
||||
# define CHECK_DLERROR
|
||||
#endif
|
||||
void (*func)();
|
||||
|
||||
rb_secure(2);
|
||||
#ifdef HAVE_DLERROR
|
||||
dlerror();
|
||||
#endif
|
||||
func = (void (*)())(VALUE)dlsym(handle, name);
|
||||
CHECK_DLERROR;
|
||||
#if defined(FUNC_STDCALL)
|
||||
if( !func ){
|
||||
int i;
|
||||
int len = (int)strlen(name);
|
||||
char *name_n;
|
||||
#if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
|
||||
{
|
||||
char *name_a = (char*)xmalloc(len+2);
|
||||
strcpy(name_a, name);
|
||||
name_n = name_a;
|
||||
name_a[len] = 'A';
|
||||
name_a[len+1] = '\0';
|
||||
func = dlsym(handle, name_a);
|
||||
CHECK_DLERROR;
|
||||
if( func ) goto found;
|
||||
name_n = xrealloc(name_a, len+6);
|
||||
}
|
||||
#else
|
||||
name_n = (char*)xmalloc(len+6);
|
||||
#endif
|
||||
memcpy(name_n, name, len);
|
||||
name_n[len++] = '@';
|
||||
for( i = 0; i < 256; i += 4 ){
|
||||
sprintf(name_n + len, "%d", i);
|
||||
func = dlsym(handle, name_n);
|
||||
CHECK_DLERROR;
|
||||
if( func ) break;
|
||||
}
|
||||
if( func ) goto found;
|
||||
name_n[len-1] = 'A';
|
||||
name_n[len++] = '@';
|
||||
for( i = 0; i < 256; i += 4 ){
|
||||
sprintf(name_n + len, "%d", i);
|
||||
func = dlsym(handle, name_n);
|
||||
CHECK_DLERROR;
|
||||
if( func ) break;
|
||||
}
|
||||
found:
|
||||
xfree(name_n);
|
||||
}
|
||||
#endif
|
||||
if( !func ){
|
||||
rb_raise(rb_eDLError, "unknown symbol \"%s\"", name);
|
||||
}
|
||||
|
||||
return PTR2NUM(func);
|
||||
}
|
||||
|
||||
void
|
||||
Init_dlhandle(void)
|
||||
{
|
||||
/*
|
||||
* Document-class: DL::Handle
|
||||
*
|
||||
* The DL::Handle is the manner to access the dynamic library
|
||||
*
|
||||
* == Example
|
||||
*
|
||||
* === Setup
|
||||
*
|
||||
* libc_so = "/lib64/libc.so.6"
|
||||
* => "/lib64/libc.so.6"
|
||||
* @handle = DL::Handle.new(libc_so)
|
||||
* => #<DL::Handle:0x00000000d69ef8>
|
||||
*
|
||||
* === Setup, with flags
|
||||
*
|
||||
* libc_so = "/lib64/libc.so.6"
|
||||
* => "/lib64/libc.so.6"
|
||||
* @handle = DL::Handle.new(libc_so, DL::RTLD_LAZY | DL::RTLD_GLOBAL)
|
||||
* => #<DL::Handle:0x00000000d69ef8>
|
||||
*
|
||||
* === Addresses to symbols
|
||||
*
|
||||
* strcpy_addr = @handle['strcpy']
|
||||
* => 140062278451968
|
||||
*
|
||||
* or
|
||||
*
|
||||
* strcpy_addr = @handle.sym('strcpy')
|
||||
* => 140062278451968
|
||||
*
|
||||
*/
|
||||
rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cObject);
|
||||
rb_define_alloc_func(rb_cDLHandle, rb_dlhandle_s_allocate);
|
||||
rb_define_singleton_method(rb_cDLHandle, "sym", rb_dlhandle_s_sym, 1);
|
||||
rb_define_singleton_method(rb_cDLHandle, "[]", rb_dlhandle_s_sym, 1);
|
||||
|
||||
/* Document-const: NEXT
|
||||
*
|
||||
* A predefined pseudo-handle of RTLD_NEXT
|
||||
*
|
||||
* Which will find the next occurrence of a function in the search order
|
||||
* after the current library.
|
||||
*/
|
||||
rb_define_const(rb_cDLHandle, "NEXT", predefined_dlhandle(RTLD_NEXT));
|
||||
|
||||
/* Document-const: DEFAULT
|
||||
*
|
||||
* A predefined pseudo-handle of RTLD_DEFAULT
|
||||
*
|
||||
* Which will find the first occurrence of the desired symbol using the
|
||||
* default library search order
|
||||
*/
|
||||
rb_define_const(rb_cDLHandle, "DEFAULT", predefined_dlhandle(RTLD_DEFAULT));
|
||||
rb_define_method(rb_cDLHandle, "initialize", rb_dlhandle_initialize, -1);
|
||||
rb_define_method(rb_cDLHandle, "to_i", rb_dlhandle_to_i, 0);
|
||||
rb_define_method(rb_cDLHandle, "close", rb_dlhandle_close, 0);
|
||||
rb_define_method(rb_cDLHandle, "sym", rb_dlhandle_sym, 1);
|
||||
rb_define_method(rb_cDLHandle, "[]", rb_dlhandle_sym, 1);
|
||||
rb_define_method(rb_cDLHandle, "disable_close", rb_dlhandle_disable_close, 0);
|
||||
rb_define_method(rb_cDLHandle, "enable_close", rb_dlhandle_enable_close, 0);
|
||||
rb_define_method(rb_cDLHandle, "close_enabled?", rb_dlhandle_close_enabled_p, 0);
|
||||
}
|
||||
|
||||
/* mode: c; tab-with=8; sw=4; ts=8; noexpandtab: */
|
|
@ -1,15 +0,0 @@
|
|||
require 'dl.so'
|
||||
|
||||
begin
|
||||
require 'fiddle' unless Object.const_defined?(:Fiddle)
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
warn "DL is deprecated, please use Fiddle"
|
||||
|
||||
module DL
|
||||
# Returns true if DL is using Fiddle, the libffi wrapper.
|
||||
def self.fiddle?
|
||||
Object.const_defined?(:Fiddle)
|
||||
end
|
||||
end
|
|
@ -1,112 +0,0 @@
|
|||
require 'dl'
|
||||
require 'thread'
|
||||
|
||||
module DL
|
||||
# The mutual exclusion (Mutex) semaphore for the DL module
|
||||
SEM = Mutex.new # :nodoc:
|
||||
|
||||
if DL.fiddle?
|
||||
# A Hash of callback Procs
|
||||
#
|
||||
# Uses Fiddle
|
||||
CdeclCallbackProcs = {} # :nodoc:
|
||||
|
||||
# A Hash of the addresses of callback Proc
|
||||
#
|
||||
# Uses Fiddle
|
||||
CdeclCallbackAddrs = {} # :nodoc:
|
||||
|
||||
# A Hash of Stdcall callback Procs
|
||||
#
|
||||
# Uses Fiddle on win32
|
||||
StdcallCallbackProcs = {} # :nodoc:
|
||||
|
||||
# A Hash of the addresses of Stdcall callback Procs
|
||||
#
|
||||
# Uses Fiddle on win32
|
||||
StdcallCallbackAddrs = {} # :nodoc:
|
||||
end
|
||||
|
||||
def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = nil, &cbp)
|
||||
if( argc < 0 )
|
||||
raise(ArgumentError, "arity should not be less than 0.")
|
||||
end
|
||||
addr = nil
|
||||
|
||||
if DL.fiddle?
|
||||
abi ||= Fiddle::Function::DEFAULT
|
||||
closure = Fiddle::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp)
|
||||
proc_entry[closure.to_i] = closure
|
||||
addr = closure.to_i
|
||||
else
|
||||
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
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
addr
|
||||
end
|
||||
|
||||
def set_cdecl_callback(ty, argc, &cbp)
|
||||
set_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, argc, ty, &cbp)
|
||||
end
|
||||
|
||||
def set_stdcall_callback(ty, argc, &cbp)
|
||||
if DL.fiddle?
|
||||
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, Fiddle::Function::STDCALL, &cbp)
|
||||
else
|
||||
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
|
||||
if DL.fiddle?
|
||||
addr = addr.to_i
|
||||
return false unless proc_entry.key?(addr)
|
||||
proc_entry.delete(addr)
|
||||
true
|
||||
else
|
||||
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
|
||||
end
|
||||
|
||||
def remove_cdecl_callback(addr, ctype = nil)
|
||||
remove_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, addr, ctype)
|
||||
end
|
||||
|
||||
def remove_stdcall_callback(addr, ctype = nil)
|
||||
remove_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, addr, ctype)
|
||||
end
|
||||
|
||||
alias set_callback set_cdecl_callback
|
||||
alias remove_callback remove_cdecl_callback
|
||||
end
|
|
@ -1,156 +0,0 @@
|
|||
module DL
|
||||
# Methods for parsing C struct and C prototype signatures.
|
||||
module CParser
|
||||
# Parses a C struct's members
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# parse_struct_signature(['int i', 'char c'])
|
||||
# => [[DL::TYPE_INT, DL::TYPE_CHAR], ["i", "c"]]
|
||||
#
|
||||
def parse_struct_signature(signature, tymap=nil)
|
||||
if( signature.is_a?(String) )
|
||||
signature = signature.split(/\s*,\s*/)
|
||||
end
|
||||
mems = []
|
||||
tys = []
|
||||
signature.each{|msig|
|
||||
tks = msig.split(/\s+(\*)?/)
|
||||
ty = tks[0..-2].join(" ")
|
||||
member = tks[-1]
|
||||
|
||||
case ty
|
||||
when /\[(\d+)\]/
|
||||
n = $1.to_i
|
||||
ty.gsub!(/\s*\[\d+\]/,"")
|
||||
ty = [ty, n]
|
||||
when /\[\]/
|
||||
ty.gsub!(/\s*\[\]/, "*")
|
||||
end
|
||||
|
||||
case member
|
||||
when /\[(\d+)\]/
|
||||
ty = [ty, $1.to_i]
|
||||
member.gsub!(/\s*\[\d+\]/,"")
|
||||
when /\[\]/
|
||||
ty = ty + "*"
|
||||
member.gsub!(/\s*\[\]/, "")
|
||||
end
|
||||
|
||||
mems.push(member)
|
||||
tys.push(parse_ctype(ty,tymap))
|
||||
}
|
||||
return tys, mems
|
||||
end
|
||||
|
||||
# Parses a C prototype signature
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# include DL::CParser
|
||||
# => Object
|
||||
#
|
||||
# parse_signature('double sum(double, double)')
|
||||
# => ["sum", DL::TYPE_DOUBLE, [DL::TYPE_DOUBLE, DL::TYPE_DOUBLE]]
|
||||
#
|
||||
def parse_signature(signature, tymap=nil)
|
||||
tymap ||= {}
|
||||
signature = signature.gsub(/\s+/, " ").strip
|
||||
case signature
|
||||
when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/
|
||||
ret = $1
|
||||
(args = $2).strip!
|
||||
ret = ret.split(/\s+/)
|
||||
args = args.split(/\s*,\s*/)
|
||||
func = ret.pop
|
||||
if( func =~ /^\*/ )
|
||||
func.gsub!(/^\*+/,"")
|
||||
ret.push("*")
|
||||
end
|
||||
ret = ret.join(" ")
|
||||
return [func, parse_ctype(ret, tymap), args.collect{|arg| parse_ctype(arg, tymap)}]
|
||||
else
|
||||
raise(RuntimeError,"can't parse the function prototype: #{signature}")
|
||||
end
|
||||
end
|
||||
|
||||
# Given a String of C type +ty+, return the corresponding DL constant.
|
||||
#
|
||||
# +ty+ can also accept an Array of C type Strings, and will returned in a
|
||||
# corresponding Array.
|
||||
#
|
||||
# If Hash +tymap+ is provided, +ty+ is expected to be the key, and the
|
||||
# value will be the C type to be looked up.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# parse_ctype('int')
|
||||
# => DL::TYPE_INT
|
||||
#
|
||||
# parse_ctype('double')
|
||||
# => DL::TYPE_DOUBLE
|
||||
#
|
||||
# parse_ctype('unsigned char')
|
||||
# => -DL::TYPE_CHAR
|
||||
#
|
||||
def parse_ctype(ty, tymap=nil)
|
||||
tymap ||= {}
|
||||
case ty
|
||||
when Array
|
||||
return [parse_ctype(ty[0], tymap), ty[1]]
|
||||
when "void"
|
||||
return TYPE_VOID
|
||||
when "char"
|
||||
return TYPE_CHAR
|
||||
when "unsigned char"
|
||||
return -TYPE_CHAR
|
||||
when "short"
|
||||
return TYPE_SHORT
|
||||
when "unsigned short"
|
||||
return -TYPE_SHORT
|
||||
when "int"
|
||||
return TYPE_INT
|
||||
when "unsigned int", 'uint'
|
||||
return -TYPE_INT
|
||||
when "long"
|
||||
return TYPE_LONG
|
||||
when "unsigned long"
|
||||
return -TYPE_LONG
|
||||
when "long long"
|
||||
if( defined?(TYPE_LONG_LONG) )
|
||||
return TYPE_LONG_LONG
|
||||
else
|
||||
raise(RuntimeError, "unsupported type: #{ty}")
|
||||
end
|
||||
when "unsigned long long"
|
||||
if( defined?(TYPE_LONG_LONG) )
|
||||
return -TYPE_LONG_LONG
|
||||
else
|
||||
raise(RuntimeError, "unsupported type: #{ty}")
|
||||
end
|
||||
when "float"
|
||||
return TYPE_FLOAT
|
||||
when "double"
|
||||
return TYPE_DOUBLE
|
||||
when "size_t"
|
||||
return TYPE_SIZE_T
|
||||
when "ssize_t"
|
||||
return TYPE_SSIZE_T
|
||||
when "ptrdiff_t"
|
||||
return TYPE_PTRDIFF_T
|
||||
when "intptr_t"
|
||||
return TYPE_INTPTR_T
|
||||
when "uintptr_t"
|
||||
return TYPE_UINTPTR_T
|
||||
when /\*/, /\[\s*\]/
|
||||
return TYPE_VOIDP
|
||||
else
|
||||
if( tymap[ty] )
|
||||
return parse_ctype(tymap[ty], tymap)
|
||||
else
|
||||
raise(DLError, "unknown type: #{ty}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,251 +0,0 @@
|
|||
require 'dl'
|
||||
require 'dl/callback'
|
||||
require 'dl/stack'
|
||||
require 'dl/value'
|
||||
require 'thread'
|
||||
|
||||
module DL
|
||||
parent = DL.fiddle? ? Fiddle::Function : Object
|
||||
|
||||
class Function < parent
|
||||
include DL
|
||||
include ValueUtil
|
||||
|
||||
if DL.fiddle?
|
||||
# :stopdoc:
|
||||
CALL_TYPE_TO_ABI = Hash.new { |h, k|
|
||||
raise RuntimeError, "unsupported call type: #{k}"
|
||||
}.merge({ :stdcall =>
|
||||
(Fiddle::Function::STDCALL rescue Fiddle::Function::DEFAULT),
|
||||
:cdecl => Fiddle::Function::DEFAULT,
|
||||
nil => Fiddle::Function::DEFAULT
|
||||
}).freeze
|
||||
private_constant :CALL_TYPE_TO_ABI
|
||||
# :startdoc:
|
||||
|
||||
def self.call_type_to_abi(call_type) # :nodoc:
|
||||
CALL_TYPE_TO_ABI[call_type]
|
||||
end
|
||||
private_class_method :call_type_to_abi
|
||||
|
||||
class FiddleClosureCFunc < Fiddle::Closure # :nodoc: all
|
||||
def initialize ctype, arg, abi, name
|
||||
@name = name
|
||||
super(ctype, arg, abi)
|
||||
end
|
||||
def name
|
||||
@name
|
||||
end
|
||||
def ptr
|
||||
to_i
|
||||
end
|
||||
end
|
||||
private_constant :FiddleClosureCFunc
|
||||
|
||||
def self.class_fiddle_closure_cfunc # :nodoc:
|
||||
FiddleClosureCFunc
|
||||
end
|
||||
private_class_method :class_fiddle_closure_cfunc
|
||||
end
|
||||
|
||||
def initialize cfunc, argtypes, abi = nil, &block
|
||||
if DL.fiddle?
|
||||
abi ||= CALL_TYPE_TO_ABI[(cfunc.calltype rescue nil)]
|
||||
if block_given?
|
||||
@cfunc = Class.new(FiddleClosureCFunc) {
|
||||
define_method(:call, block)
|
||||
}.new(cfunc.ctype, argtypes, abi, cfunc.name)
|
||||
else
|
||||
@cfunc = cfunc
|
||||
end
|
||||
|
||||
@args = argtypes
|
||||
super(@cfunc, @args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi)
|
||||
else
|
||||
@cfunc = cfunc
|
||||
@stack = Stack.new(argtypes.collect{|ty| ty.abs})
|
||||
if( @cfunc.ctype < 0 )
|
||||
@cfunc.ctype = @cfunc.ctype.abs
|
||||
@unsigned = true
|
||||
else
|
||||
@unsigned = false
|
||||
end
|
||||
if block_given?
|
||||
bind(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_i()
|
||||
@cfunc.to_i
|
||||
end
|
||||
|
||||
def name
|
||||
@cfunc.name
|
||||
end
|
||||
|
||||
def call(*args, &block)
|
||||
if DL.fiddle?
|
||||
if block_given?
|
||||
args.find { |a| DL::Function === a }.bind_at_call(&block)
|
||||
end
|
||||
super
|
||||
else
|
||||
funcs = []
|
||||
if $SAFE >= 1 && args.any? { |x| x.tainted? }
|
||||
raise SecurityError, "tainted parameter not allowed"
|
||||
end
|
||||
_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
|
||||
end
|
||||
|
||||
def wrap_result(r)
|
||||
case @cfunc.ctype
|
||||
when TYPE_VOIDP
|
||||
r = CPtr.new(r)
|
||||
else
|
||||
if( @unsigned )
|
||||
r = unsigned_value(r, @cfunc.ctype)
|
||||
end
|
||||
end
|
||||
r
|
||||
end
|
||||
|
||||
def bind(&block)
|
||||
if DL.fiddle?
|
||||
@cfunc = Class.new(FiddleClosureCFunc) {
|
||||
def initialize ctype, args, abi, name, block
|
||||
super(ctype, args, abi, name)
|
||||
@block = block
|
||||
end
|
||||
|
||||
def call *args
|
||||
@block.call(*args)
|
||||
end
|
||||
}.new(@cfunc.ctype, @args, abi, name, block)
|
||||
@ptr = @cfunc
|
||||
return nil
|
||||
else
|
||||
if( !block )
|
||||
raise(RuntimeError, "block must be given.")
|
||||
end
|
||||
unless block.lambda?
|
||||
block = Class.new(self.class){define_method(:call, block); def initialize(obj); obj.instance_variables.each{|s| instance_variable_set(s, obj.instance_variable_get(s))}; end}.new(self).method(:call)
|
||||
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
|
||||
if( @cfunc.ptr == 0 )
|
||||
raise(RuntimeException, "can't bind C function.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unbind()
|
||||
if DL.fiddle? then
|
||||
if @cfunc.kind_of?(Fiddle::Closure) and @cfunc.ptr != 0 then
|
||||
call_type = case abi
|
||||
when CALL_TYPE_TO_ABI[nil]
|
||||
nil
|
||||
when CALL_TYPE_TO_ABI[:stdcall]
|
||||
:stdcall
|
||||
else
|
||||
raise(RuntimeError, "unsupported abi: #{abi}")
|
||||
end
|
||||
@cfunc = CFunc.new(0, @cfunc.ctype, name, call_type)
|
||||
return 0
|
||||
elsif @cfunc.ptr != 0 then
|
||||
@cfunc.ptr = 0
|
||||
return 0
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if( @cfunc.ptr != 0 )
|
||||
case @cfunc.calltype
|
||||
when :cdecl
|
||||
remove_cdecl_callback(@cfunc.ptr, @cfunc.ctype)
|
||||
when :stdcall
|
||||
remove_stdcall_callback(@cfunc.ptr, @cfunc.ctype)
|
||||
else
|
||||
raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}")
|
||||
end
|
||||
@cfunc.ptr = 0
|
||||
end
|
||||
end
|
||||
|
||||
def bound?()
|
||||
@cfunc.ptr != 0
|
||||
end
|
||||
|
||||
def bind_at_call(&block)
|
||||
bind(&block)
|
||||
end
|
||||
|
||||
def unbind_at_call()
|
||||
end
|
||||
end
|
||||
|
||||
class TempFunction < Function
|
||||
def bind_at_call(&block)
|
||||
bind(&block)
|
||||
end
|
||||
|
||||
def unbind_at_call()
|
||||
unbind()
|
||||
end
|
||||
end
|
||||
|
||||
class CarriedFunction < Function
|
||||
def initialize(cfunc, argtypes, n)
|
||||
super(cfunc, argtypes)
|
||||
@carrier = []
|
||||
@index = n
|
||||
@mutex = Mutex.new
|
||||
end
|
||||
|
||||
def create_carrier(data)
|
||||
ary = []
|
||||
userdata = [ary, data]
|
||||
@mutex.lock()
|
||||
@carrier.push(userdata)
|
||||
return dlwrap(userdata)
|
||||
end
|
||||
|
||||
def bind_at_call(&block)
|
||||
userdata = @carrier[-1]
|
||||
userdata[0].push(block)
|
||||
bind{|*args|
|
||||
ptr = args[@index]
|
||||
if( !ptr )
|
||||
raise(RuntimeError, "The index of userdata should be lower than #{args.size}.")
|
||||
end
|
||||
userdata = dlunwrap(Integer(ptr))
|
||||
args[@index] = userdata[1]
|
||||
userdata[0][0].call(*args)
|
||||
}
|
||||
@mutex.unlock()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,268 +0,0 @@
|
|||
require 'dl'
|
||||
require 'dl/func.rb'
|
||||
require 'dl/struct.rb'
|
||||
require 'dl/cparser.rb'
|
||||
|
||||
module DL
|
||||
class CompositeHandler
|
||||
def initialize(handlers)
|
||||
@handlers = handlers
|
||||
end
|
||||
|
||||
def handlers()
|
||||
@handlers
|
||||
end
|
||||
|
||||
def sym(symbol)
|
||||
@handlers.each{|handle|
|
||||
if( handle )
|
||||
begin
|
||||
addr = handle.sym(symbol)
|
||||
return addr
|
||||
rescue DLError
|
||||
end
|
||||
end
|
||||
}
|
||||
return nil
|
||||
end
|
||||
|
||||
def [](symbol)
|
||||
sym(symbol)
|
||||
end
|
||||
end
|
||||
|
||||
# DL::Importer includes the means to dynamically load libraries and build
|
||||
# modules around them including calling extern functions within the C
|
||||
# library that has been loaded.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# require 'dl'
|
||||
# require 'dl/import'
|
||||
#
|
||||
# module LibSum
|
||||
# extend DL::Importer
|
||||
# dlload './libsum.so'
|
||||
# extern 'double sum(double*, int)'
|
||||
# extern 'double split(double)'
|
||||
# end
|
||||
#
|
||||
module Importer
|
||||
include DL
|
||||
include CParser
|
||||
extend Importer
|
||||
|
||||
def dlload(*libs)
|
||||
handles = libs.collect{|lib|
|
||||
case lib
|
||||
when nil
|
||||
nil
|
||||
when Handle
|
||||
lib
|
||||
when Importer
|
||||
lib.handlers
|
||||
else
|
||||
begin
|
||||
DL.dlopen(lib)
|
||||
rescue DLError
|
||||
raise(DLError, "can't load #{lib}")
|
||||
end
|
||||
end
|
||||
}.flatten()
|
||||
@handler = CompositeHandler.new(handles)
|
||||
@func_map = {}
|
||||
@type_alias = {}
|
||||
end
|
||||
|
||||
def typealias(alias_type, orig_type)
|
||||
@type_alias[alias_type] = orig_type
|
||||
end
|
||||
|
||||
def sizeof(ty)
|
||||
@type_alias ||= nil
|
||||
case ty
|
||||
when String
|
||||
ty = parse_ctype(ty, @type_alias).abs()
|
||||
case ty
|
||||
when TYPE_CHAR
|
||||
return SIZEOF_CHAR
|
||||
when TYPE_SHORT
|
||||
return SIZEOF_SHORT
|
||||
when TYPE_INT
|
||||
return SIZEOF_INT
|
||||
when TYPE_LONG
|
||||
return SIZEOF_LONG
|
||||
when TYPE_LONG_LONG
|
||||
return SIZEOF_LONG_LON
|
||||
when TYPE_FLOAT
|
||||
return SIZEOF_FLOAT
|
||||
when TYPE_DOUBLE
|
||||
return SIZEOF_DOUBLE
|
||||
when TYPE_VOIDP
|
||||
return SIZEOF_VOIDP
|
||||
else
|
||||
raise(DLError, "unknown type: #{ty}")
|
||||
end
|
||||
when Class
|
||||
if( ty.instance_methods().include?(:to_ptr) )
|
||||
return ty.size()
|
||||
end
|
||||
end
|
||||
return CPtr[ty].size()
|
||||
end
|
||||
|
||||
def parse_bind_options(opts)
|
||||
h = {}
|
||||
while( opt = opts.shift() )
|
||||
case opt
|
||||
when :stdcall, :cdecl
|
||||
h[:call_type] = opt
|
||||
when :carried, :temp, :temporal, :bind
|
||||
h[:callback_type] = opt
|
||||
h[:carrier] = opts.shift()
|
||||
else
|
||||
h[opt] = true
|
||||
end
|
||||
end
|
||||
h
|
||||
end
|
||||
private :parse_bind_options
|
||||
|
||||
def extern(signature, *opts)
|
||||
@type_alias ||= nil
|
||||
symname, ctype, argtype = parse_signature(signature, @type_alias)
|
||||
opt = parse_bind_options(opts)
|
||||
f = import_function(symname, ctype, argtype, opt[:call_type])
|
||||
name = symname.gsub(/@.+/,'')
|
||||
@func_map[name] = f
|
||||
# define_method(name){|*args,&block| f.call(*args,&block)}
|
||||
begin
|
||||
/^(.+?):(\d+)/ =~ caller.first
|
||||
file, line = $1, $2.to_i
|
||||
rescue
|
||||
file, line = __FILE__, __LINE__+3
|
||||
end
|
||||
module_eval(<<-EOS, file, line)
|
||||
def #{name}(*args, &block)
|
||||
@func_map['#{name}'].call(*args,&block)
|
||||
end
|
||||
EOS
|
||||
module_function(name)
|
||||
f
|
||||
end
|
||||
|
||||
def bind(signature, *opts, &blk)
|
||||
@type_alias ||= nil
|
||||
name, ctype, argtype = parse_signature(signature, @type_alias)
|
||||
h = parse_bind_options(opts)
|
||||
case h[:callback_type]
|
||||
when :bind, nil
|
||||
f = bind_function(name, ctype, argtype, h[:call_type], &blk)
|
||||
when :temp, :temporal
|
||||
f = create_temp_function(name, ctype, argtype, h[:call_type])
|
||||
when :carried
|
||||
f = create_carried_function(name, ctype, argtype, h[:call_type], h[:carrier])
|
||||
else
|
||||
raise(RuntimeError, "unknown callback type: #{h[:callback_type]}")
|
||||
end
|
||||
@func_map[name] = f
|
||||
#define_method(name){|*args,&block| f.call(*args,&block)}
|
||||
begin
|
||||
/^(.+?):(\d+)/ =~ caller.first
|
||||
file, line = $1, $2.to_i
|
||||
rescue
|
||||
file, line = __FILE__, __LINE__+3
|
||||
end
|
||||
module_eval(<<-EOS, file, line)
|
||||
def #{name}(*args,&block)
|
||||
@func_map['#{name}'].call(*args,&block)
|
||||
end
|
||||
EOS
|
||||
module_function(name)
|
||||
f
|
||||
end
|
||||
|
||||
# Creates a class to wrap the C struct described by +signature+.
|
||||
#
|
||||
# MyStruct = struct ['int i', 'char c']
|
||||
def struct(signature)
|
||||
@type_alias ||= nil
|
||||
tys, mems = parse_struct_signature(signature, @type_alias)
|
||||
DL::CStructBuilder.create(CStruct, tys, mems)
|
||||
end
|
||||
|
||||
# Creates a class to wrap the C union described by +signature+.
|
||||
#
|
||||
# MyUnion = union ['int i', 'char c']
|
||||
def union(signature)
|
||||
@type_alias ||= nil
|
||||
tys, mems = parse_struct_signature(signature, @type_alias)
|
||||
DL::CStructBuilder.create(CUnion, tys, mems)
|
||||
end
|
||||
|
||||
def [](name)
|
||||
@func_map[name]
|
||||
end
|
||||
|
||||
def create_value(ty, val=nil)
|
||||
s = struct([ty + " value"])
|
||||
ptr = s.malloc()
|
||||
if( val )
|
||||
ptr.value = val
|
||||
end
|
||||
return ptr
|
||||
end
|
||||
alias value create_value
|
||||
|
||||
def import_value(ty, addr)
|
||||
s = struct([ty + " value"])
|
||||
ptr = s.new(addr)
|
||||
return ptr
|
||||
end
|
||||
|
||||
def handler
|
||||
defined?(@handler) or raise "call dlload before importing symbols and functions"
|
||||
@handler
|
||||
end
|
||||
|
||||
def import_symbol(name)
|
||||
addr = handler.sym(name)
|
||||
if( !addr )
|
||||
raise(DLError, "cannot find the symbol: #{name}")
|
||||
end
|
||||
CPtr.new(addr)
|
||||
end
|
||||
|
||||
def import_function(name, ctype, argtype, call_type = nil)
|
||||
addr = handler.sym(name)
|
||||
if( !addr )
|
||||
raise(DLError, "cannot find the function: #{name}()")
|
||||
end
|
||||
Function.new(CFunc.new(addr, ctype, name, call_type || :cdecl), argtype)
|
||||
end
|
||||
|
||||
def bind_function(name, ctype, argtype, call_type = nil, &block)
|
||||
if DL.fiddle?
|
||||
klass = Function.instance_eval { class_fiddle_closure_cfunc }
|
||||
abi = Function.instance_eval { call_type_to_abi(call_type) }
|
||||
closure = Class.new(klass) {
|
||||
define_method(:call, block)
|
||||
}.new(ctype, argtype, abi, name)
|
||||
|
||||
Function.new(closure, argtype, abi)
|
||||
else
|
||||
f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
|
||||
f.bind(&block)
|
||||
f
|
||||
end
|
||||
end
|
||||
|
||||
def create_temp_function(name, ctype, argtype, call_type = nil)
|
||||
TempFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
|
||||
end
|
||||
|
||||
def create_carried_function(name, ctype, argtype, call_type = nil, n = 0)
|
||||
CarriedFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype, n)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,128 +0,0 @@
|
|||
require 'dl'
|
||||
|
||||
module DL
|
||||
module PackInfo
|
||||
ALIGN_MAP = {
|
||||
TYPE_VOIDP => ALIGN_VOIDP,
|
||||
TYPE_CHAR => ALIGN_CHAR,
|
||||
TYPE_SHORT => ALIGN_SHORT,
|
||||
TYPE_INT => ALIGN_INT,
|
||||
TYPE_LONG => ALIGN_LONG,
|
||||
TYPE_FLOAT => ALIGN_FLOAT,
|
||||
TYPE_DOUBLE => ALIGN_DOUBLE,
|
||||
-TYPE_CHAR => ALIGN_CHAR,
|
||||
-TYPE_SHORT => ALIGN_SHORT,
|
||||
-TYPE_INT => ALIGN_INT,
|
||||
-TYPE_LONG => ALIGN_LONG,
|
||||
}
|
||||
|
||||
PACK_MAP = {
|
||||
TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"),
|
||||
TYPE_CHAR => "c",
|
||||
TYPE_SHORT => "s!",
|
||||
TYPE_INT => "i!",
|
||||
TYPE_LONG => "l!",
|
||||
TYPE_FLOAT => "f",
|
||||
TYPE_DOUBLE => "d",
|
||||
-TYPE_CHAR => "c",
|
||||
-TYPE_SHORT => "s!",
|
||||
-TYPE_INT => "i!",
|
||||
-TYPE_LONG => "l!",
|
||||
}
|
||||
|
||||
SIZE_MAP = {
|
||||
TYPE_VOIDP => SIZEOF_VOIDP,
|
||||
TYPE_CHAR => SIZEOF_CHAR,
|
||||
TYPE_SHORT => SIZEOF_SHORT,
|
||||
TYPE_INT => SIZEOF_INT,
|
||||
TYPE_LONG => SIZEOF_LONG,
|
||||
TYPE_FLOAT => SIZEOF_FLOAT,
|
||||
TYPE_DOUBLE => SIZEOF_DOUBLE,
|
||||
-TYPE_CHAR => SIZEOF_CHAR,
|
||||
-TYPE_SHORT => SIZEOF_SHORT,
|
||||
-TYPE_INT => SIZEOF_INT,
|
||||
-TYPE_LONG => SIZEOF_LONG,
|
||||
}
|
||||
if defined?(TYPE_LONG_LONG)
|
||||
ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG
|
||||
PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q"
|
||||
SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG
|
||||
end
|
||||
|
||||
def align(addr, align)
|
||||
d = addr % align
|
||||
if( d == 0 )
|
||||
addr
|
||||
else
|
||||
addr + (align - d)
|
||||
end
|
||||
end
|
||||
module_function :align
|
||||
end
|
||||
|
||||
class Packer
|
||||
include PackInfo
|
||||
|
||||
def self.[](*types)
|
||||
new(types)
|
||||
end
|
||||
|
||||
def initialize(types)
|
||||
parse_types(types)
|
||||
end
|
||||
|
||||
def size()
|
||||
@size
|
||||
end
|
||||
|
||||
def pack(ary)
|
||||
case SIZEOF_VOIDP
|
||||
when SIZEOF_LONG
|
||||
ary.pack(@template)
|
||||
when SIZEOF_LONG_LONG
|
||||
ary.pack(@template)
|
||||
else
|
||||
raise(RuntimeError, "sizeof(void*)?")
|
||||
end
|
||||
end
|
||||
|
||||
def unpack(ary)
|
||||
case SIZEOF_VOIDP
|
||||
when SIZEOF_LONG
|
||||
ary.join().unpack(@template)
|
||||
when SIZEOF_LONG_LONG
|
||||
ary.join().unpack(@template)
|
||||
else
|
||||
raise(RuntimeError, "sizeof(void*)?")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_types(types)
|
||||
@template = ""
|
||||
addr = 0
|
||||
types.each{|t|
|
||||
orig_addr = addr
|
||||
if( t.is_a?(Array) )
|
||||
addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP])
|
||||
else
|
||||
addr = align(orig_addr, ALIGN_MAP[t])
|
||||
end
|
||||
d = addr - orig_addr
|
||||
if( d > 0 )
|
||||
@template << "x#{d}"
|
||||
end
|
||||
if( t.is_a?(Array) )
|
||||
@template << (PACK_MAP[t[0]] * t[1])
|
||||
addr += (SIZE_MAP[t[0]] * t[1])
|
||||
else
|
||||
@template << PACK_MAP[t]
|
||||
addr += SIZE_MAP[t]
|
||||
end
|
||||
}
|
||||
addr = align(addr, ALIGN_MAP[TYPE_VOIDP])
|
||||
@size = addr
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,116 +0,0 @@
|
|||
require 'dl'
|
||||
|
||||
module DL
|
||||
class Stack
|
||||
def self.[](*types)
|
||||
new(types)
|
||||
end
|
||||
|
||||
def initialize(types)
|
||||
parse_types(types)
|
||||
end
|
||||
|
||||
def size()
|
||||
@size
|
||||
end
|
||||
|
||||
def types()
|
||||
@types
|
||||
end
|
||||
|
||||
def pack(ary)
|
||||
case SIZEOF_VOIDP
|
||||
when SIZEOF_LONG
|
||||
ary.pack(@template).unpack('l!*')
|
||||
when SIZEOF_LONG_LONG
|
||||
ary.pack(@template).unpack('q*')
|
||||
else
|
||||
raise(RuntimeError, "sizeof(void*)?")
|
||||
end
|
||||
end
|
||||
|
||||
def unpack(ary)
|
||||
case SIZEOF_VOIDP
|
||||
when SIZEOF_LONG
|
||||
ary.pack('l!*').unpack(@template)
|
||||
when SIZEOF_LONG_LONG
|
||||
ary.pack('q*').unpack(@template)
|
||||
else
|
||||
raise(RuntimeError, "sizeof(void*)?")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def align(addr, align)
|
||||
d = addr % align
|
||||
if( d == 0 )
|
||||
addr
|
||||
else
|
||||
addr + (align - d)
|
||||
end
|
||||
end
|
||||
|
||||
ALIGN_MAP = {
|
||||
TYPE_VOIDP => ALIGN_VOIDP,
|
||||
TYPE_CHAR => ALIGN_VOIDP,
|
||||
TYPE_SHORT => ALIGN_VOIDP,
|
||||
TYPE_INT => ALIGN_VOIDP,
|
||||
TYPE_LONG => ALIGN_VOIDP,
|
||||
TYPE_FLOAT => ALIGN_FLOAT,
|
||||
TYPE_DOUBLE => ALIGN_DOUBLE,
|
||||
}
|
||||
|
||||
PACK_MAP = {
|
||||
TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG)? "q" : "l!"),
|
||||
TYPE_CHAR => "c",
|
||||
TYPE_SHORT => "s!",
|
||||
TYPE_INT => "i!",
|
||||
TYPE_LONG => "l!",
|
||||
TYPE_FLOAT => "f",
|
||||
TYPE_DOUBLE => "d",
|
||||
}
|
||||
|
||||
SIZE_MAP = {
|
||||
TYPE_VOIDP => SIZEOF_VOIDP,
|
||||
TYPE_CHAR => SIZEOF_CHAR,
|
||||
TYPE_SHORT => SIZEOF_SHORT,
|
||||
TYPE_INT => SIZEOF_INT,
|
||||
TYPE_LONG => SIZEOF_LONG,
|
||||
TYPE_FLOAT => SIZEOF_FLOAT,
|
||||
TYPE_DOUBLE => SIZEOF_DOUBLE,
|
||||
}
|
||||
if defined?(TYPE_LONG_LONG)
|
||||
ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_LONG_LONG
|
||||
PACK_MAP[TYPE_LONG_LONG] = "q"
|
||||
SIZE_MAP[TYPE_LONG_LONG] = SIZEOF_LONG_LONG
|
||||
end
|
||||
|
||||
def parse_types(types)
|
||||
@types = types
|
||||
@template = ""
|
||||
addr = 0
|
||||
types.each{|t|
|
||||
addr = add_padding(addr, ALIGN_MAP[t])
|
||||
@template << PACK_MAP[t]
|
||||
addr += SIZE_MAP[t]
|
||||
}
|
||||
addr = add_padding(addr, ALIGN_MAP[SIZEOF_VOIDP])
|
||||
if( addr % SIZEOF_VOIDP == 0 )
|
||||
@size = addr / SIZEOF_VOIDP
|
||||
else
|
||||
@size = (addr / SIZEOF_VOIDP) + 1
|
||||
end
|
||||
end
|
||||
|
||||
def add_padding(addr, align)
|
||||
orig_addr = addr
|
||||
addr = align(orig_addr, align)
|
||||
d = addr - orig_addr
|
||||
if( d > 0 )
|
||||
@template << "x#{d}"
|
||||
end
|
||||
addr
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,236 +0,0 @@
|
|||
require 'dl'
|
||||
require 'dl/value'
|
||||
require 'dl/pack.rb'
|
||||
|
||||
module DL
|
||||
# C struct shell
|
||||
class CStruct
|
||||
# accessor to DL::CStructEntity
|
||||
def CStruct.entity_class()
|
||||
CStructEntity
|
||||
end
|
||||
end
|
||||
|
||||
# C union shell
|
||||
class CUnion
|
||||
# accessor to DL::CUnionEntity
|
||||
def CUnion.entity_class()
|
||||
CUnionEntity
|
||||
end
|
||||
end
|
||||
|
||||
# Used to construct C classes (CUnion, CStruct, etc)
|
||||
#
|
||||
# DL::Importer#struct and DL::Importer#union wrap this functionality in an
|
||||
# easy-to-use manner.
|
||||
module CStructBuilder
|
||||
# Construct a new class given a C:
|
||||
# * class +klass+ (CUnion, CStruct, or other that provide an
|
||||
# #entity_class)
|
||||
# * +types+ (DL:TYPE_INT, DL::TYPE_SIZE_T, etc., see the C types
|
||||
# constants)
|
||||
# * corresponding +members+
|
||||
#
|
||||
# DL::Importer#struct and DL::Importer#union wrap this functionality in an
|
||||
# easy-to-use manner.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# require 'dl/struct'
|
||||
# require 'dl/cparser'
|
||||
#
|
||||
# include DL::CParser
|
||||
#
|
||||
# types, members = parse_struct_signature(['int i','char c'])
|
||||
#
|
||||
# MyStruct = DL::CStructBuilder.create(CUnion, types, members)
|
||||
#
|
||||
# obj = MyStruct.allocate
|
||||
#
|
||||
def create(klass, types, members)
|
||||
new_class = Class.new(klass){
|
||||
define_method(:initialize){|addr|
|
||||
@entity = klass.entity_class.new(addr, types)
|
||||
@entity.assign_names(members)
|
||||
}
|
||||
define_method(:to_ptr){ @entity }
|
||||
define_method(:to_i){ @entity.to_i }
|
||||
members.each{|name|
|
||||
define_method(name){ @entity[name] }
|
||||
define_method(name + "="){|val| @entity[name] = val }
|
||||
}
|
||||
}
|
||||
size = klass.entity_class.size(types)
|
||||
new_class.module_eval(<<-EOS, __FILE__, __LINE__+1)
|
||||
def new_class.size()
|
||||
#{size}
|
||||
end
|
||||
def new_class.malloc()
|
||||
addr = DL.malloc(#{size})
|
||||
new(addr)
|
||||
end
|
||||
EOS
|
||||
return new_class
|
||||
end
|
||||
module_function :create
|
||||
end
|
||||
|
||||
# A C struct wrapper
|
||||
class CStructEntity < (DL.fiddle? ? Fiddle::Pointer : CPtr)
|
||||
include PackInfo
|
||||
include ValueUtil
|
||||
|
||||
# Allocates a C struct the +types+ provided. The C function +func+ is
|
||||
# called when the instance is garbage collected.
|
||||
def CStructEntity.malloc(types, func = nil)
|
||||
addr = DL.malloc(CStructEntity.size(types))
|
||||
CStructEntity.new(addr, types, func)
|
||||
end
|
||||
|
||||
# Given +types+, returns the offset for the packed sizes of those types
|
||||
#
|
||||
# DL::CStructEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR,
|
||||
# DL::TYPE_VOIDP])
|
||||
# => 24
|
||||
def CStructEntity.size(types)
|
||||
offset = 0
|
||||
|
||||
max_align = types.map { |type, count = 1|
|
||||
last_offset = offset
|
||||
|
||||
align = PackInfo::ALIGN_MAP[type]
|
||||
offset = PackInfo.align(last_offset, align) +
|
||||
(PackInfo::SIZE_MAP[type] * count)
|
||||
|
||||
align
|
||||
}.max
|
||||
|
||||
PackInfo.align(offset, max_align)
|
||||
end
|
||||
|
||||
# Wraps the C pointer +addr+ as a C struct with the given +types+. The C
|
||||
# function +func+ is called when the instance is garbage collected.
|
||||
#
|
||||
# See also DL::CPtr.new
|
||||
def initialize(addr, types, func = nil)
|
||||
set_ctypes(types)
|
||||
super(addr, @size, func)
|
||||
end
|
||||
|
||||
# Set the names of the +members+ in this C struct
|
||||
def assign_names(members)
|
||||
@members = members
|
||||
end
|
||||
|
||||
# Given +types+, calculate the offsets and sizes for the types in the
|
||||
# struct.
|
||||
def set_ctypes(types)
|
||||
@ctypes = types
|
||||
@offset = []
|
||||
offset = 0
|
||||
|
||||
max_align = types.map { |type, count = 1|
|
||||
orig_offset = offset
|
||||
align = ALIGN_MAP[type]
|
||||
offset = PackInfo.align(orig_offset, align)
|
||||
|
||||
@offset << offset
|
||||
|
||||
offset += (SIZE_MAP[type] * count)
|
||||
|
||||
align
|
||||
}.max
|
||||
|
||||
@size = PackInfo.align(offset, max_align)
|
||||
end
|
||||
|
||||
# Fetch struct member +name+
|
||||
def [](name)
|
||||
idx = @members.index(name)
|
||||
if( idx.nil? )
|
||||
raise(ArgumentError, "no such member: #{name}")
|
||||
end
|
||||
ty = @ctypes[idx]
|
||||
if( ty.is_a?(Array) )
|
||||
r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
|
||||
else
|
||||
r = super(@offset[idx], SIZE_MAP[ty.abs])
|
||||
end
|
||||
packer = Packer.new([ty])
|
||||
val = packer.unpack([r])
|
||||
case ty
|
||||
when Array
|
||||
case ty[0]
|
||||
when TYPE_VOIDP
|
||||
val = val.collect{|v| CPtr.new(v)}
|
||||
end
|
||||
when TYPE_VOIDP
|
||||
val = CPtr.new(val[0])
|
||||
else
|
||||
val = val[0]
|
||||
end
|
||||
if( ty.is_a?(Integer) && (ty < 0) )
|
||||
return unsigned_value(val, ty)
|
||||
elsif( ty.is_a?(Array) && (ty[0] < 0) )
|
||||
return val.collect{|v| unsigned_value(v,ty[0])}
|
||||
else
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
# Set struct member +name+, to value +val+
|
||||
def []=(name, val)
|
||||
idx = @members.index(name)
|
||||
if( idx.nil? )
|
||||
raise(ArgumentError, "no such member: #{name}")
|
||||
end
|
||||
ty = @ctypes[idx]
|
||||
packer = Packer.new([ty])
|
||||
val = wrap_arg(val, ty, [])
|
||||
buff = packer.pack([val].flatten())
|
||||
super(@offset[idx], buff.size, buff)
|
||||
if( ty.is_a?(Integer) && (ty < 0) )
|
||||
return unsigned_value(val, ty)
|
||||
elsif( ty.is_a?(Array) && (ty[0] < 0) )
|
||||
return val.collect{|v| unsigned_value(v,ty[0])}
|
||||
else
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
def to_s() # :nodoc:
|
||||
super(@size)
|
||||
end
|
||||
end
|
||||
|
||||
# A C union wrapper
|
||||
class CUnionEntity < CStructEntity
|
||||
include PackInfo
|
||||
|
||||
# Allocates a C union the +types+ provided. The C function +func+ is
|
||||
# called when the instance is garbage collected.
|
||||
def CUnionEntity.malloc(types, func=nil)
|
||||
addr = DL.malloc(CUnionEntity.size(types))
|
||||
CUnionEntity.new(addr, types, func)
|
||||
end
|
||||
|
||||
# Given +types+, returns the size needed for the union.
|
||||
#
|
||||
# DL::CUnionEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR,
|
||||
# DL::TYPE_VOIDP])
|
||||
# => 8
|
||||
def CUnionEntity.size(types)
|
||||
types.map { |type, count = 1|
|
||||
PackInfo::SIZE_MAP[type] * count
|
||||
}.max
|
||||
end
|
||||
|
||||
# Given +types+, calculate the necessary offset and for each union member
|
||||
def set_ctypes(types)
|
||||
@ctypes = types
|
||||
@offset = Array.new(types.length, 0)
|
||||
@size = self.class.size types
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
module DL
|
||||
# Adds Windows type aliases to the including class for use with
|
||||
# DL::Importer.
|
||||
#
|
||||
# The aliases added are:
|
||||
# * ATOM
|
||||
# * BOOL
|
||||
# * BYTE
|
||||
# * DWORD
|
||||
# * DWORD32
|
||||
# * DWORD64
|
||||
# * HANDLE
|
||||
# * HDC
|
||||
# * HINSTANCE
|
||||
# * HWND
|
||||
# * LPCSTR
|
||||
# * LPSTR
|
||||
# * PBYTE
|
||||
# * PDWORD
|
||||
# * PHANDLE
|
||||
# * PVOID
|
||||
# * PWORD
|
||||
# * UCHAR
|
||||
# * UINT
|
||||
# * ULONG
|
||||
# * WORD
|
||||
module Win32Types
|
||||
def included(m) # :nodoc:
|
||||
m.module_eval{
|
||||
typealias "DWORD", "unsigned long"
|
||||
typealias "PDWORD", "unsigned long *"
|
||||
typealias "DWORD32", "unsigned long"
|
||||
typealias "DWORD64", "unsigned long long"
|
||||
typealias "WORD", "unsigned short"
|
||||
typealias "PWORD", "unsigned short *"
|
||||
typealias "BOOL", "int"
|
||||
typealias "ATOM", "int"
|
||||
typealias "BYTE", "unsigned char"
|
||||
typealias "PBYTE", "unsigned char *"
|
||||
typealias "UINT", "unsigned int"
|
||||
typealias "ULONG", "unsigned long"
|
||||
typealias "UCHAR", "unsigned char"
|
||||
typealias "HANDLE", "uintptr_t"
|
||||
typealias "PHANDLE", "void*"
|
||||
typealias "PVOID", "void*"
|
||||
typealias "LPCSTR", "char*"
|
||||
typealias "LPSTR", "char*"
|
||||
typealias "HINSTANCE", "unsigned int"
|
||||
typealias "HDC", "unsigned int"
|
||||
typealias "HWND", "unsigned int"
|
||||
}
|
||||
end
|
||||
module_function :included
|
||||
end
|
||||
|
||||
# Adds basic type aliases to the including class for use with DL::Importer.
|
||||
#
|
||||
# The aliases added are +uint+ and +u_int+ (<tt>unsigned int</tt>) and
|
||||
# +ulong+ and +u_long+ (<tt>unsigned long</tt>)
|
||||
module BasicTypes
|
||||
def included(m) # :nodoc:
|
||||
m.module_eval{
|
||||
typealias "uint", "unsigned int"
|
||||
typealias "u_int", "unsigned int"
|
||||
typealias "ulong", "unsigned long"
|
||||
typealias "u_long", "unsigned long"
|
||||
}
|
||||
end
|
||||
module_function :included
|
||||
end
|
||||
end
|
|
@ -1,114 +0,0 @@
|
|||
require 'dl'
|
||||
|
||||
module DL
|
||||
module ValueUtil
|
||||
def unsigned_value(val, ty)
|
||||
case ty.abs
|
||||
when TYPE_CHAR
|
||||
[val].pack("c").unpack("C")[0]
|
||||
when TYPE_SHORT
|
||||
[val].pack("s!").unpack("S!")[0]
|
||||
when TYPE_INT
|
||||
[val].pack("i!").unpack("I!")[0]
|
||||
when TYPE_LONG
|
||||
[val].pack("l!").unpack("L!")[0]
|
||||
when TYPE_LONG_LONG
|
||||
[val].pack("q").unpack("Q")[0]
|
||||
else
|
||||
val
|
||||
end
|
||||
end
|
||||
|
||||
def signed_value(val, ty)
|
||||
case ty.abs
|
||||
when TYPE_CHAR
|
||||
[val].pack("C").unpack("c")[0]
|
||||
when TYPE_SHORT
|
||||
[val].pack("S!").unpack("s!")[0]
|
||||
when TYPE_INT
|
||||
[val].pack("I!").unpack("i!")[0]
|
||||
when TYPE_LONG
|
||||
[val].pack("L!").unpack("l!")[0]
|
||||
when TYPE_LONG_LONG
|
||||
[val].pack("Q").unpack("q")[0]
|
||||
else
|
||||
val
|
||||
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
|
||||
end
|
||||
|
||||
def wrap_arg(arg, ty, funcs = [], &block)
|
||||
require 'dl/func'
|
||||
|
||||
funcs ||= []
|
||||
case arg
|
||||
when nil
|
||||
return 0
|
||||
when CPtr
|
||||
return arg.to_i
|
||||
when IO
|
||||
case ty
|
||||
when TYPE_VOIDP
|
||||
return CPtr[arg].to_i
|
||||
else
|
||||
return arg.to_i
|
||||
end
|
||||
when Function
|
||||
if( block )
|
||||
arg.bind_at_call(&block)
|
||||
funcs.push(arg)
|
||||
elsif !arg.bound?
|
||||
raise(RuntimeError, "block must be given.")
|
||||
end
|
||||
return arg.to_i
|
||||
when String
|
||||
if( ty.is_a?(Array) )
|
||||
return arg.unpack('C*')
|
||||
else
|
||||
case SIZEOF_VOIDP
|
||||
when SIZEOF_LONG
|
||||
return [arg].pack("p").unpack("l!")[0]
|
||||
when SIZEOF_LONG_LONG
|
||||
return [arg].pack("p").unpack("q")[0]
|
||||
else
|
||||
raise(RuntimeError, "sizeof(void*)?")
|
||||
end
|
||||
end
|
||||
when Float, Integer
|
||||
return arg
|
||||
when Array
|
||||
if( ty.is_a?(Array) ) # used only by struct
|
||||
case ty[0]
|
||||
when TYPE_VOIDP
|
||||
return arg.collect{|v| Integer(v)}
|
||||
when TYPE_CHAR
|
||||
if( arg.is_a?(String) )
|
||||
return val.unpack('C*')
|
||||
end
|
||||
end
|
||||
return arg
|
||||
else
|
||||
return arg
|
||||
end
|
||||
else
|
||||
if( arg.respond_to?(:to_ptr) )
|
||||
return arg.to_ptr.to_i
|
||||
else
|
||||
begin
|
||||
return Integer(arg)
|
||||
rescue
|
||||
raise(ArgumentError, "unknown argument type: #{arg.class}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -104,11 +104,6 @@
|
|||
#include <conversions.h>
|
||||
#include <function.h>
|
||||
|
||||
/* FIXME
|
||||
* These constants need to match up with DL. We need to refactor this to use
|
||||
* the DL header files or vice versa.
|
||||
*/
|
||||
|
||||
#define TYPE_VOID 0
|
||||
#define TYPE_VOIDP 1
|
||||
#define TYPE_CHAR 2
|
||||
|
|
|
@ -224,7 +224,7 @@ Init_fiddle_function(void)
|
|||
*
|
||||
* === ABI check
|
||||
*
|
||||
* @libc = DL.dlopen "/lib/libc.so.6"
|
||||
* @libc = Fiddle.dlopen "/lib/libc.so.6"
|
||||
* #=> #<Fiddle::Handle:0x00000001d7a8d8>
|
||||
* f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
||||
* #=> #<Fiddle::Function:0x00000001d8ee00>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
if (compiled?('dl') or compiled?('fiddle')) and $mswin||$mingw||$cygwin
|
||||
if compiled?('fiddle') and $mswin||$mingw||$cygwin
|
||||
create_makefile('win32')
|
||||
end
|
||||
|
|
|
@ -2,18 +2,24 @@
|
|||
# for backward compatibility
|
||||
warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: Win32API is deprecated after Ruby 1.9.1; use dl directly instead" if $VERBOSE
|
||||
|
||||
require 'dl'
|
||||
require 'fiddle'
|
||||
|
||||
class Win32API
|
||||
DLL = {}
|
||||
TYPEMAP = {"0" => DL::TYPE_VOID, "S" => DL::TYPE_VOIDP, "I" => DL::TYPE_LONG}
|
||||
POINTER_TYPE = DL::SIZEOF_VOIDP == DL::SIZEOF_LONG_LONG ? 'q*' : 'l!*'
|
||||
TYPEMAP = {"0" => Fiddle::TYPE_VOID, "S" => Fiddle::TYPE_VOIDP, "I" => Fiddle::TYPE_LONG}
|
||||
POINTER_TYPE = Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG_LONG ? 'q*' : 'l!*'
|
||||
|
||||
def initialize(dllname, func, import, export = "0", calltype = :stdcall)
|
||||
@proto = [import].join.tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1')
|
||||
handle = DLL[dllname] ||= DL.dlopen(dllname)
|
||||
@func = DL::CFunc.new(handle[func], TYPEMAP[export.tr("VPpNnLlIi", "0SSI")], func, calltype)
|
||||
rescue DL::DLError => e
|
||||
handle = DLL[dllname] ||= Fiddle.dlopen(dllname)
|
||||
|
||||
@func = Fiddle::Function.new(
|
||||
handle[func],
|
||||
import.map { |win_type| TYPEMAP[win_type.tr("VPpNnLlIi", "0SSI")] },
|
||||
TYPEMAP[export.tr("VPpNnLlIi", "0SSI")],
|
||||
Fiddle::Importer::CALL_TYPE_TO_ABI[calltype]
|
||||
)
|
||||
rescue Fiddle::DLError => e
|
||||
raise LoadError, e.message, e.backtrace
|
||||
end
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
begin
|
||||
require 'fiddle/import'
|
||||
importer = Fiddle::Importer
|
||||
rescue LoadError
|
||||
require 'dl/import'
|
||||
importer = DL::Importer
|
||||
end
|
||||
require 'fiddle/import'
|
||||
|
||||
module Win32
|
||||
end
|
||||
|
||||
Win32.module_eval do
|
||||
Importer = importer
|
||||
Importer = Fiddle::Importer
|
||||
end
|
||||
|
|
|
@ -59,7 +59,6 @@ module RDoc
|
|||
"rb_eZeroDivError" => "ZeroDivError",
|
||||
|
||||
"rb_mComparable" => "Comparable",
|
||||
"rb_mDL" => "DL",
|
||||
"rb_mEnumerable" => "Enumerable",
|
||||
"rb_mErrno" => "Errno",
|
||||
"rb_mFConst" => "File::Constants",
|
||||
|
|
|
@ -96,14 +96,14 @@ module SecureRandom
|
|||
crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
|
||||
@crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'VIP', 'L')
|
||||
|
||||
hProvStr = " " * DL::SIZEOF_VOIDP
|
||||
hProvStr = " " * Fiddle::SIZEOF_VOIDP
|
||||
prov_rsa_full = 1
|
||||
crypt_verifycontext = 0xF0000000
|
||||
|
||||
if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
|
||||
raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
|
||||
end
|
||||
type = DL::SIZEOF_VOIDP == DL::SIZEOF_LONG_LONG ? 'q' : 'l'
|
||||
type = Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG_LONG ? 'q' : 'l'
|
||||
@hProv, = hProvStr.unpack(type)
|
||||
|
||||
@has_win32 = true
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
# -*- coding: us-ascii -*-
|
||||
require 'test/unit'
|
||||
require_relative '../ruby/envutil'
|
||||
EnvUtil.suppress_warning {require 'dl'}
|
||||
|
||||
libc_so = libm_so = nil
|
||||
|
||||
case RUBY_PLATFORM
|
||||
when /cygwin/
|
||||
libc_so = "cygwin1.dll"
|
||||
libm_so = "cygwin1.dll"
|
||||
when /x86_64-linux/
|
||||
libc_so = "/lib64/libc.so.6"
|
||||
libm_so = "/lib64/libm.so.6"
|
||||
when /linux/
|
||||
libdir = '/lib'
|
||||
case [0].pack('L!').size
|
||||
when 4
|
||||
# 32-bit ruby
|
||||
libdir = '/lib32' if File.directory? '/lib32'
|
||||
when 8
|
||||
# 64-bit ruby
|
||||
libdir = '/lib64' if File.directory? '/lib64'
|
||||
end
|
||||
libc_so = File.join(libdir, "libc.so.6")
|
||||
libm_so = File.join(libdir, "libm.so.6")
|
||||
when /mingw/, /mswin/
|
||||
require "rbconfig"
|
||||
libc_so = libm_so = RbConfig::CONFIG["RUBY_SO_NAME"].split(/-/).find{|e| /^msvc/ =~ e} + ".dll"
|
||||
when /darwin/
|
||||
libc_so = "/usr/lib/libc.dylib"
|
||||
libm_so = "/usr/lib/libm.dylib"
|
||||
when /kfreebsd/
|
||||
libc_so = "/lib/libc.so.0.1"
|
||||
libm_so = "/lib/libm.so.1"
|
||||
when /gnu/ #GNU/Hurd
|
||||
libc_so = "/lib/libc.so.0.3"
|
||||
libm_so = "/lib/libm.so.6"
|
||||
when /mirbsd/
|
||||
libc_so = "/usr/lib/libc.so.41.10"
|
||||
libm_so = "/usr/lib/libm.so.7.0"
|
||||
when /freebsd/
|
||||
libc_so = "/lib/libc.so.7"
|
||||
libm_so = "/lib/libm.so.5"
|
||||
when /bsd|dragonfly/
|
||||
libc_so = "/usr/lib/libc.so"
|
||||
libm_so = "/usr/lib/libm.so"
|
||||
when /solaris/
|
||||
libdir = '/lib'
|
||||
case [0].pack('L!').size
|
||||
when 4
|
||||
# 32-bit ruby
|
||||
libdir = '/lib' if File.directory? '/lib'
|
||||
when 8
|
||||
# 64-bit ruby
|
||||
libdir = '/lib/64' if File.directory? '/lib/64'
|
||||
end
|
||||
libc_so = File.join(libdir, "libc.so")
|
||||
libm_so = File.join(libdir, "libm.so")
|
||||
when /aix/
|
||||
pwd=Dir.pwd
|
||||
libc_so = libm_so = "#{pwd}/libaixdltest.so"
|
||||
unless File.exist? libc_so
|
||||
cobjs=%w!strcpy.o!
|
||||
mobjs=%w!floats.o sin.o!
|
||||
funcs=%w!sin sinf strcpy strncpy!
|
||||
expfile='dltest.exp'
|
||||
require 'tmpdir'
|
||||
Dir.mktmpdir do |dir|
|
||||
begin
|
||||
Dir.chdir dir
|
||||
%x!/usr/bin/ar x /usr/lib/libc.a #{cobjs.join(' ')}!
|
||||
%x!/usr/bin/ar x /usr/lib/libm.a #{mobjs.join(' ')}!
|
||||
%x!echo "#{funcs.join("\n")}\n" > #{expfile}!
|
||||
require 'rbconfig'
|
||||
if RbConfig::CONFIG["GCC"] = 'yes'
|
||||
lflag='-Wl,'
|
||||
else
|
||||
lflag=''
|
||||
end
|
||||
flags="#{lflag}-bE:#{expfile} #{lflag}-bnoentry -lm"
|
||||
%x!#{RbConfig::CONFIG["LDSHARED"]} -o #{libc_so} #{(cobjs+mobjs).join(' ')} #{flags}!
|
||||
ensure
|
||||
Dir.chdir pwd
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
libc_so = ARGV[0] if ARGV[0] && ARGV[0][0] == ?/
|
||||
libm_so = ARGV[1] if ARGV[1] && ARGV[1][0] == ?/
|
||||
if( !(libc_so && libm_so) )
|
||||
$stderr.puts("libc and libm not found: #{$0} <libc> <libm>")
|
||||
end
|
||||
end
|
||||
|
||||
libc_so = nil if !libc_so || (libc_so[0] == ?/ && !File.file?(libc_so))
|
||||
libm_so = nil if !libm_so || (libm_so[0] == ?/ && !File.file?(libm_so))
|
||||
|
||||
if !libc_so || !libm_so
|
||||
ruby = EnvUtil.rubybin
|
||||
ldd = `ldd #{ruby}`
|
||||
#puts ldd
|
||||
libc_so = $& if !libc_so && %r{/\S*/libc\.so\S*} =~ ldd
|
||||
libm_so = $& if !libm_so && %r{/\S*/libm\.so\S*} =~ ldd
|
||||
#p [libc_so, libm_so]
|
||||
end
|
||||
|
||||
DL::LIBC_SO = libc_so
|
||||
DL::LIBM_SO = libm_so
|
||||
|
||||
module DL
|
||||
class TestBase < Test::Unit::TestCase
|
||||
include Math
|
||||
include DL
|
||||
|
||||
def setup
|
||||
@libc = dlopen(LIBC_SO)
|
||||
@libm = dlopen(LIBM_SO)
|
||||
end
|
||||
|
||||
def assert_match(expected, actual, message="")
|
||||
assert_operator(expected, :===, actual, message)
|
||||
end
|
||||
|
||||
def assert_positive(actual)
|
||||
assert_operator(actual, :>, 0)
|
||||
end
|
||||
|
||||
def assert_zero(actual)
|
||||
assert_equal(0, actual)
|
||||
end
|
||||
|
||||
def assert_negative(actual)
|
||||
assert_operator(actual, :<, 0)
|
||||
end
|
||||
|
||||
def test_empty()
|
||||
end
|
||||
|
||||
def teardown
|
||||
if /linux/ =~ RUBY_PLATFORM
|
||||
GC.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,55 +0,0 @@
|
|||
# -*- coding: us-ascii -*-
|
||||
require_relative 'test_base'
|
||||
|
||||
EnvUtil.suppress_warning {require 'dl/struct'}
|
||||
|
||||
module DL
|
||||
class TestCStructEntity < TestBase
|
||||
def test_class_size
|
||||
types = [TYPE_DOUBLE, TYPE_CHAR]
|
||||
|
||||
size = CStructEntity.size types
|
||||
|
||||
alignments = types.map { |type| PackInfo::ALIGN_MAP[type] }
|
||||
|
||||
expected = PackInfo.align 0, alignments[0]
|
||||
expected += PackInfo::SIZE_MAP[TYPE_DOUBLE]
|
||||
|
||||
expected = PackInfo.align expected, alignments[1]
|
||||
expected += PackInfo::SIZE_MAP[TYPE_CHAR]
|
||||
|
||||
expected = PackInfo.align expected, alignments.max
|
||||
|
||||
assert_equal expected, size
|
||||
end
|
||||
|
||||
def test_class_size_with_count
|
||||
size = CStructEntity.size([[TYPE_DOUBLE, 2], [TYPE_CHAR, 20]])
|
||||
|
||||
types = [TYPE_DOUBLE, TYPE_CHAR]
|
||||
alignments = types.map { |type| PackInfo::ALIGN_MAP[type] }
|
||||
|
||||
expected = PackInfo.align 0, alignments[0]
|
||||
expected += PackInfo::SIZE_MAP[TYPE_DOUBLE] * 2
|
||||
|
||||
expected = PackInfo.align expected, alignments[1]
|
||||
expected += PackInfo::SIZE_MAP[TYPE_CHAR] * 20
|
||||
|
||||
expected = PackInfo.align expected, alignments.max
|
||||
|
||||
assert_equal expected, size
|
||||
end
|
||||
|
||||
def test_set_ctypes
|
||||
union = CStructEntity.malloc [TYPE_INT, TYPE_LONG]
|
||||
union.assign_names %w[int long]
|
||||
|
||||
# this test is roundabout because the stored ctypes are not accessible
|
||||
union['long'] = 1
|
||||
union['int'] = 2
|
||||
|
||||
assert_equal 1, union['long']
|
||||
assert_equal 2, union['int']
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,31 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
|
||||
require 'dl/struct'
|
||||
|
||||
module DL
|
||||
class TestCUnionEntity < TestBase
|
||||
def test_class_size
|
||||
size = CUnionEntity.size([TYPE_DOUBLE, TYPE_CHAR])
|
||||
|
||||
assert_equal SIZEOF_DOUBLE, size
|
||||
end
|
||||
|
||||
def test_class_size_with_count
|
||||
size = CUnionEntity.size([[TYPE_DOUBLE, 2], [TYPE_CHAR, 20]])
|
||||
|
||||
assert_equal SIZEOF_CHAR * 20, size
|
||||
end
|
||||
|
||||
def test_set_ctypes
|
||||
union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG]
|
||||
union.assign_names %w[int long]
|
||||
|
||||
# this test is roundabout because the stored ctypes are not accessible
|
||||
union['long'] = 1
|
||||
assert_equal 1, union['long']
|
||||
|
||||
union['int'] = 1
|
||||
assert_equal 1, union['int']
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,72 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
require_relative '../ruby/envutil'
|
||||
require 'dl/callback'
|
||||
require 'dl/func'
|
||||
|
||||
module DL
|
||||
class TestCallback < TestBase
|
||||
include DL
|
||||
|
||||
def test_remove_callback_failed
|
||||
assert_equal(false, remove_callback(0, TYPE_VOIDP))
|
||||
end
|
||||
|
||||
def test_remove_callback
|
||||
addr = set_callback(TYPE_VOIDP, 1) do |str|
|
||||
str
|
||||
end
|
||||
assert remove_callback(addr, TYPE_VOIDP), 'callback removed'
|
||||
end
|
||||
|
||||
def test_callback_return_value
|
||||
addr = set_callback(TYPE_VOIDP, 1) do |str|
|
||||
str
|
||||
end
|
||||
func = CFunc.new(addr, TYPE_VOIDP, 'test')
|
||||
f = Function.new(func, [TYPE_VOIDP])
|
||||
ptr = CPtr['blah']
|
||||
assert_equal ptr.to_i, f.call(ptr).to_i
|
||||
end
|
||||
|
||||
def test_callback_return_arbitrary
|
||||
foo = 'foo'
|
||||
addr = set_callback(TYPE_VOIDP, 1) do |ptr|
|
||||
CPtr[foo].to_i
|
||||
end
|
||||
func = CFunc.new(addr, TYPE_VOIDP, 'test')
|
||||
f = Function.new(func, [TYPE_VOIDP])
|
||||
|
||||
ptr = CPtr['foo']
|
||||
assert_equal 'foo', f.call(ptr).to_s
|
||||
end
|
||||
|
||||
def test_callback_with_string
|
||||
called_with = nil
|
||||
addr = set_callback(TYPE_VOID, 1) do |str|
|
||||
called_with = dlunwrap(str)
|
||||
end
|
||||
func = CFunc.new(addr, TYPE_VOID, 'test')
|
||||
f = Function.new(func, [TYPE_VOIDP])
|
||||
|
||||
# Don't remove local variable arg.
|
||||
# This necessary to protect objects from GC.
|
||||
arg = 'foo'
|
||||
f.call(dlwrap(arg))
|
||||
assert_equal arg, called_with
|
||||
end
|
||||
|
||||
def test_call_callback
|
||||
called = false
|
||||
|
||||
addr = set_callback(TYPE_VOID, 1) do |foo|
|
||||
called = true
|
||||
end
|
||||
|
||||
func = CFunc.new(addr, TYPE_VOID, 'test')
|
||||
f = Function.new(func, [TYPE_VOIDP])
|
||||
f.call(nil)
|
||||
|
||||
assert called, 'function should be called'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,80 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
require 'dl/func'
|
||||
|
||||
module DL
|
||||
class TestCFunc < TestBase
|
||||
def setup
|
||||
super
|
||||
@name = 'strcpy'
|
||||
@cf = CFunc.new(@libc[@name], TYPE_VOIDP, @name)
|
||||
end
|
||||
|
||||
def test_ptr=
|
||||
@cf.ptr = @libc['malloc']
|
||||
assert_equal @cf.ptr, @libc['malloc']
|
||||
end
|
||||
|
||||
def test_ptr
|
||||
assert_equal @cf.ptr, @libc[@name]
|
||||
end
|
||||
|
||||
def test_set_calltype
|
||||
@cf.calltype = :foo
|
||||
assert_equal :foo, @cf.calltype
|
||||
end
|
||||
|
||||
def test_new_ptr_type_name
|
||||
assert_equal @name, @cf.name
|
||||
assert @cf.name.tainted?, 'name should be tainted'
|
||||
assert_equal :cdecl, @cf.calltype
|
||||
assert_equal TYPE_VOIDP, @cf.ctype
|
||||
end
|
||||
|
||||
def test_new_ptr
|
||||
cf = CFunc.new(@libc['strcpy'])
|
||||
assert_nil cf.name
|
||||
assert_equal :cdecl, cf.calltype
|
||||
assert_equal TYPE_VOID, cf.ctype
|
||||
end
|
||||
|
||||
def test_name_should_be_duped
|
||||
assert_equal @name, @cf.name
|
||||
assert @cf.name.tainted?, 'name should be tainted'
|
||||
|
||||
name = @name.dup
|
||||
@name << 'foo'
|
||||
|
||||
assert_equal name, @cf.name
|
||||
end
|
||||
|
||||
def test_to_s
|
||||
s = @cf.to_s
|
||||
assert s.tainted?, 'to_s should be tainted'
|
||||
assert_match(/ptr=#{sprintf("0x0*%x", @cf.ptr)}/, s)
|
||||
assert_match(/name='#{@cf.name}'/, s)
|
||||
assert_match(/type=#{@cf.ctype}/, s)
|
||||
end
|
||||
|
||||
def test_inspect
|
||||
assert_equal @cf.inspect, @cf.to_s
|
||||
end
|
||||
|
||||
def test_inspect_is_tainted
|
||||
assert @cf.inspect.tainted?, 'inspect is tainted'
|
||||
end
|
||||
|
||||
def test_to_i
|
||||
assert_equal @cf.to_i, @cf.ptr
|
||||
assert_equal @libc[@name], @cf.to_i
|
||||
end
|
||||
|
||||
def test_last_error
|
||||
Thread.new do
|
||||
f = Function.new(@cf, [TYPE_VOIDP, TYPE_VOIDP])
|
||||
assert_nil CFunc.last_error
|
||||
f.call("000", "123")
|
||||
assert_not_nil CFunc.last_error
|
||||
end.join
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
|
||||
require 'dl/cparser'
|
||||
|
||||
module DL
|
||||
class TestCParser < TestBase
|
||||
include CParser
|
||||
|
||||
def test_uint_ctype
|
||||
assert_equal(-TYPE_INT, parse_ctype('uint'))
|
||||
end
|
||||
|
||||
def test_size_t_ctype
|
||||
assert_equal(TYPE_SIZE_T, parse_ctype("size_t"))
|
||||
end
|
||||
|
||||
def test_ssize_t_ctype
|
||||
assert_equal(TYPE_SSIZE_T, parse_ctype("ssize_t"))
|
||||
end
|
||||
|
||||
def test_ptrdiff_t_ctype
|
||||
assert_equal(TYPE_PTRDIFF_T, parse_ctype("ptrdiff_t"))
|
||||
end
|
||||
|
||||
def test_intptr_t_ctype
|
||||
assert_equal(TYPE_INTPTR_T, parse_ctype("intptr_t"))
|
||||
end
|
||||
|
||||
def test_uintptr_t_ctype
|
||||
assert_equal(TYPE_UINTPTR_T, parse_ctype("uintptr_t"))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,226 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
require_relative '../ruby/envutil'
|
||||
|
||||
module DL
|
||||
class TestCPtr < TestBase
|
||||
def test_cptr_to_int
|
||||
null = DL::NULL
|
||||
assert_equal(null.to_i, null.to_int)
|
||||
end
|
||||
|
||||
def test_malloc_free_func_int
|
||||
free = CFunc.new(DL::RUBY_FREE, TYPE_VOID, 'free')
|
||||
|
||||
ptr = CPtr.malloc(10, free.to_i)
|
||||
assert_equal 10, ptr.size
|
||||
assert_equal free.to_i, ptr.free.to_i
|
||||
end
|
||||
|
||||
def test_malloc_free_func
|
||||
free = CFunc.new(DL::RUBY_FREE, TYPE_VOID, 'free')
|
||||
|
||||
ptr = CPtr.malloc(10, free)
|
||||
assert_equal 10, ptr.size
|
||||
assert_equal free.to_i, ptr.free.to_i
|
||||
end
|
||||
|
||||
def test_to_str
|
||||
str = "hello world"
|
||||
ptr = CPtr[str]
|
||||
|
||||
assert_equal 3, ptr.to_str(3).length
|
||||
assert_equal str, ptr.to_str
|
||||
|
||||
ptr[5] = 0
|
||||
assert_equal "hello\0world", ptr.to_str
|
||||
end
|
||||
|
||||
def test_to_s
|
||||
str = "hello world"
|
||||
ptr = CPtr[str]
|
||||
|
||||
assert_equal 3, ptr.to_s(3).length
|
||||
assert_equal str, ptr.to_s
|
||||
|
||||
ptr[5] = 0
|
||||
assert_equal 'hello', ptr.to_s
|
||||
end
|
||||
|
||||
def test_minus
|
||||
str = "hello world"
|
||||
ptr = CPtr[str]
|
||||
assert_equal ptr.to_s, (ptr + 3 - 3).to_s
|
||||
end
|
||||
|
||||
# TODO: what if the pointer size is 0? raise an exception? do we care?
|
||||
def test_plus
|
||||
str = "hello world"
|
||||
ptr = CPtr[str]
|
||||
new_str = ptr + 3
|
||||
assert_equal 'lo world', new_str.to_s
|
||||
end
|
||||
|
||||
def test_inspect
|
||||
ptr = CPtr.new(0)
|
||||
inspect = ptr.inspect
|
||||
assert_match(/size=#{ptr.size}/, inspect)
|
||||
assert_match(/free=#{sprintf("%#x", ptr.free.to_i)}/, inspect)
|
||||
assert_match(/ptr=#{sprintf("%#x", ptr.to_i)}/, inspect)
|
||||
end
|
||||
|
||||
def test_to_ptr_string
|
||||
str = "hello world"
|
||||
ptr = CPtr[str]
|
||||
assert ptr.tainted?, 'pointer should be tainted'
|
||||
assert_equal str.length, ptr.size
|
||||
assert_equal 'hello', ptr[0,5]
|
||||
end
|
||||
|
||||
def test_to_ptr_io
|
||||
buf = CPtr.malloc(10)
|
||||
File.open(__FILE__, 'r') do |f|
|
||||
ptr = CPtr.to_ptr f
|
||||
fread = CFunc.new(@libc['fread'], TYPE_VOID, 'fread')
|
||||
fread.call([buf.to_i, DL::SIZEOF_CHAR, buf.size - 1, ptr.to_i])
|
||||
end
|
||||
|
||||
File.open(__FILE__, 'r') do |f|
|
||||
assert_equal f.read(9), buf.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_to_ptr_with_ptr
|
||||
ptr = CPtr.new 0
|
||||
ptr2 = CPtr.to_ptr Struct.new(:to_ptr).new(ptr)
|
||||
assert_equal ptr, ptr2
|
||||
|
||||
assert_raises(DL::DLError) do
|
||||
CPtr.to_ptr Struct.new(:to_ptr).new(nil)
|
||||
end
|
||||
end
|
||||
|
||||
def test_to_ptr_with_num
|
||||
ptr = CPtr.new 0
|
||||
assert_equal ptr, CPtr[0]
|
||||
end
|
||||
|
||||
def test_equals
|
||||
ptr = CPtr.new 0
|
||||
ptr2 = CPtr.new 0
|
||||
assert_equal ptr2, ptr
|
||||
end
|
||||
|
||||
def test_not_equals
|
||||
ptr = CPtr.new 0
|
||||
assert_not_equal 10, ptr, '10 should not equal the pointer'
|
||||
end
|
||||
|
||||
def test_cmp
|
||||
ptr = CPtr.new 0
|
||||
assert_nil(ptr <=> 10, '10 should not be comparable')
|
||||
end
|
||||
|
||||
def test_ref_ptr
|
||||
ary = [0,1,2,4,5]
|
||||
addr = CPtr.new(dlwrap(ary))
|
||||
assert_equal addr.to_i, addr.ref.ptr.to_i
|
||||
|
||||
assert_equal addr.to_i, (+ (- addr)).to_i
|
||||
end
|
||||
|
||||
def test_to_value
|
||||
ary = [0,1,2,4,5]
|
||||
addr = CPtr.new(dlwrap(ary))
|
||||
assert_equal ary, addr.to_value
|
||||
end
|
||||
|
||||
def test_free
|
||||
ptr = CPtr.malloc(4)
|
||||
assert_nil ptr.free
|
||||
end
|
||||
|
||||
def test_free=
|
||||
assert_normal_exit(<<-"End", '[ruby-dev:39269]')
|
||||
require 'dl'
|
||||
DL::LIBC_SO = #{DL::LIBC_SO.dump}
|
||||
DL::LIBM_SO = #{DL::LIBM_SO.dump}
|
||||
include DL
|
||||
@libc = dlopen(LIBC_SO)
|
||||
@libm = dlopen(LIBM_SO)
|
||||
free = CFunc.new(DL::RUBY_FREE, TYPE_VOID, 'free')
|
||||
ptr = CPtr.malloc(4)
|
||||
ptr.free = free
|
||||
free.ptr
|
||||
ptr.free.ptr
|
||||
End
|
||||
|
||||
free = CFunc.new(DL::RUBY_FREE, TYPE_VOID, 'free')
|
||||
ptr = CPtr.malloc(4)
|
||||
ptr.free = free
|
||||
|
||||
assert_equal free.ptr, ptr.free.ptr
|
||||
end
|
||||
|
||||
def test_null?
|
||||
ptr = CPtr.new(0)
|
||||
assert ptr.null?
|
||||
end
|
||||
|
||||
def test_size
|
||||
ptr = CPtr.malloc(4)
|
||||
assert_equal 4, ptr.size
|
||||
DL.free ptr.to_i
|
||||
end
|
||||
|
||||
def test_size=
|
||||
ptr = CPtr.malloc(4)
|
||||
ptr.size = 10
|
||||
assert_equal 10, ptr.size
|
||||
DL.free ptr.to_i
|
||||
end
|
||||
|
||||
def test_aref_aset
|
||||
check = Proc.new{|str,ptr|
|
||||
assert_equal(str.size(), ptr.size())
|
||||
assert_equal(str, ptr.to_s())
|
||||
assert_equal(str[0,2], ptr.to_s(2))
|
||||
assert_equal(str[0,2], ptr[0,2])
|
||||
assert_equal(str[1,2], ptr[1,2])
|
||||
assert_equal(str[1,0], ptr[1,0])
|
||||
assert_equal(str[0].ord, ptr[0])
|
||||
assert_equal(str[1].ord, ptr[1])
|
||||
}
|
||||
str = 'abc'
|
||||
ptr = CPtr[str]
|
||||
check.call(str, ptr)
|
||||
|
||||
str[0] = "c"
|
||||
assert_equal 'c'.ord, ptr[0] = "c".ord
|
||||
check.call(str, ptr)
|
||||
|
||||
str[0,2] = "aa"
|
||||
assert_equal 'aa', ptr[0,2] = "aa"
|
||||
check.call(str, ptr)
|
||||
|
||||
ptr2 = CPtr['cdeeee']
|
||||
str[0,2] = "cd"
|
||||
assert_equal ptr2, ptr[0,2] = ptr2
|
||||
check.call(str, ptr)
|
||||
|
||||
ptr3 = CPtr['vvvv']
|
||||
str[0,2] = "vv"
|
||||
assert_equal ptr3.to_i, ptr[0,2] = ptr3.to_i
|
||||
check.call(str, ptr)
|
||||
end
|
||||
|
||||
def test_null_pointer
|
||||
nullpo = CPtr.new(0)
|
||||
assert_raise(DLError) {nullpo[0]}
|
||||
assert_raise(DLError) {nullpo[0] = 1}
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_memory_leak
|
||||
assert_no_memory_leak(%w[-W0 -rdl.so], '', '100_000.times {DL::CPtr.allocate}', rss: true)
|
||||
end
|
||||
end
|
|
@ -1,140 +0,0 @@
|
|||
require_relative 'test_base.rb'
|
||||
require 'dl/callback'
|
||||
require 'dl/func'
|
||||
require 'dl/pack'
|
||||
|
||||
module DL
|
||||
class TestDL < TestBase
|
||||
def ptr2num(*list)
|
||||
list.pack("p*").unpack(PackInfo::PACK_MAP[TYPE_VOIDP] + "*")
|
||||
end
|
||||
|
||||
# TODO: refactor test repetition
|
||||
|
||||
def test_realloc
|
||||
str = "abc"
|
||||
ptr_id = DL.realloc(0, 4)
|
||||
ptr = CPtr.new(ptr_id, 4)
|
||||
|
||||
assert_equal ptr_id, ptr.to_i
|
||||
|
||||
cfunc = CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy')
|
||||
cfunc.call([ptr_id,str].pack("l!p").unpack("l!*"))
|
||||
assert_equal("abc\0", ptr[0,4])
|
||||
DL.free ptr_id
|
||||
end
|
||||
|
||||
def test_malloc
|
||||
str = "abc"
|
||||
|
||||
ptr_id = DL.malloc(4)
|
||||
ptr = CPtr.new(ptr_id, 4)
|
||||
|
||||
assert_equal ptr_id, ptr.to_i
|
||||
|
||||
cfunc = CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy')
|
||||
cfunc.call([ptr_id,str].pack("l!p").unpack("l!*"))
|
||||
assert_equal("abc\0", ptr[0,4])
|
||||
DL.free ptr_id
|
||||
end
|
||||
|
||||
def test_call_int()
|
||||
cfunc = CFunc.new(@libc['atoi'], TYPE_INT, 'atoi')
|
||||
x = cfunc.call(["100"].pack("p").unpack("l!*"))
|
||||
assert_equal(100, x)
|
||||
|
||||
cfunc = CFunc.new(@libc['atoi'], TYPE_INT, 'atoi')
|
||||
x = cfunc.call(["-100"].pack("p").unpack("l!*"))
|
||||
assert_equal(-100, x)
|
||||
end
|
||||
|
||||
def test_call_long()
|
||||
cfunc = CFunc.new(@libc['atol'], TYPE_LONG, 'atol')
|
||||
x = cfunc.call(["100"].pack("p").unpack("l!*"))
|
||||
assert_equal(100, x)
|
||||
cfunc = CFunc.new(@libc['atol'], TYPE_LONG, 'atol')
|
||||
x = cfunc.call(["-100"].pack("p").unpack("l!*"))
|
||||
assert_equal(-100, x)
|
||||
end
|
||||
|
||||
def test_call_double()
|
||||
cfunc = CFunc.new(@libc['atof'], TYPE_DOUBLE, 'atof')
|
||||
x = cfunc.call(["0.1"].pack("p").unpack("l!*"))
|
||||
assert_in_delta(0.1, x)
|
||||
|
||||
cfunc = CFunc.new(@libc['atof'], TYPE_DOUBLE, 'atof')
|
||||
x = cfunc.call(["-0.1"].pack("p").unpack("l!*"))
|
||||
assert_in_delta(-0.1, x)
|
||||
end
|
||||
|
||||
def test_sin
|
||||
return if /x86_64/ =~ RUBY_PLATFORM
|
||||
pi_2 = Math::PI/2
|
||||
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 = Function.new(CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin'),
|
||||
[TYPE_DOUBLE])
|
||||
x = cfunc.call(-pi_2)
|
||||
assert_equal(Math.sin(-pi_2), x)
|
||||
end
|
||||
|
||||
def test_strlen()
|
||||
cfunc = CFunc.new(@libc['strlen'], TYPE_INT, 'strlen')
|
||||
x = cfunc.call(["abc"].pack("p").unpack("l!*"))
|
||||
assert_equal("abc".size, x)
|
||||
end
|
||||
|
||||
def test_strcpy()
|
||||
buff = "xxxx"
|
||||
str = "abc"
|
||||
cfunc = CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy')
|
||||
x = cfunc.call(ptr2num(buff,str))
|
||||
assert_equal("abc\0", buff)
|
||||
assert_equal("abc\0", CPtr.new(x).to_s(4))
|
||||
|
||||
buff = "xxxx"
|
||||
str = "abc"
|
||||
cfunc = CFunc.new(@libc['strncpy'], TYPE_VOIDP, 'strncpy')
|
||||
x = cfunc.call(ptr2num(buff,str) + [3])
|
||||
assert_equal("abcx", buff)
|
||||
assert_equal("abcx", CPtr.new(x).to_s(4))
|
||||
|
||||
ptr = CPtr.malloc(4)
|
||||
str = "abc"
|
||||
cfunc = CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy')
|
||||
x = cfunc.call([ptr.to_i, *ptr2num(str)])
|
||||
assert_equal("abc\0", ptr[0,4])
|
||||
assert_equal("abc\0", CPtr.new(x).to_s(4))
|
||||
end
|
||||
|
||||
def test_callback()
|
||||
buff = "foobarbaz"
|
||||
cb = set_callback(TYPE_INT,2){|x,y| CPtr.new(x)[0] <=> CPtr.new(y)[0]}
|
||||
cfunc = CFunc.new(@libc['qsort'], TYPE_VOID, 'qsort')
|
||||
cfunc.call(ptr2num(buff) + [buff.size, 1, cb])
|
||||
assert_equal('aabbfoorz', buff)
|
||||
end
|
||||
|
||||
def test_dlwrap()
|
||||
ary = [0,1,2,4,5]
|
||||
addr = dlwrap(ary)
|
||||
ary2 = dlunwrap(addr)
|
||||
assert_equal(ary, ary2)
|
||||
end
|
||||
|
||||
def test_type_size_t
|
||||
assert_equal(DL::TYPE_SSIZE_T, DL::TYPE_SIZE_T.abs)
|
||||
end
|
||||
|
||||
def test_type_uintptr_t
|
||||
assert_equal(-DL::TYPE_INTPTR_T, DL::TYPE_UINTPTR_T)
|
||||
end
|
||||
|
||||
def test_sizeof_uintptr_t
|
||||
assert_equal(DL::SIZEOF_VOIDP, DL::SIZEOF_INTPTR_T)
|
||||
end
|
||||
end
|
||||
end # module DL
|
|
@ -1,190 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
require 'dl/func'
|
||||
|
||||
module DL
|
||||
class TestFunc < TestBase
|
||||
def test_name
|
||||
f = Function.new(CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP])
|
||||
assert_equal 'strcpy', f.name
|
||||
end
|
||||
|
||||
def test_name_with_block
|
||||
begin
|
||||
cb = Function.new(CFunc.new(0, TYPE_INT, '<callback>qsort'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP]){|x,y| CPtr.new(x)[0] <=> CPtr.new(y)[0]}
|
||||
assert_equal('<callback>qsort', cb.name)
|
||||
ensure
|
||||
cb.unbind if cb # max number of callbacks is limited to MAX_CALLBACK
|
||||
end
|
||||
end
|
||||
|
||||
def test_bound
|
||||
f = Function.new(CFunc.new(0, TYPE_INT, 'test'), [TYPE_INT, TYPE_INT])
|
||||
assert_equal false, f.bound?
|
||||
begin
|
||||
f.bind { |x,y| x + y }
|
||||
assert_equal true, f.bound?
|
||||
ensure
|
||||
f.unbind # max number of callbacks is limited to MAX_CALLBACK
|
||||
end
|
||||
end
|
||||
|
||||
def test_bound_for_callback_closure
|
||||
begin
|
||||
f = Function.new(CFunc.new(0, TYPE_INT, 'test'),
|
||||
[TYPE_INT, TYPE_INT]) { |x,y| x + y }
|
||||
assert_equal true, f.bound?
|
||||
ensure
|
||||
f.unbind if f # max number of callbacks is limited to MAX_CALLBACK
|
||||
end
|
||||
end
|
||||
|
||||
def test_unbind
|
||||
f = Function.new(CFunc.new(0, TYPE_INT, 'test'), [TYPE_INT, TYPE_INT])
|
||||
begin
|
||||
f.bind { |x, y| x + y }
|
||||
assert_nothing_raised { f.unbind }
|
||||
assert_equal false, f.bound?
|
||||
# unbind() after unbind() should not raise error
|
||||
assert_nothing_raised { f.unbind }
|
||||
ensure
|
||||
f.unbind # max number of callbacks is limited to MAX_CALLBACK
|
||||
end
|
||||
end
|
||||
|
||||
def test_unbind_normal_function
|
||||
f = Function.new(CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP])
|
||||
assert_nothing_raised { f.unbind }
|
||||
assert_equal false, f.bound?
|
||||
# unbind() after unbind() should not raise error
|
||||
assert_nothing_raised { f.unbind }
|
||||
end
|
||||
|
||||
def test_bind
|
||||
f = Function.new(CFunc.new(0, TYPE_INT, 'test'), [TYPE_INT, TYPE_INT])
|
||||
begin
|
||||
assert_nothing_raised {
|
||||
f.bind { |x, y| x + y }
|
||||
}
|
||||
assert_equal 579, f.call(123, 456)
|
||||
ensure
|
||||
f.unbind # max number of callbacks is limited to MAX_CALLBACK
|
||||
end
|
||||
end
|
||||
|
||||
def test_to_i
|
||||
cfunc = CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy')
|
||||
f = Function.new(cfunc, [TYPE_VOIDP, TYPE_VOIDP])
|
||||
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
|
||||
return if /x86_64/ =~ RUBY_PLATFORM
|
||||
begin
|
||||
f = Function.new(CFunc.new(@libm['sinf'], TYPE_FLOAT, 'sinf'),
|
||||
[TYPE_FLOAT])
|
||||
rescue DL::DLError
|
||||
skip "libm may not have sinf()"
|
||||
end
|
||||
assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
|
||||
end
|
||||
|
||||
def test_sin
|
||||
return if /x86_64/ =~ RUBY_PLATFORM
|
||||
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])
|
||||
buff = "000"
|
||||
str = f.call(buff, "123")
|
||||
assert_equal("123", buff)
|
||||
assert_equal("123", str.to_s)
|
||||
end
|
||||
|
||||
def test_string()
|
||||
stress, GC.stress = GC.stress, true
|
||||
f = Function.new(CFunc.new(@libc['strcpy'], TYPE_VOIDP, 'strcpy'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP])
|
||||
buff = "000"
|
||||
str = f.call(buff, "123")
|
||||
assert_equal("123", buff)
|
||||
assert_equal("123", str.to_s)
|
||||
ensure
|
||||
GC.stress = stress
|
||||
end
|
||||
|
||||
def test_isdigit()
|
||||
f = Function.new(CFunc.new(@libc['isdigit'], TYPE_INT, 'isdigit'),
|
||||
[TYPE_INT])
|
||||
r1 = f.call(?1.ord)
|
||||
r2 = f.call(?2.ord)
|
||||
rr = f.call(?r.ord)
|
||||
assert_positive(r1)
|
||||
assert_positive(r2)
|
||||
assert_zero(rr)
|
||||
end
|
||||
|
||||
def test_atof()
|
||||
f = Function.new(CFunc.new(@libc['atof'], TYPE_DOUBLE, 'atof'),
|
||||
[TYPE_VOIDP])
|
||||
r = f.call("12.34")
|
||||
assert_match(12.00..13.00, r)
|
||||
end
|
||||
|
||||
def test_strtod()
|
||||
f = Function.new(CFunc.new(@libc['strtod'], TYPE_DOUBLE, 'strtod'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP])
|
||||
buff1 = CPtr["12.34"]
|
||||
buff2 = buff1 + 4
|
||||
r = f.call(buff1, - buff2)
|
||||
assert_in_delta(12.34, r, 0.001)
|
||||
end
|
||||
|
||||
def test_qsort1()
|
||||
assert_separately(%W[--disable=gems -r#{__dir__}/test_base.rb -rdl/func], __FILE__, __LINE__, <<-"end;")
|
||||
include DL
|
||||
@libc = dlopen(LIBC_SO)
|
||||
@libm = dlopen(LIBM_SO)
|
||||
begin
|
||||
cb = Function.new(CFunc.new(0, TYPE_INT, '<callback>qsort'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP]){|x,y| CPtr.new(x)[0] <=> CPtr.new(y)[0]}
|
||||
qsort = Function.new(CFunc.new(@libc['qsort'], TYPE_VOID, 'qsort'),
|
||||
[TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP])
|
||||
buff = "9341"
|
||||
|
||||
qsort.call(buff, buff.size, 1, cb)
|
||||
assert_equal("1349", buff)
|
||||
|
||||
bug4929 = '[ruby-core:37395]'
|
||||
buff = "9341"
|
||||
EnvUtil.under_gc_stress {qsort.call(buff, buff.size, 1, cb)}
|
||||
assert_equal("1349", buff, bug4929)
|
||||
ensure
|
||||
cb.unbind if cb # max number of callbacks is limited to MAX_CALLBACK
|
||||
end
|
||||
end;
|
||||
end
|
||||
|
||||
def test_qsort2()
|
||||
cb = TempFunction.new(CFunc.new(0, TYPE_INT, '<callback>qsort'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP])
|
||||
qsort = Function.new(CFunc.new(@libc['qsort'], TYPE_VOID, 'qsort'),
|
||||
[TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP])
|
||||
buff = "9341"
|
||||
qsort.call(buff, buff.size, 1, cb){|x,y| CPtr.new(x)[0] <=> CPtr.new(y)[0]}
|
||||
assert_equal("1349", buff)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,191 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
require_relative '../ruby/envutil'
|
||||
|
||||
module DL
|
||||
class TestHandle < TestBase
|
||||
def test_to_i
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
assert_kind_of Integer, handle.to_i
|
||||
end
|
||||
|
||||
def test_static_sym_secure
|
||||
assert_raises(SecurityError) do
|
||||
Thread.new do
|
||||
$SAFE = 2
|
||||
DL::Handle.sym('calloc')
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_static_sym_unknown
|
||||
assert_raises(DL::DLError) { DL::Handle.sym('fooo') }
|
||||
assert_raises(DL::DLError) { DL::Handle['fooo'] }
|
||||
end
|
||||
|
||||
def test_static_sym
|
||||
skip "DL::Handle.sym is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
begin
|
||||
# Linux / Darwin / FreeBSD
|
||||
assert_not_nil DL::Handle.sym('dlopen')
|
||||
assert_equal DL::Handle.sym('dlopen'), DL::Handle['dlopen']
|
||||
rescue
|
||||
# NetBSD
|
||||
require 'objspace'
|
||||
assert_not_nil DL::Handle.sym('Init_objspace')
|
||||
assert_equal DL::Handle.sym('Init_objspace'), DL::Handle['Init_objspace']
|
||||
end
|
||||
end
|
||||
|
||||
def test_sym_closed_handle
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
handle.close
|
||||
assert_raises(DL::DLError) { handle.sym("calloc") }
|
||||
assert_raises(DL::DLError) { handle["calloc"] }
|
||||
end
|
||||
|
||||
def test_sym_unknown
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
assert_raises(DL::DLError) { handle.sym('fooo') }
|
||||
assert_raises(DL::DLError) { handle['fooo'] }
|
||||
end
|
||||
|
||||
def test_sym_with_bad_args
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
assert_raises(TypeError) { handle.sym(nil) }
|
||||
assert_raises(TypeError) { handle[nil] }
|
||||
end
|
||||
|
||||
def test_sym_secure
|
||||
assert_raises(SecurityError) do
|
||||
Thread.new do
|
||||
$SAFE = 2
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
handle.sym('calloc')
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_sym
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
assert_not_nil handle.sym('calloc')
|
||||
assert_not_nil handle['calloc']
|
||||
end
|
||||
|
||||
def test_handle_close
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
assert_equal 0, handle.close
|
||||
end
|
||||
|
||||
def test_handle_close_twice
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
handle.close
|
||||
assert_raises(DL::DLError) do
|
||||
handle.close
|
||||
end
|
||||
end
|
||||
|
||||
def test_dlopen_returns_handle
|
||||
assert_instance_of DL::Handle, dlopen(LIBC_SO)
|
||||
end
|
||||
|
||||
def test_dlopen_safe
|
||||
assert_raises(SecurityError) do
|
||||
Thread.new do
|
||||
$SAFE = 2
|
||||
dlopen(LIBC_SO)
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_initialize_safe
|
||||
assert_raises(SecurityError) do
|
||||
Thread.new do
|
||||
$SAFE = 2
|
||||
DL::Handle.new(LIBC_SO)
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_initialize_noargs
|
||||
handle = DL::Handle.new
|
||||
assert_not_nil handle['rb_str_new']
|
||||
end
|
||||
|
||||
def test_initialize_flags
|
||||
handle = DL::Handle.new(LIBC_SO, DL::RTLD_LAZY | DL::RTLD_GLOBAL)
|
||||
assert_not_nil handle['calloc']
|
||||
end
|
||||
|
||||
def test_enable_close
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
assert !handle.close_enabled?, 'close is enabled'
|
||||
|
||||
handle.enable_close
|
||||
assert handle.close_enabled?, 'close is not enabled'
|
||||
end
|
||||
|
||||
def test_disable_close
|
||||
handle = DL::Handle.new(LIBC_SO)
|
||||
|
||||
handle.enable_close
|
||||
assert handle.close_enabled?, 'close is enabled'
|
||||
handle.disable_close
|
||||
assert !handle.close_enabled?, 'close is enabled'
|
||||
end
|
||||
|
||||
def test_NEXT
|
||||
begin
|
||||
# Linux / Darwin
|
||||
#
|
||||
# There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find
|
||||
# the first occurrence of the desired symbol using the default library search order. The
|
||||
# latter will find the next occurrence of a function in the search order after the current
|
||||
# library. This allows one to provide a wrapper around a function in another shared
|
||||
# library.
|
||||
# --- Ubuntu Linux 8.04 dlsym(3)
|
||||
handle = DL::Handle::NEXT
|
||||
assert_not_nil handle['malloc']
|
||||
rescue
|
||||
# BSD
|
||||
#
|
||||
# If dlsym() is called with the special handle RTLD_NEXT, then the search
|
||||
# for the symbol is limited to the shared objects which were loaded after
|
||||
# the one issuing the call to dlsym(). Thus, if the function is called
|
||||
# from the main program, all the shared libraries are searched. If it is
|
||||
# called from a shared library, all subsequent shared libraries are
|
||||
# searched. RTLD_NEXT is useful for implementing wrappers around library
|
||||
# functions. For example, a wrapper function getpid() could access the
|
||||
# "real" getpid() with dlsym(RTLD_NEXT, "getpid"). (Actually, the dlfunc()
|
||||
# interface, below, should be used, since getpid() is a function and not a
|
||||
# data object.)
|
||||
# --- FreeBSD 8.0 dlsym(3)
|
||||
assert_in_out_err(['RUBYOPT' => '-W0'], <<-INPUT, /\A#<DL::Handle:0x[0-9a-f]+>\z/)
|
||||
require 'dl'
|
||||
require 'objspace'
|
||||
print DL::Handle::NEXT.inspect
|
||||
INPUT
|
||||
end
|
||||
end unless /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
|
||||
def test_DEFAULT
|
||||
skip "DL::Handle::DEFAULT is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
handle = DL::Handle::DEFAULT
|
||||
assert_not_nil handle['malloc']
|
||||
end unless /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
|
||||
def test_dlerror
|
||||
# FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls
|
||||
# getaddrinfo(3). And nsdispatch(3) doesn't call dlerror(3) even if
|
||||
# it calls _nss_cache_cycle_prevention_function with dlsym(3).
|
||||
# So our DL::Handle#sym must call dlerror(3) before call dlsym.
|
||||
# In general uses of dlerror(3) should call it before use it.
|
||||
require 'socket'
|
||||
Socket.gethostbyname("localhost")
|
||||
DL.dlopen("/lib/libc.so.7").sym('strcpy')
|
||||
end if /freebsd/=~ RUBY_PLATFORM
|
||||
end
|
||||
|
||||
def test_no_memory_leak
|
||||
assert_no_memory_leak(%w[-W0 -rdl.so], '', '100_000.times {DL::Handle.allocate}; GC.start', rss: true)
|
||||
end
|
||||
end
|
|
@ -1,165 +0,0 @@
|
|||
# coding: US-ASCII
|
||||
require_relative 'test_base'
|
||||
require 'dl/import'
|
||||
|
||||
module DL
|
||||
module LIBC
|
||||
extend Importer
|
||||
dlload LIBC_SO, LIBM_SO
|
||||
|
||||
typealias 'string', 'char*'
|
||||
typealias 'FILE*', 'void*'
|
||||
|
||||
extern "void *strcpy(char*, char*)"
|
||||
extern "int isdigit(int)"
|
||||
extern "double atof(string)"
|
||||
extern "unsigned long strtoul(char*, char **, int)"
|
||||
extern "int qsort(void*, unsigned long, unsigned long, void*)"
|
||||
extern "int fprintf(FILE*, char*)"
|
||||
extern "int gettimeofday(timeval*, timezone*)" rescue nil
|
||||
|
||||
QsortCallback = bind("void *qsort_callback(void*, void*)", :temp)
|
||||
BoundQsortCallback = bind("void *bound_qsort_callback(void*, void*)"){|ptr1,ptr2| ptr1[0] <=> ptr2[0]}
|
||||
Timeval = struct [
|
||||
"long tv_sec",
|
||||
"long tv_usec",
|
||||
]
|
||||
Timezone = struct [
|
||||
"int tz_minuteswest",
|
||||
"int tz_dsttime",
|
||||
]
|
||||
MyStruct = struct [
|
||||
"short num[5]",
|
||||
"char c",
|
||||
"unsigned char buff[7]",
|
||||
]
|
||||
|
||||
CallCallback = bind("void call_callback(void*, void*)"){|ptr1, ptr2|
|
||||
f = Function.new(CFunc.new(ptr1.to_i, TYPE_VOID, "<anonymous>"), [TYPE_VOIDP])
|
||||
f.call(ptr2)
|
||||
}
|
||||
CarriedFunction = bind("void callback_function(void*)", :carried, 0)
|
||||
end
|
||||
|
||||
class TestImport < TestBase
|
||||
def test_ensure_call_dlload
|
||||
err = assert_raises(RuntimeError) do
|
||||
Class.new do
|
||||
extend Importer
|
||||
extern "void *strcpy(char*, char*)"
|
||||
end
|
||||
end
|
||||
assert_match(/call dlload before/, err.message)
|
||||
end
|
||||
|
||||
def test_malloc()
|
||||
s1 = LIBC::Timeval.malloc()
|
||||
s2 = LIBC::Timeval.malloc()
|
||||
assert_not_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
|
||||
end
|
||||
|
||||
def test_sizeof()
|
||||
assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
|
||||
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
|
||||
end
|
||||
|
||||
def test_unsigned_result()
|
||||
d = (2 ** 31) + 1
|
||||
|
||||
r = LIBC.strtoul(d.to_s, 0, 0)
|
||||
assert_equal(d, r)
|
||||
end
|
||||
|
||||
def test_io()
|
||||
if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM )
|
||||
return
|
||||
end
|
||||
io_in,io_out = IO.pipe()
|
||||
LIBC.fprintf(io_out, "hello")
|
||||
io_out.flush()
|
||||
io_out.close()
|
||||
str = io_in.read()
|
||||
io_in.close()
|
||||
assert_equal("hello", str)
|
||||
end
|
||||
|
||||
def test_value()
|
||||
i = LIBC.value('int', 2)
|
||||
assert_equal(2, i.value)
|
||||
|
||||
d = LIBC.value('double', 2.0)
|
||||
assert_equal(2.0, d.value)
|
||||
|
||||
ary = LIBC.value('int[3]', [0,1,2])
|
||||
assert_equal([0,1,2], ary.value)
|
||||
end
|
||||
|
||||
def test_carried_function()
|
||||
data1 = "data"
|
||||
data2 = nil
|
||||
LIBC.call_callback(LIBC::CarriedFunction, LIBC::CarriedFunction.create_carrier(data1)){|d|
|
||||
data2 = d
|
||||
}
|
||||
assert_equal(data1, data2)
|
||||
end
|
||||
|
||||
def test_struct()
|
||||
s = LIBC::MyStruct.malloc()
|
||||
s.num = [0,1,2,3,4]
|
||||
s.c = ?a.ord
|
||||
s.buff = "012345\377"
|
||||
assert_equal([0,1,2,3,4], s.num)
|
||||
assert_equal(?a.ord, s.c)
|
||||
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
|
||||
end
|
||||
|
||||
def test_gettimeofday()
|
||||
if( defined?(LIBC.gettimeofday) )
|
||||
timeval = LIBC::Timeval.malloc()
|
||||
timezone = LIBC::Timezone.malloc()
|
||||
LIBC.gettimeofday(timeval, timezone)
|
||||
cur = Time.now()
|
||||
assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
|
||||
end
|
||||
end
|
||||
|
||||
def test_strcpy()
|
||||
buff = "000"
|
||||
str = LIBC.strcpy(buff, "123")
|
||||
assert_equal("123", buff)
|
||||
assert_equal("123", str.to_s)
|
||||
end
|
||||
|
||||
def test_isdigit()
|
||||
r1 = LIBC.isdigit(?1.ord)
|
||||
r2 = LIBC.isdigit(?2.ord)
|
||||
rr = LIBC.isdigit(?r.ord)
|
||||
assert_positive(r1)
|
||||
assert_positive(r2)
|
||||
assert_zero(rr)
|
||||
end
|
||||
|
||||
def test_atof()
|
||||
r = LIBC.atof("12.34")
|
||||
assert_match(12.00..13.00, r)
|
||||
end
|
||||
|
||||
def test_strtod()
|
||||
f = Function.new(CFunc.new(@libc['strtod'], TYPE_DOUBLE, 'strtod'),
|
||||
[TYPE_VOIDP, TYPE_VOIDP])
|
||||
buff1 = "12.34"
|
||||
buff2 = " "
|
||||
r = f.call(buff1, buff2)
|
||||
assert_match(12.00..13.00, r)
|
||||
end
|
||||
|
||||
def test_qsort()
|
||||
buff = "9341"
|
||||
LIBC.qsort(buff, buff.size, 1, LIBC::QsortCallback){|ptr1,ptr2| ptr1[0] <=> ptr2[0]}
|
||||
assert_equal("1349", buff)
|
||||
buff = "9341"
|
||||
LIBC.qsort(buff, buff.size, 1, LIBC::BoundQsortCallback)
|
||||
assert_equal("1349", buff)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,54 +0,0 @@
|
|||
require_relative 'test_base'
|
||||
require 'dl/import'
|
||||
require 'dl/types'
|
||||
|
||||
class DL::TestWin32 < DL::TestBase
|
||||
module Win32API
|
||||
extend DL::Importer
|
||||
|
||||
dlload "kernel32.dll"
|
||||
|
||||
include DL::Win32Types
|
||||
|
||||
OSVERSIONINFO = struct [
|
||||
"DWORD dwOSVersionInfoSize",
|
||||
"DWORD dwMajorVersion",
|
||||
"DWORD dwMinorVersion",
|
||||
"DWORD dwBuildNumber",
|
||||
"DWORD dwPlatformId",
|
||||
"UCHAR szCSDVersion[128]",
|
||||
]
|
||||
|
||||
typealias "POSVERSIONINFO", "OSVERSIONINFO*"
|
||||
|
||||
extern "BOOL GetVersionEx(POSVERSIONINFO)", :stdcall
|
||||
|
||||
def get_version_ex()
|
||||
ptr = OSVERSIONINFO.malloc()
|
||||
ptr.dwOSVersionInfoSize = OSVERSIONINFO.size
|
||||
ret = GetVersionEx(ptr)
|
||||
if( ret )
|
||||
ptr
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
module_function :get_version_ex
|
||||
rescue DL::DLError
|
||||
end
|
||||
|
||||
if defined?(Win32API::OSVERSIONINFO)
|
||||
def test_version()
|
||||
platform = Win32API.get_version_ex().dwPlatformId
|
||||
case ENV['OS']
|
||||
when 'Windows_NT'
|
||||
expect = 2
|
||||
when /Windows.+/
|
||||
expect = 1
|
||||
else
|
||||
expect = 0
|
||||
end
|
||||
assert_equal(expect, platform)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,36 +15,13 @@ module Memory
|
|||
read_status {|k, v| keys << k; vals << v}
|
||||
|
||||
when /mswin|mingw/ =~ RUBY_PLATFORM
|
||||
begin
|
||||
require 'fiddle/import'
|
||||
rescue LoadError
|
||||
$LOAD_PATH.unshift File.join(File.join(__dir__, '..'), 'lib')
|
||||
require_relative 'envutil'
|
||||
EnvUtil.suppress_warning do
|
||||
require 'dl/import'
|
||||
end
|
||||
end
|
||||
begin
|
||||
require 'fiddle/types'
|
||||
rescue LoadError
|
||||
require_relative 'envutil'
|
||||
EnvUtil.suppress_warning do
|
||||
require 'dl/types'
|
||||
end
|
||||
end
|
||||
require 'fiddle/import'
|
||||
require 'fiddle/types'
|
||||
|
||||
module Win32
|
||||
begin
|
||||
extend Fiddle::Importer
|
||||
rescue NameError
|
||||
extend DL::Importer
|
||||
end
|
||||
extend Fiddle::Importer
|
||||
dlload "kernel32.dll", "psapi.dll"
|
||||
begin
|
||||
include Fiddle::Win32Types
|
||||
rescue NameError
|
||||
include DL::Win32Types
|
||||
end
|
||||
include Fiddle::Win32Types
|
||||
typealias "SIZE_T", "size_t"
|
||||
|
||||
PROCESS_MEMORY_COUNTERS = struct [
|
||||
|
|
Loading…
Add table
Reference in a new issue