Index: ext/dl/cfunc.c =================================================================== --- ext/dl/cfunc.c (revision 24783) +++ ext/dl/cfunc.c (working copy) @@ -8,229 +8,34 @@ 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(void *ptr) -{ - struct cfunc_data *data = ptr; - 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; + return rb_funcall( + rb_cDLCFunc, rb_intern("new"), + 4, + PTR2NUM(func), + INT2NUM(type), + rb_str_new2(name), + ID2SYM(calltype) + ); } - else{ - val = Qnil; - } - return val; + return Qnil; } -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; -} - int rb_dlcfunc_kind_p(VALUE func) { - if (TYPE(func) == T_DATA) return 0; - return RDATA(func)->dfree == dlcfunc_free; + return (Qtrue == rb_obj_is_kind_of(func, rb_cDLCFunc)); } -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; -} - - # define DECL_FUNC_CDECL(f,ret,args) ret (FUNC_CDECL(*f))(args) #ifdef FUNC_STDCALL # define DECL_FUNC_STDCALL(f,ret,args) ret (FUNC_STDCALL(*f))(args) @@ -245,23 +50,22 @@ 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; + ID calltype = SYM2ID(rb_iv_get(self, "@calltype")); + int type = NUM2INT(rb_iv_get(self, "@ctype")); + void * ptr = NUM2PTR(rb_iv_get(self, "@ptr")); 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 ){ + if( ptr == 0 ){ rb_raise(rb_eDLError, "can't call null-function"); return Qnil; } @@ -275,15 +79,15 @@ } /* calltype == CFUNC_CDECL */ - if( cfunc->calltype == CFUNC_CDECL + if( calltype == CFUNC_CDECL #ifndef FUNC_STDCALL - || cfunc->calltype == CFUNC_STDCALL + || calltype == CFUNC_STDCALL #endif ){ - switch( cfunc->type ){ + switch( type ){ case DLTYPE_VOID: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,void,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,void,DLSTACK_PROTO##n) = ptr; \ f(DLSTACK_ARGS##n(stack)); \ result = Qnil; \ } @@ -292,7 +96,7 @@ break; case DLTYPE_VOIDP: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,void*,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,void*,DLSTACK_PROTO##n) = ptr; \ void * ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = PTR2NUM(ret); \ @@ -302,7 +106,7 @@ break; case DLTYPE_CHAR: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,char,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,char,DLSTACK_PROTO##n) = ptr; \ char ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = CHR2FIX(ret); \ @@ -312,7 +116,7 @@ break; case DLTYPE_SHORT: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,short,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,short,DLSTACK_PROTO##n) = ptr; \ short ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = INT2NUM((int)ret); \ @@ -322,7 +126,7 @@ break; case DLTYPE_INT: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,int,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,int,DLSTACK_PROTO##n) = ptr; \ int ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = INT2NUM(ret); \ @@ -332,7 +136,7 @@ break; case DLTYPE_LONG: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,long,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,long,DLSTACK_PROTO##n) = ptr; \ long ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = LONG2NUM(ret); \ @@ -343,7 +147,7 @@ #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; \ + DECL_FUNC_CDECL(f,LONG_LONG,DLSTACK_PROTO##n) = ptr; \ LONG_LONG ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = LL2NUM(ret); \ @@ -354,7 +158,7 @@ #endif case DLTYPE_FLOAT: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,float,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,float,DLSTACK_PROTO##n) = ptr; \ float ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = rb_float_new(ret); \ @@ -364,7 +168,7 @@ break; case DLTYPE_DOUBLE: #define CASE(n) case n: { \ - DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n) = cfunc->ptr; \ + DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n) = ptr; \ double ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = rb_float_new(ret); \ @@ -373,16 +177,16 @@ #undef CASE break; default: - rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type); + rb_raise(rb_eDLTypeError, "unknown type %d", type); } } #ifdef FUNC_STDCALL - else if( cfunc->calltype == CFUNC_STDCALL ){ + else if( calltype == CFUNC_STDCALL ){ /* calltype == CFUNC_STDCALL */ - switch( cfunc->type ){ + switch( type ){ case DLTYPE_VOID: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,void,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,void,DLSTACK_PROTO##n##_) = ptr; \ f(DLSTACK_ARGS##n(stack)); \ result = Qnil; \ } @@ -391,7 +195,7 @@ break; case DLTYPE_VOIDP: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,void*,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,void*,DLSTACK_PROTO##n##_) = ptr; \ void * ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = PTR2NUM(ret); \ @@ -401,7 +205,7 @@ break; case DLTYPE_CHAR: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,char,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,char,DLSTACK_PROTO##n##_) = ptr; \ char ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = CHR2FIX(ret); \ @@ -411,7 +215,7 @@ break; case DLTYPE_SHORT: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,short,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,short,DLSTACK_PROTO##n##_) = ptr; \ short ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = INT2NUM((int)ret); \ @@ -421,7 +225,7 @@ break; case DLTYPE_INT: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,int,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,int,DLSTACK_PROTO##n##_) = ptr; \ int ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = INT2NUM(ret); \ @@ -431,7 +235,7 @@ break; case DLTYPE_LONG: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,long,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,long,DLSTACK_PROTO##n##_) = ptr; \ long ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = LONG2NUM(ret); \ @@ -442,7 +246,7 @@ #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; \ + DECL_FUNC_STDCALL(f,LONG_LONG,DLSTACK_PROTO##n##_) = ptr; \ LONG_LONG ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = LL2NUM(ret); \ @@ -453,7 +257,7 @@ #endif case DLTYPE_FLOAT: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,float,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,float,DLSTACK_PROTO##n##_) = ptr; \ float ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = rb_float_new(ret); \ @@ -463,7 +267,7 @@ break; case DLTYPE_DOUBLE: #define CASE(n) case n: { \ - DECL_FUNC_STDCALL(f,double,DLSTACK_PROTO##n##_) = cfunc->ptr; \ + DECL_FUNC_STDCALL(f,double,DLSTACK_PROTO##n##_) = ptr; \ double ret; \ ret = f(DLSTACK_ARGS##n(stack)); \ result = rb_float_new(ret); \ @@ -472,7 +276,7 @@ #undef CASE break; default: - rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type); + rb_raise(rb_eDLTypeError, "unknown type %d", type); } } #endif @@ -483,50 +287,21 @@ #else "unsupported call type: %llx", #endif - cfunc->calltype); + calltype); } - rb_dl_set_last_error(self, INT2NUM(errno)); + rb_funcall(rb_cDLCFunc, rb_intern("last_error="), 1, INT2NUM(errno)); #if defined(HAVE_WINDOWS_H) - rb_dl_set_win32_last_error(self, INT2NUM(GetLastError())); + rb_funcall(rb_cDLCFunc, rb_intern("win32_last_error="), 1, 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(void) { - 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); } Index: ext/dl/dl.h =================================================================== --- ext/dl/dl.h (revision 24783) +++ ext/dl/dl.h (working copy) @@ -194,12 +194,6 @@ }; -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) @@ -212,7 +206,6 @@ }; #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); Index: ext/dl/lib/dl/cfunc.rb =================================================================== --- ext/dl/lib/dl/cfunc.rb (revision 0) +++ ext/dl/lib/dl/cfunc.rb (revision 0) @@ -0,0 +1,46 @@ +module DL + class CFunc + attr_reader :name + attr_accessor :calltype + attr_accessor :ctype + attr_accessor :ptr + + ### + # Set the +errno+ for the last error + def self.last_error= errno + Thread.current[:__DL2_LAST_ERROR__] = errno + end + + ### + # Get the +errno+ of the last error + def self.last_error + Thread.current[:__DL2_LAST_ERROR__] + end + + ### + # Set the +errno+ for the last error. ONLY USED ON WIN32! + def self.win32_last_error= errno + Thread.current[:__DL2_WIN32_LAST_ERROR__] = errno + end + + ### + # Get the +errno+ of the last error. ONLY USED ON WIN32! + def self.win32_last_error + Thread.current[:__DL2_WIN32_LAST_ERROR__] + end + + def initialize ptr, ctype = TYPE_VOID, name = nil, calltype = :cdecl + @name = name && name.dup.taint + @calltype = calltype + @ctype = ctype + @ptr = ptr + end + + def to_s + "#<#{self.class}:#{sprintf("0x%x", object_id)} ptr=#{sprintf("0x%x", ptr)} type=#{ctype} name=#{name.inspect}>" + end + + alias :inspect :to_s + alias :to_i :ptr + end +end Index: ext/dl/lib/dl/func.rb =================================================================== --- ext/dl/lib/dl/func.rb (revision 24783) +++ ext/dl/lib/dl/func.rb (working copy) @@ -2,6 +2,7 @@ require 'dl/callback' require 'dl/stack' require 'dl/value' +require 'dl/cfunc' require 'thread' module DL Index: ext/dl/cptr.c =================================================================== --- ext/dl/cptr.c (revision 24783) +++ ext/dl/cptr.c (working copy) @@ -16,7 +16,7 @@ return NULL; } if (rb_dlcfunc_kind_p(func)) { - return RCFUNC_DATA(func)->ptr; + return NUM2PTR(rb_funcall(func, rb_intern("ptr"), 0)); } return NUM2PTR(rb_Integer(func)); } Index: test/dl/test_cfunc.rb =================================================================== --- test/dl/test_cfunc.rb (revision 0) +++ test/dl/test_cfunc.rb (revision 0) @@ -0,0 +1,58 @@ +require '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_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 + assert @cf.to_s.tainted?, 'to_s should be tainted' + assert_match(/ptr=#{sprintf("0x%x", @cf.ptr)}/, @cf.to_s) + assert_match(/name=#{@cf.name.inspect}/, @cf.to_s) + assert_match(/type=#{@cf.ctype}/, @cf.to_s) + end + + def test_inspect + assert_equal @cf.inspect, @cf.to_s + end + + def test_to_i + assert_equal @cf.to_i, @cf.ptr + end + + def test_last_error + f = Function.new(@cf, [TYPE_VOIDP, TYPE_VOIDP]) + assert_nil CFunc.last_error + str = f.call("000", "123") + assert_not_nil CFunc.last_error + end + end +end Index: test/dl/test_cptr.rb =================================================================== --- test/dl/test_cptr.rb (revision 0) +++ test/dl/test_cptr.rb (revision 0) @@ -0,0 +1,18 @@ +require 'test_base' + +module DL + class TestCPtr < TestBase + def test_free + ptr = CPtr.malloc(4) + assert_nil ptr.free + end + + def test_free= + free = CFunc.new(@libc['free'], TYPE_VOID, 'free') + ptr = CPtr.malloc(4) + ptr.free = free + + assert_equal free.ptr, ptr.free.ptr + end + end +end