1
0
Fork 0
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:
tenderlove 2014-10-31 21:13:09 +00:00
parent 88326272bc
commit 07308c4d30
48 changed files with 33 additions and 5810 deletions

View file

@ -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
View file

@ -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

View file

@ -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

View file

@ -8,7 +8,6 @@
#digest/rmd160
#digest/sha1
#digest/sha2
#dl
#etc
#fcntl
#gdbm

View file

@ -8,7 +8,6 @@ digest/md5
digest/rmd160
digest/sha1
digest/sha2
dl
enumerator
etc
fcntl

View file

@ -10,7 +10,6 @@ digest/md5
digest/rmd160
digest/sha1
digest/sha2
#dl
enumerator
etc
fcntl

View file

@ -12,9 +12,6 @@
# digest/rmd160
# digest/sha1
# digest/sha2
# dl
# dl/callback
# #dl/win32
# etc
# fcntl
# fiber

View file

@ -9,7 +9,6 @@ digest/md5
digest/rmd160
digest/sha1
digest/sha2
dl
enumerator
etc
fcntl

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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

View file

@ -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: */

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 [