From bda37095ca990568069aaf07dc6b3fa8ceebc327 Mon Sep 17 00:00:00 2001 From: ttate Date: Fri, 4 Feb 2005 13:35:37 +0000 Subject: added new files. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@7883 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/dl/cfunc.c | 512 +++++++++++++++++++++++++++++++++++++++++++++ ext/dl/cptr.c | 462 ++++++++++++++++++++++++++++++++++++++++ ext/dl/depend | 13 ++ ext/dl/dl.c | 138 ++++++++++++ ext/dl/dl.h | 183 ++++++++++++++++ ext/dl/extconf.rb | 28 +++ ext/dl/handle.c | 200 ++++++++++++++++++ ext/dl/lib/dl/import.rb | 182 ++++++++++++++++ ext/dl/lib/dl/struct.rb | 203 ++++++++++++++++++ ext/dl/lib/dl/types.rb | 40 ++++ ext/dl/mkcallback.rb | 190 +++++++++++++++++ ext/dl/test/test_all.rb | 11 + ext/dl/test/test_base.rb | 52 +++++ ext/dl/test/test_dl2.rb | 89 ++++++++ ext/dl/test/test_func.rb | 62 ++++++ ext/dl/test/test_import.rb | 137 ++++++++++++ ext/dl/test/test_win32.rb | 53 +++++ 17 files changed, 2555 insertions(+) create mode 100644 ext/dl/cfunc.c create mode 100644 ext/dl/cptr.c create mode 100644 ext/dl/depend create mode 100644 ext/dl/dl.c create mode 100644 ext/dl/dl.h create mode 100644 ext/dl/extconf.rb create mode 100644 ext/dl/handle.c create mode 100644 ext/dl/lib/dl/import.rb create mode 100644 ext/dl/lib/dl/struct.rb create mode 100644 ext/dl/lib/dl/types.rb create mode 100644 ext/dl/mkcallback.rb create mode 100644 ext/dl/test/test_all.rb create mode 100644 ext/dl/test/test_base.rb create mode 100644 ext/dl/test/test_dl2.rb create mode 100644 ext/dl/test/test_func.rb create mode 100644 ext/dl/test/test_import.rb create mode 100644 ext/dl/test/test_win32.rb diff --git a/ext/dl/cfunc.c b/ext/dl/cfunc.c new file mode 100644 index 0000000000..6dcca5c6db --- /dev/null +++ b/ext/dl/cfunc.c @@ -0,0 +1,512 @@ +/* -*- C -*- + * $Id$ + */ + +#include +#include +#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(HAVE_WINDOWS_H) +#include +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 + + +void +dlcfunc_free(struct cfunc_data *data) +{ + if( data->name ){ + xfree(data->name); + } + xfree(data); +} + +VALUE +rb_dlcfunc_new(void (*func)(), int type, const char *name, ID calltype) +{ + VALUE val; + struct cfunc_data *data; + + rb_secure(4); + if( func ){ + val = Data_Make_Struct(rb_cDLCFunc, struct cfunc_data, 0, dlcfunc_free, data); + data->ptr = 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_obj_is_kind_of(val, rb_cDLCFunc) ){ + Data_Get_Struct(val, struct cfunc_data, data); + func = data->ptr; + } + else if( val == Qnil ){ + func = NULL; + } + else{ + rb_raise(rb_eTypeError, "DL::CFunc was expected"); + } + + return func; +} + +VALUE +rb_dlcfunc_s_allocate(VALUE klass) +{ + VALUE obj; + struct cfunc_data *data; + + obj = Data_Make_Struct(klass, struct cfunc_data, 0, dlcfunc_free, data); + data->ptr = 0; + data->name = 0; + data->type = 0; + data->calltype = CFUNC_CDECL; + + return obj; +} + +VALUE +rb_dlcfunc_initialize(int argc, VALUE argv[], VALUE self) +{ + VALUE addr, name, type, calltype; + struct cfunc_data *data; + void *saddr; + const char *sname; + + rb_scan_args(argc, argv, "13", &addr, &type, &name, &calltype); + + saddr = (void*)(NUM2PTR(rb_Integer(addr))); + sname = NIL_P(name) ? NULL : StringValuePtr(name); + + Data_Get_Struct(self, struct cfunc_data, data); + if( data->name ) xfree(data->name); + data->ptr = saddr; + data->name = sname ? strdup(sname) : 0; + data->type = (type == Qnil) ? DLTYPE_VOID : NUM2INT(type); + data->calltype = (calltype == Qnil) ? CFUNC_CDECL : SYM2ID(calltype); + + return Qnil; +} + +VALUE +rb_dlcfunc_name(VALUE self) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + return cfunc->name ? rb_tainted_str_new2(cfunc->name) : Qnil; +} + +VALUE +rb_dlcfunc_ctype(VALUE self) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + return INT2NUM(cfunc->type); +} + +VALUE +rb_dlcfunc_set_ctype(VALUE self, VALUE ctype) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + cfunc->type = NUM2INT(ctype); + return ctype; +} + +VALUE +rb_dlcfunc_calltype(VALUE self) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + return ID2SYM(cfunc->calltype); +} + +VALUE +rb_dlcfunc_set_calltype(VALUE self, VALUE sym) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + cfunc->calltype = SYM2ID(sym); + return sym; +} + + +VALUE +rb_dlcfunc_ptr(VALUE self) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + return PTR2NUM(cfunc->ptr); +} + +VALUE +rb_dlcfunc_set_ptr(VALUE self, VALUE addr) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + cfunc->ptr = NUM2PTR(addr); + + return Qnil; +} + +VALUE +rb_dlcfunc_inspect(VALUE self) +{ + VALUE val; + char *str; + int str_size; + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + + str_size = (cfunc->name ? strlen(cfunc->name) : 0) + 100; + str = ruby_xmalloc(str_size); + snprintf(str, str_size - 1, + "#", + cfunc, + cfunc->ptr, + cfunc->type, + cfunc->name ? cfunc->name : ""); + val = rb_tainted_str_new2(str); + ruby_xfree(str); + + return val; +} + +#if defined(__GNUC__) +# define DECL_FUNC(f,ret,args,calltype) ret (__attribute__((calltype)) *f)(args) +/* # define DECL_FUNC(f,ret,args,calltype) ret (*f)(args) */ +#else +# error "unsupported compiler." +#endif + +#define CALL_CASE switch( RARRAY(ary)->len ){ \ + 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."); \ +} + + +VALUE +rb_dlcfunc_call(VALUE self, VALUE ary) +{ + struct cfunc_data *cfunc; + int i; + DLSTACK_TYPE stack[DLSTACK_SIZE]; + VALUE result = Qnil; + + rb_secure_update(self); + + memset(stack, 0, sizeof(DLSTACK_TYPE) * DLSTACK_SIZE); + Check_Type(ary, T_ARRAY); + + Data_Get_Struct(self, struct cfunc_data, cfunc); + + if( cfunc->ptr == 0 ){ + rb_raise(rb_eDLError, "can't call null-function."); + return Qnil; + } + + for( i = 0; i < RARRAY(ary)->len; i++ ){ + if( i >= DLSTACK_SIZE ){ + rb_raise(rb_eDLError, "too many arguments (stack overflow)"); + } + stack[i] = NUM2LONG(RARRAY(ary)->ptr[i]); + } + + /* calltype == CFUNC_CDECL */ + if( cfunc->calltype == CFUNC_CDECL ){ + switch( cfunc->type ){ + case DLTYPE_VOID: +#define CASE(n) case n: { \ + DECL_FUNC(f,void,DLSTACK_PROTO##n,cdecl) = cfunc->ptr; \ + f(DLSTACK_ARGS##n(stack)); \ + result = Qnil; \ +} + CALL_CASE; +#undef CASE + break; + case DLTYPE_VOIDP: +#define CASE(n) case n: { \ + DECL_FUNC(f,void*,DLSTACK_PROTO##n,cdecl) = 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(f,char,DLSTACK_PROTO##n,cdecl) = 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(f,short,DLSTACK_PROTO##n,cdecl) = 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(f,int,DLSTACK_PROTO##n,cdecl) = 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(f,long,DLSTACK_PROTO##n,cdecl) = 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(f,long long,DLSTACK_PROTO,cdecl) = cfunc->ptr; \ + LONG_LONG ret; \ + ret = f(DLSTACK_ARGS(stack)); \ + result = LL2NUM(ret); \ +} + CALL_CASE; +#undef CASE + break; +#endif + case DLTYPE_FLOAT: +#define CASE(n) case n: { \ + DECL_FUNC(f,float,DLSTACK_PROTO,cdecl) = cfunc->ptr; \ + float ret; \ + ret = f(DLSTACK_ARGS(stack)); \ + result = rb_float_new(ret); \ +} + CALL_CASE; +#undef CASE + break; + case DLTYPE_DOUBLE: +#define CASE(n) case n: { \ + DECL_FUNC(f,double,DLSTACK_PROTO,cdecl) = cfunc->ptr; \ + double ret; \ + ret = f(DLSTACK_ARGS(stack)); \ + result = rb_float_new(ret); \ +} + CALL_CASE; +#undef CASE + break; + default: + rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type); + } + } + else if( cfunc->calltype == CFUNC_STDCALL ){ + /* calltype == CFUNC_STDCALL */ + switch( cfunc->type ){ + case DLTYPE_VOID: +#define CASE(n) case n: { \ + DECL_FUNC(f,void,DLSTACK_PROTO##n,stdcall) = cfunc->ptr; \ + f(DLSTACK_ARGS##n(stack)); \ + result = Qnil; \ +} + CALL_CASE; +#undef CASE + break; + case DLTYPE_VOIDP: +#define CASE(n) case n: { \ + DECL_FUNC(f,void*,DLSTACK_PROTO##n,stdcall) = 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(f,char,DLSTACK_PROTO##n,stdcall) = 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(f,short,DLSTACK_PROTO##n,stdcall) = 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(f,int,DLSTACK_PROTO##n,stdcall) = 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(f,long,DLSTACK_PROTO##n,stdcall) = 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(f,long long,DLSTACK_PROTO,stdcall) = cfunc->ptr; \ + LONG_LONG ret; \ + ret = f(DLSTACK_ARGS(stack)); \ + result = LL2NUM(ret); \ +} + CALL_CASE; +#undef CASE + break; +#endif + case DLTYPE_FLOAT: +#define CASE(n) case n: { \ + DECL_FUNC(f,float,DLSTACK_PROTO,stdcall) = cfunc->ptr; \ + float ret; \ + ret = f(DLSTACK_ARGS(stack)); \ + result = rb_float_new(ret); \ +} + CALL_CASE; +#undef CASE + break; + case DLTYPE_DOUBLE: +#define CASE(n) case n: { \ + DECL_FUNC(f,double,DLSTACK_PROTO,stdcall) = cfunc->ptr; \ + double ret; \ + ret = f(DLSTACK_ARGS(stack)); \ + result = rb_float_new(ret); \ +} + CALL_CASE; +#undef CASE + break; + default: + rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type); + } + } + else{ + rb_raise(rb_eDLError, "unsupported call type: %x", cfunc->calltype); + } + + rb_dl_set_last_error(self, INT2NUM(errno)); +#if defined(HAVE_WINDOWS_H) + rb_dl_set_win32_last_error(self, INT2NUM(GetLastError())); +#endif + + return result; +} + +VALUE +rb_dlcfunc_to_i(VALUE self) +{ + struct cfunc_data *cfunc; + + Data_Get_Struct(self, struct cfunc_data, cfunc); + return PTR2NUM(cfunc->ptr); +} + +void +Init_dlcfunc() +{ + id_last_error = rb_intern("__DL2_LAST_ERROR__"); +#if defined(HAVE_WINDOWS_H) + id_win32_last_error = rb_intern("__DL2_WIN32_LAST_ERROR__"); +#endif + rb_cDLCFunc = rb_define_class_under(rb_mDL, "CFunc", rb_cObject); + rb_define_alloc_func(rb_cDLCFunc, rb_dlcfunc_s_allocate); + rb_define_module_function(rb_cDLCFunc, "last_error", rb_dl_get_last_error, 0); +#if defined(HAVE_WINDOWS_H) + 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); +} diff --git a/ext/dl/cptr.c b/ext/dl/cptr.c new file mode 100644 index 0000000000..27be637a5a --- /dev/null +++ b/ext/dl/cptr.c @@ -0,0 +1,462 @@ +/* -*- C -*- + * $Id$ + */ + +#include +#include +#include +#include /* for ruby version code */ +#include "dl.h" + +VALUE rb_cDLCPtr; + +static void +dlptr_free(struct ptr_data *data) +{ + if (data->ptr) { + if (data->free) { + (*(data->free))(data->ptr); + } + } +} + +static void +dlptr_mark(struct ptr_data *data) +{ +} + +void +dlptr_init(VALUE val) +{ + struct ptr_data *data; + + Data_Get_Struct(val, struct ptr_data, data); + OBJ_TAINT(val); +} + +VALUE +rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func) +{ + struct ptr_data *data; + VALUE val; + + rb_secure(4); + val = Data_Make_Struct(klass, struct ptr_data, + 0, dlptr_free, data); + data->ptr = ptr; + data->free = func; + data->size = size; + dlptr_init(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; + + rb_secure(4); + 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)) { + Data_Get_Struct(val, struct ptr_data, 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; + + rb_secure(4); + obj = Data_Make_Struct(klass, struct ptr_data, dlptr_mark, dlptr_free, data); + data->ptr = 0; + data->size = 0; + data->free = 0; + + return obj; +} + +static VALUE +rb_dlptr_initialize(int argc, VALUE argv[], VALUE self) +{ + VALUE ptr, sym, size; + struct ptr_data *data; + void *p = NULL; + freefunc_t f = NULL; + long s = 0; + + switch (rb_scan_args(argc, argv, "12", &ptr, &size, &sym)) { + case 1: + p = (void*)(NUM2PTR(rb_Integer(ptr))); + break; + case 2: + p = (void*)(NUM2PTR(rb_Integer(ptr))); + s = NUM2LONG(size); + break; + case 3: + p = (void*)(NUM2PTR(rb_Integer(ptr))); + s = NUM2LONG(size); + f = NIL_P(sym) ? NULL : RCFUNC_DATA(sym)->ptr; + break; + default: + rb_bug("rb_dlptr_initialize"); + } + + if (p) { + Data_Get_Struct(self, struct ptr_data, data); + if (data->ptr && data->free) { + /* Free previous memory. Use of inappropriate initialize may cause SEGV. */ + (*(data->free))(data->ptr); + } + data->ptr = p; + data->size = s; + data->free = f; + } + + return Qnil; +} + +static VALUE +rb_dlptr_s_malloc(int argc, VALUE argv[], VALUE klass) +{ + VALUE size, sym, obj; + int 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 = RCFUNC_DATA(sym)->ptr; + break; + default: + rb_bug("rb_dlptr_s_malloc"); + } + + obj = rb_dlptr_malloc(s,f); + + return obj; +} + +VALUE +rb_dlptr_to_i(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return PTR2NUM(data->ptr); +} + +VALUE +rb_dlptr_to_value(VALUE self) +{ + struct ptr_data *data; + Data_Get_Struct(self, struct ptr_data, data); + return (VALUE)(data->ptr); +} + +VALUE +rb_dlptr_ptr(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return rb_dlptr_new(*((void**)(data->ptr)),0,0); +} + +VALUE +rb_dlptr_ref(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return rb_dlptr_new(&(data->ptr),0,0); +} + +VALUE +rb_dlptr_null_p(VALUE self) +{ + struct ptr_data *data; + + Data_Get_Struct(self, struct ptr_data, data); + return data->ptr ? Qfalse : Qtrue; +} + +VALUE +rb_dlptr_free_set(VALUE self, VALUE val) +{ + struct ptr_data *data; + extern VALUE rb_cDLCFunc; + + Data_Get_Struct(self, struct ptr_data, data); + if( rb_obj_is_kind_of(val, rb_cDLCFunc) == Qtrue ){ + data->free = RCFUNC_DATA(val)->ptr; + } + else{ + data->free = NUM2PTR(rb_Integer(val)); + } + + return Qnil; +} + +VALUE +rb_dlptr_free_get(VALUE self) +{ + struct ptr_data *pdata; + + Data_Get_Struct(self, struct ptr_data, pdata); + + return rb_dlcfunc_new(pdata->free, DLTYPE_VOID, "free", CFUNC_CDECL); +} + +VALUE +rb_dlptr_to_s(int argc, VALUE argv[], VALUE self) +{ + struct ptr_data *data; + VALUE arg1, val; + int len; + + Data_Get_Struct(self, struct ptr_data, 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; +} + +VALUE +rb_dlptr_to_str(int argc, VALUE argv[], VALUE self) +{ + struct ptr_data *data; + VALUE arg1, val; + int len; + + Data_Get_Struct(self, struct ptr_data, 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; +} + +VALUE +rb_dlptr_inspect(VALUE self) +{ + struct ptr_data *data; + char str[1024]; + + Data_Get_Struct(self, struct ptr_data, data); + snprintf(str, 1023, "#<%s:%p ptr=%p size=%ld free=%p>", + rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free); + return rb_str_new2(str); +} + +VALUE +rb_dlptr_eql(VALUE self, VALUE other) +{ + void *ptr1, *ptr2; + ptr1 = rb_dlptr2cptr(self); + ptr2 = rb_dlptr2cptr(other); + + return ptr1 == ptr2 ? Qtrue : Qfalse; +} + +VALUE +rb_dlptr_cmp(VALUE self, VALUE other) +{ + void *ptr1, *ptr2; + ptr1 = rb_dlptr2cptr(self); + ptr2 = rb_dlptr2cptr(other); + return PTR2NUM((long)ptr1 - (long)ptr2); +} + +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); +} + +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); +} + +VALUE +rb_dlptr_aref(int argc, VALUE argv[], VALUE self) +{ + VALUE arg0, arg1; + size_t offset, len; + + switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){ + case 1: + offset = NUM2ULONG(arg0); + len = 1; + break; + case 2: + offset = NUM2ULONG(arg0); + len = NUM2ULONG(arg1); + break; + defualt: + rb_bug("rb_dlptr_aset()"); + } + return rb_tainted_str_new(RPTR_DATA(self)->ptr + offset, len); +} + +VALUE +rb_dlptr_aset(int argc, VALUE argv[], VALUE self) +{ + VALUE arg0, arg1, arg2; + size_t offset, len; + void *mem; + + switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){ + case 2: + offset = NUM2ULONG(arg0); + len = 1; + mem = NUM2PTR(arg1); + break; + case 3: + offset = NUM2ULONG(arg0); + len = NUM2ULONG(arg1); + if( TYPE(arg2) == T_STRING ){ + mem = StringValuePtr(arg2); + } + else if( rb_obj_is_kind_of(arg2, rb_cDLCPtr) ){ + mem = rb_dlptr2cptr(arg2); + } + else{ + mem = NUM2PTR(arg2); + } + break; + defualt: + rb_bug("rb_dlptr_aset()"); + } + memcpy(RPTR_DATA(self)->ptr + offset, mem, len); + return Qnil; +} + +VALUE +rb_dlptr_size(int argc, VALUE argv[], VALUE self) +{ + VALUE size; + + if (rb_scan_args(argc, argv, "01", &size) == 0){ + return LONG2NUM(RPTR_DATA(self)->size); + } + else{ + RPTR_DATA(self)->size = NUM2LONG(size); + return size; + } +} + +VALUE +rb_dlptr_s_to_ptr(VALUE self, VALUE val) +{ + if( rb_obj_is_kind_of(val, rb_cIO) == Qtrue ){ + OpenFile *fptr; + FILE *fp; + GetOpenFile(val, fptr); +#if RUBY_VERSION_CODE >= 190 + fp = rb_io_stdio_file(fptr); +#else + fp = fptr->f; +#endif + return rb_dlptr_new(fp, sizeof(FILE), NULL); + } + else{ + return rb_dlptr_new(NUM2PTR(rb_Integer(val)), 0, NULL); + } +} + +void +Init_dlptr() +{ + 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_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, -1); + rb_define_method(rb_cDLCPtr, "size=", rb_dlptr_size, -1); + + rb_define_const(rb_mDL, "NULL", rb_dlptr_new(0, 0, 0)); +} diff --git a/ext/dl/depend b/ext/dl/depend new file mode 100644 index 0000000000..3d86c3405f --- /dev/null +++ b/ext/dl/depend @@ -0,0 +1,13 @@ +DISTCLEANFILES = $(srcdir)/callback.h + +cfunc.o: cfunc.c dl.h + +cptr.o: cptr.c dl.h + +handle.o: handle.c dl.h + +dl.o: dl.c dl.h callback.h + +callback.h: $(srcdir)/mkcallback.rb dl.h + @echo "generating callback.h" + @$(RUBY) $(srcdir)/mkcallback.rb $(srcdir)/dl.h > $@ diff --git a/ext/dl/dl.c b/ext/dl/dl.c new file mode 100644 index 0000000000..3d1d90a642 --- /dev/null +++ b/ext/dl/dl.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include "dl.h" + +VALUE rb_mDL; +VALUE rb_eDLError; +VALUE rb_eDLTypeError; + +ID rbdl_id_cdecl; +ID rbdl_id_stdcall; + +VALUE +rb_dl_dlopen(int argc, VALUE argv[], VALUE self) +{ + rb_secure(2); + return rb_class_new_instance(argc, argv, rb_cDLHandle); +} + +VALUE +rb_dl_malloc(VALUE self, VALUE size) +{ + void *ptr; + + ptr = (void*)ruby_xmalloc(NUM2INT(size)); + return PTR2NUM(ptr); +} + +VALUE +rb_dl_realloc(VALUE self, VALUE addr, VALUE size) +{ + void *ptr = NUM2PTR(addr); + + ptr = (void*)ruby_xrealloc(ptr, NUM2INT(size)); + return PTR2NUM(ptr); +} + +VALUE +rb_dl_free(VALUE self, VALUE addr) +{ + void *ptr = NUM2PTR(addr); + ruby_xfree(ptr); + return Qnil; +} + +VALUE +rb_dl_ptr2value(VALUE self, VALUE addr) +{ + return (VALUE)NUM2PTR(addr); +} + +VALUE +rb_dl_value2ptr(VALUE self, VALUE val) +{ + return PTR2NUM((void*)val); +} + +#if defined(__GNUC__) +# define PRE_DECL_CDECL __attribute__((cdecl)) +# define PRE_DECL_STDCALL __attribute__((stdcall)) +# define POST_DECL_CDECL +# define POST_DECL_STDCALL +#else +# error "unsupported compiler" +#endif + +#include "callback.h" + +void +Init_dl() +{ + rbdl_id_cdecl = rb_intern("cdecl"); + rbdl_id_stdcall = rb_intern("stdcall"); + + void Init_dlhandle(); + void Init_dlcfunc(); + void Init_dlptr(); + + rb_mDL = rb_define_module("DL"); + rb_eDLError = rb_define_class_under(rb_mDL, "DLError", rb_eStandardError); + rb_eDLTypeError = rb_define_class_under(rb_mDL, "DLTypeError", rb_eDLError); + + rb_define_const(rb_mDL, "MAX_CALLBACK", INT2NUM(MAX_CALLBACK)); + rb_define_const(rb_mDL, "DLSTACK_SIZE", INT2NUM(DLSTACK_SIZE)); + + rb_dl_init_callbacks(); + + rb_define_const(rb_mDL, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL)); + rb_define_const(rb_mDL, "RTLD_LAZY", INT2NUM(RTLD_LAZY)); + rb_define_const(rb_mDL, "RTLD_NOW", INT2NUM(RTLD_NOW)); + + rb_define_const(rb_mDL, "TYPE_VOID", INT2NUM(DLTYPE_VOID)); + rb_define_const(rb_mDL, "TYPE_VOIDP", INT2NUM(DLTYPE_VOIDP)); + rb_define_const(rb_mDL, "TYPE_CHAR", INT2NUM(DLTYPE_CHAR)); + rb_define_const(rb_mDL, "TYPE_SHORT", INT2NUM(DLTYPE_SHORT)); + rb_define_const(rb_mDL, "TYPE_INT", INT2NUM(DLTYPE_INT)); + rb_define_const(rb_mDL, "TYPE_LONG", INT2NUM(DLTYPE_LONG)); +#if HAVE_LONG_LONG + rb_define_const(rb_mDL, "TYPE_LONG_LONG", INT2NUM(DLTYPE_LONG_LONG)); +#endif + rb_define_const(rb_mDL, "TYPE_FLOAT", INT2NUM(DLTYPE_FLOAT)); + rb_define_const(rb_mDL, "TYPE_DOUBLE", INT2NUM(DLTYPE_DOUBLE)); + + rb_define_const(rb_mDL, "ALIGN_VOIDP", INT2NUM(ALIGN_VOIDP)); + rb_define_const(rb_mDL, "ALIGN_CHAR", INT2NUM(ALIGN_CHAR)); + rb_define_const(rb_mDL, "ALIGN_SHORT", INT2NUM(ALIGN_SHORT)); + rb_define_const(rb_mDL, "ALIGN_INT", INT2NUM(ALIGN_INT)); + rb_define_const(rb_mDL, "ALIGN_LONG", INT2NUM(ALIGN_LONG)); +#if HAVE_LONG_LONG + rb_define_const(rb_mDL, "ALIGN_LONG_LONG", INT2NUM(ALIGN_LONG_LONG)); +#endif + rb_define_const(rb_mDL, "ALIGN_FLOAT", INT2NUM(ALIGN_FLOAT)); + rb_define_const(rb_mDL, "ALIGN_DOUBLE",INT2NUM(ALIGN_DOUBLE)); + + rb_define_const(rb_mDL, "SIZEOF_VOIDP", INT2NUM(sizeof(void*))); + rb_define_const(rb_mDL, "SIZEOF_CHAR", INT2NUM(sizeof(char))); + rb_define_const(rb_mDL, "SIZEOF_SHORT", INT2NUM(sizeof(short))); + rb_define_const(rb_mDL, "SIZEOF_INT", INT2NUM(sizeof(int))); + rb_define_const(rb_mDL, "SIZEOF_LONG", INT2NUM(sizeof(long))); +#if HAVE_LONG_LONG + rb_define_const(rb_mDL, "SIZEOF_LONG_LONG", INT2NUM(sizeof(LONG_LONG))); +#endif + rb_define_const(rb_mDL, "SIZEOF_FLOAT", INT2NUM(sizeof(float))); + rb_define_const(rb_mDL, "SIZEOF_DOUBLE",INT2NUM(sizeof(double))); + + 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); + rb_define_const(rb_mDL, "RUBY_FREE", PTR2NUM(ruby_xfree)); + + Init_dlhandle(); + Init_dlcfunc(); + Init_dlptr(); +} diff --git a/ext/dl/dl.h b/ext/dl/dl.h new file mode 100644 index 0000000000..27621fa5a7 --- /dev/null +++ b/ext/dl/dl.h @@ -0,0 +1,183 @@ +#ifndef RUBY_DL_H +#define RUBY_DL_H + +#include + +#if defined(HAVE_DLFCN_H) +# include +# /* 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(HAVE_WINDOWS_H) +# include +# define dlclose(ptr) FreeLibrary((HINSTANCE)ptr) +# define dlopen(name,flag) ((void*)LoadLibrary(name)) +# define dlerror() "unknown error" +# 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 long +#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 +#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 + +#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; + +typedef struct { char c; void *x; } s_voidp; +typedef struct { char c; short x; } s_short; +typedef struct { char c; int x; } s_int; +typedef struct { char c; long x; } s_long; +typedef struct { char c; float x; } s_float; +typedef struct { char c; double x; } s_double; +#if HAVE_LONG_LONG +typedef struct { char c; LONG_LONG x; } s_long_long; +#endif + +#define ALIGN_VOIDP (sizeof(s_voidp) - sizeof(void *)) +#define ALIGN_SHORT (sizeof(s_short) - sizeof(short)) +#define ALIGN_CHAR (1) +#define ALIGN_INT (sizeof(s_int) - sizeof(int)) +#define ALIGN_LONG (sizeof(s_long) - sizeof(long)) +#if HAVE_LONG_LONG +#define ALIGN_LONG_LONG (sizeof(s_long_long) - sizeof(LONG_LONG)) +#endif +#define ALIGN_FLOAT (sizeof(s_float) - sizeof(float)) +#define ALIGN_DOUBLE (sizeof(s_double) - sizeof(double)) + +#define DLALIGN(ptr,offset,align) {\ + while( (((unsigned long)((char *)ptr + offset)) % align) != 0 ) offset++;\ +} + + +#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; +}; +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; +}; + +#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); +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 diff --git a/ext/dl/extconf.rb b/ext/dl/extconf.rb new file mode 100644 index 0000000000..6311fc82d1 --- /dev/null +++ b/ext/dl/extconf.rb @@ -0,0 +1,28 @@ +require 'mkmf' + +if( Config::CONFIG['CC'] =~ /gcc/ ) + $CFLAGS << " -fno-defer-pop -fno-omit-frame-pointer" +end + +$INSTALLFILES = [ + ["dl.h", "$(archdir)$(target_prefix)", ""], +] + +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 ) + create_makefile("dl") +end diff --git a/ext/dl/handle.c b/ext/dl/handle.c new file mode 100644 index 0000000000..7f5ce12168 --- /dev/null +++ b/ext/dl/handle.c @@ -0,0 +1,200 @@ +/* -*- C -*- + * $Id$ + */ + +#include +#include "dl.h" + +VALUE rb_cDLHandle; + +void +dlhandle_free(struct dl_handle *dlhandle) +{ + if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){ + dlclose(dlhandle->ptr); + } +} + +VALUE +rb_dlhandle_close(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + dlhandle->open = 0; + return INT2NUM(dlclose(dlhandle->ptr)); +} + +VALUE +rb_dlhandle_s_allocate(VALUE klass) +{ + VALUE obj; + struct dl_handle *dlhandle; + + obj = Data_Make_Struct(rb_cDLHandle, struct dl_handle, 0, + dlhandle_free, dlhandle); + dlhandle->ptr = 0; + dlhandle->open = 0; + dlhandle->enable_close = 0; + + return obj; +} + +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"); + } + + ptr = dlopen(clib, cflag); +#if defined(HAVE_DLERROR) + if( !ptr && (err = dlerror()) ){ + rb_raise(rb_eDLError, err); + } +#else + if( !ptr ){ + err = dlerror(); + rb_raise(rb_eDLError, err); + } +#endif + Data_Get_Struct(self, struct dl_handle, 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; +} + +VALUE +rb_dlhandle_enable_close(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + dlhandle->enable_close = 1; + return Qnil; +} + +VALUE +rb_dlhandle_disable_close(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + dlhandle->enable_close = 0; + return Qnil; +} + +VALUE +rb_dlhandle_to_i(VALUE self) +{ + struct dl_handle *dlhandle; + + Data_Get_Struct(self, struct dl_handle, dlhandle); + return PTR2NUM(dlhandle); +} + +VALUE +rb_dlhandle_sym(VALUE self, VALUE sym) +{ + void (*func)(); + struct sym_data *data; + struct dl_handle *dlhandle; + void *handle; + const char *name; + const char *err; + + rb_secure(2); + + if( sym == Qnil ){ +#if defined(RTLD_NEXT) + name = RTLD_NEXT; +#else + name = NULL; +#endif + } + else{ + name = StringValuePtr(sym); + } + + + Data_Get_Struct(self, struct dl_handle, dlhandle); + if( ! dlhandle->open ){ + rb_raise(rb_eDLError, "Closed handle."); + } + handle = dlhandle->ptr; + + func = dlsym(handle, name); +#if defined(HAVE_DLERROR) + if( !func && (err = dlerror()) ) +#else + if( !func ) +#endif + { +#if defined(__CYGWIN__) || defined(WIN32) || defined(__MINGW32__) + { + int len = strlen(name); + char *name_a = (char*)xmalloc(len+2); + strcpy(name_a, name); + name_a[len] = 'A'; + name_a[len+1] = '\0'; + func = dlsym(handle, name_a); + xfree(name_a); +#if defined(HAVE_DLERROR) + if( !func && (err = dlerror()) ) +#else + if( !func ) +#endif + { + rb_raise(rb_eDLError, "Unknown symbol \"%sA\".", name); + } + } +#else + rb_raise(rb_eDLError, "Unknown symbol \"%s\".", name); +#endif + } + + return PTR2NUM(func); +} + +void +Init_dlhandle() +{ + rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cObject); + rb_define_alloc_func(rb_cDLHandle, rb_dlhandle_s_allocate); + 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); +} diff --git a/ext/dl/lib/dl/import.rb b/ext/dl/lib/dl/import.rb new file mode 100644 index 0000000000..2f21e9195c --- /dev/null +++ b/ext/dl/lib/dl/import.rb @@ -0,0 +1,182 @@ +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 + + 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 + nil + 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 parse_bind_options(opts) + h = {} + prekey = nil + 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) + name, ctype, argtype = parse_signature(signature, @type_alias) + opt = parse_bind_options(opts) + f = import_function(name, ctype, argtype, opt[:call_type]) + @func_map[name] = f + #define_method(name){|*args,&block| f.call(*args,&block)} + module_eval(<<-EOS) + def #{name}(*args, &block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + def bind(signature, *opts, &blk) + 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)} + module_eval(<<-EOS) + def #{name}(*args,&block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + def struct(signature) + tys, mems = parse_struct_signature(signature, @type_alias) + CStructBuilder.create(CStruct, tys, mems) + end + + def union(signature) + tys, mems = parse_struct_signature(signature, @type_alias) + 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 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) + f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype) + f.bind(&block) + f + end + + def create_temp_function(name, ctype, argtype, call_type = nil) + 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 diff --git a/ext/dl/lib/dl/struct.rb b/ext/dl/lib/dl/struct.rb new file mode 100644 index 0000000000..5c0775277e --- /dev/null +++ b/ext/dl/lib/dl/struct.rb @@ -0,0 +1,203 @@ +require 'dl' +require 'dl/pack.rb' + +module DL + class CStruct + def CStruct.entity_class() + CStructEntity + end + end + + class CUnion + def CUnion.entity_class() + CUnionEntity + end + end + + module CStructBuilder + def create(klass, types, members) + new_class = Class.new(klass){ + entity = nil + 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) + 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 + + class CStructEntity < CPtr + include PackInfo + include ValueUtil + + def CStructEntity.malloc(types, func = nil) + addr = DL.malloc(CStructEntity.size(types)) + CStructEntity.new(addr, types, func) + end + + def CStructEntity.size(types) + offset = 0 + types.each_with_index{|t,i| + orig_offset = offset + if( t.is_a?(Array) ) + offset = PackInfo.align(orig_offset, PackInfo::ALIGN_MAP[TYPE_VOIDP]) + size = offset - orig_offset + offset += (PackInfo::SIZE_MAP[t[0]] * t[1]) + else + offset = PackInfo.align(orig_offset, PackInfo::ALIGN_MAP[t]) + size = offset - orig_offset + offset += PackInfo::SIZE_MAP[t] + end + } + offset = PackInfo.align(offset, PackInfo::ALIGN_MAP[TYPE_VOIDP]) + offset + end + + def initialize(addr, types, func = nil) + set_ctypes(types) + super(addr, @size, func) + end + + def assign_names(members) + @members = members + end + + def set_ctypes(types) + @ctypes = types + @offset = [] + offset = 0 + types.each_with_index{|t,i| + orig_offset = offset + if( t.is_a?(Array) ) + offset = align(orig_offset, ALIGN_MAP[TYPE_VOIDP]) + else + offset = align(orig_offset, ALIGN_MAP[t]) + end + size = offset - orig_offset + @offset[i] = offset + if( t.is_a?(Array) ) + offset += (SIZE_MAP[t[0]] * t[1]) + else + offset += SIZE_MAP[t] + end + } + offset = align(offset, ALIGN_MAP[TYPE_VOIDP]) + @size = offset + end + + 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 + + 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() + super(@size) + end + end + + class CUnionEntity < CStruct + include PackInfo + + def CUnionEntity.malloc(types) + addr = DL.malloc(CUnionEntity.size(types)) + CUnionEntity.new(addr, types, func) + end + + def CUnionEntity.size(types) + size = 0 + types.each_with_index{|t,i| + if( t.is_a?(Array) ) + tsize = PackInfo::SIZE_MAP[t[0]] * t[1] + else + tsize = PackInfo::SIZE_MAP[t] + end + if( tsize > size ) + size = tsize + end + } + end + + def set_ctypes(types) + @ctypes = types + @offset = [] + @size = 0 + types.each_with_index{|t,i| + @offset[i] = 0 + if( t.is_a?(Array) ) + size = SIZE_MAP[t[0]] * t[1] + else + size = SIZE_MAP[t] + end + if( size > @size ) + @size = size + end + } + end + end +end + diff --git a/ext/dl/lib/dl/types.rb b/ext/dl/lib/dl/types.rb new file mode 100644 index 0000000000..b85ac890cd --- /dev/null +++ b/ext/dl/lib/dl/types.rb @@ -0,0 +1,40 @@ +module DL + module Win32Types + def included(m) + m.module_eval{ + typealias "DWORD", "unsigned long" + typealias "PDWORD", "unsigned 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", "unsigned long" + 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 + + module BasicTypes + def included(m) + 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 diff --git a/ext/dl/mkcallback.rb b/ext/dl/mkcallback.rb new file mode 100644 index 0000000000..0b071d4caa --- /dev/null +++ b/ext/dl/mkcallback.rb @@ -0,0 +1,190 @@ +$out ||= $stdout +$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 => 'RFLOAT(%s)->value', + }, + DOUBLE => { + :name => 'double', + :type => 'double', + :conv => 'RFLOAT(%s)->value', + }, + 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 << (< ") + exit + end +end + +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(expected === actual, message) + end + + def assert_positive(actual) + assert(actual > 0) + end + + def assert_zero(actual) + assert(actual == 0) + end + + def assert_negative(actual) + assert(actual < 0) + end + + def test_empty() + end + end +end diff --git a/ext/dl/test/test_dl2.rb b/ext/dl/test/test_dl2.rb new file mode 100644 index 0000000000..d1b18057cf --- /dev/null +++ b/ext/dl/test/test_dl2.rb @@ -0,0 +1,89 @@ +require 'test_base.rb' +require 'dl/callback' + +module DL +class TestDL < TestBase + 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_match(0.09..0.11, x) + + cfunc = CFunc.new(@libc['atof'], TYPE_DOUBLE, 'atof') + x = cfunc.call(["-0.1"].pack("p").unpack("l!*")) + assert_match(-0.11 .. -0.09, x) + end + + def test_sin() + cfunc = CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin') + x = cfunc.call([3.14/2].pack("d").unpack("l!*")) + assert_equal(x, Math.sin(3.14/2)) + + cfunc = CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin') + x = cfunc.call([-3.14/2].pack("d").unpack("l!*")) + assert_equal(Math.sin(-3.14/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([buff,str].pack("pp").unpack("l!*")) + 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([buff,str,3].pack("ppi").unpack("l!*")) + 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,str].pack("lp").unpack("l!*")) + 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([buff, buff.size, 1, cb].pack("pI!I!L!").unpack("l!*")) + 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 +end +end # module DL diff --git a/ext/dl/test/test_func.rb b/ext/dl/test/test_func.rb new file mode 100644 index 0000000000..64a32d9565 --- /dev/null +++ b/ext/dl/test/test_func.rb @@ -0,0 +1,62 @@ +require 'test_base' +require 'dl/func' + +module DL + class TestFunc < TestBase + 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_isdigit() + f = Function.new(CFunc.new(@libc['isdigit'], TYPE_INT, 'isdigit'), + [TYPE_INT]) + r1 = f.call(?1) + r2 = f.call(?2) + rr = f.call(?r) + assert_positive(r1) + assert_positive(r2) + assert_zero(rr) + end + + def test_atof() + f = Function.new(CFunc.new(@libc['atof'], TYPE_FLOAT, '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 = "12.34" + buff2 = " " + r = f.call(buff1, buff2) + assert_match(12.00..13.00, r) + end + + def test_qsort1() + cb = Function.new(CFunc.new(0, TYPE_INT, '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_INT, TYPE_INT, TYPE_VOIDP]) + buff = "9341" + qsort.call(buff, buff.size, 1, cb) + assert_equal("1349", buff) + end + + def test_qsort2() + cb = TempFunction.new(CFunc.new(0, TYPE_INT, 'qsort'), + [TYPE_VOIDP, TYPE_VOIDP]) + qsort = Function.new(CFunc.new(@libc['qsort'], TYPE_VOID, 'qsort'), + [TYPE_VOIDP, TYPE_INT, TYPE_INT, 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 diff --git a/ext/dl/test/test_import.rb b/ext/dl/test/test_import.rb new file mode 100644 index 0000000000..ced2ac8dd7 --- /dev/null +++ b/ext/dl/test/test_import.rb @@ -0,0 +1,137 @@ +require '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 "float atof(string)" + extern "unsigned long strtoul(char*, char **, int)" + extern "int qsort(void*, int, int, void*)" + extern "void fprintf(FILE*, char*)" + extern "int gettimeofday(timeval*, timezone*)" rescue nil + + QsortCallback = bind("void *qsort_callback(void*, void*)", :temp) + BoundQsortCallback = bind("void *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 [ + "int num[10]", + "unsigned char buff[8]", + ] + + CallCallback = bind("void call_callback(void*, void*)"){|ptr1, ptr2| + f = Function.new(CFunc.new(ptr1.to_i, DL::TYPE_VOID, ""), [TYPE_VOIDP]) + f.call(ptr2) + } + CarriedFunction = bind("void callback_function(void*)", :carried, 0) + end + + class TestImport < TestBase + def test_unsigned_result() + d = (2 ** 31) + 1 + + r = LIBC.strtoul(d.to_s, 0, 0) + assert_equal(d, r) + end + + def test_io() + io_in,io_out = IO.pipe() + LIBC.fprintf(DL::CPtr[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,5,6,7,8,9] + s.buff = "0123456\377" + assert_equal([0,1,2,3,4,5,6,7,8,9], s.num) + assert_equal([?0,?1,?2,?3,?4,?5,?6,?\377], 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) + r2 = LIBC.isdigit(?2) + rr = LIBC.isdigit(?r) + 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 diff --git a/ext/dl/test/test_win32.rb b/ext/dl/test/test_win32.rb new file mode 100644 index 0000000000..b2210287a1 --- /dev/null +++ b/ext/dl/test/test_win32.rb @@ -0,0 +1,53 @@ +require 'test_base' +require 'dl/import' +require 'dl/types' + +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 +end + +module DL +class TestWin32 < TestBase + 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 -- cgit v1.2.3