ext/dl/cfunc.c

Go to the documentation of this file.
00001 /* -*- C -*-
00002  * $Id: cfunc.c 30559 2011-01-16 06:30:33Z yugui $
00003  */
00004 
00005 #include <ruby.h>
00006 #include <errno.h>
00007 #include "dl.h"
00008 
00009 VALUE rb_big2ulong_pack(VALUE x);
00010 
00011 VALUE rb_cDLCFunc;
00012 
00013 static ID id_last_error;
00014 
00015 static VALUE
00016 rb_dl_get_last_error(VALUE self)
00017 {
00018     return rb_thread_local_aref(rb_thread_current(), id_last_error);
00019 }
00020 
00021 static VALUE
00022 rb_dl_set_last_error(VALUE self, VALUE val)
00023 {
00024     rb_thread_local_aset(rb_thread_current(), id_last_error, val);
00025     return Qnil;
00026 }
00027 
00028 #if defined(_WIN32)
00029 #include <windows.h>
00030 static ID id_win32_last_error;
00031 
00032 static VALUE
00033 rb_dl_get_win32_last_error(VALUE self)
00034 {
00035     return rb_thread_local_aref(rb_thread_current(), id_win32_last_error);
00036 }
00037 
00038 static VALUE
00039 rb_dl_set_win32_last_error(VALUE self, VALUE val)
00040 {
00041     rb_thread_local_aset(rb_thread_current(), id_win32_last_error, val);
00042     return Qnil;
00043 }
00044 #endif
00045 
00046 
00047 static void
00048 dlcfunc_free(void *ptr)
00049 {
00050     struct cfunc_data *data = ptr;
00051     if( data->name ){
00052         xfree(data->name);
00053     }
00054     xfree(data);
00055 }
00056 
00057 static size_t
00058 dlcfunc_memsize(const void *ptr)
00059 {
00060     const struct cfunc_data *data = ptr;
00061     size_t size = 0;
00062     if( data ){
00063         size += sizeof(*data);
00064         if( data->name ){
00065             size += strlen(data->name) + 1;
00066         }
00067     }
00068     return size;
00069 }
00070 
00071 const rb_data_type_t dlcfunc_data_type = {
00072     "dl/cfunc",
00073     0, dlcfunc_free, dlcfunc_memsize,
00074 };
00075 
00076 VALUE
00077 rb_dlcfunc_new(void (*func)(), int type, const char *name, ID calltype)
00078 {
00079     VALUE val;
00080     struct cfunc_data *data;
00081 
00082     rb_secure(4);
00083     if( func ){
00084         val = TypedData_Make_Struct(rb_cDLCFunc, struct cfunc_data, &dlcfunc_data_type, data);
00085         data->ptr  = (void *)(VALUE)func;
00086         data->name = name ? strdup(name) : NULL;
00087         data->type = type;
00088         data->calltype = calltype;
00089     }
00090     else{
00091         val = Qnil;
00092     }
00093 
00094     return val;
00095 }
00096 
00097 void *
00098 rb_dlcfunc2ptr(VALUE val)
00099 {
00100     struct cfunc_data *data;
00101     void * func;
00102 
00103     if( rb_typeddata_is_kind_of(val, &dlcfunc_data_type) ){
00104         data = DATA_PTR(val);
00105         func = data->ptr;
00106     }
00107     else if( val == Qnil ){
00108         func = NULL;
00109     }
00110     else{
00111         rb_raise(rb_eTypeError, "DL::CFunc was expected");
00112     }
00113 
00114     return func;
00115 }
00116 
00117 static VALUE
00118 rb_dlcfunc_s_allocate(VALUE klass)
00119 {
00120     VALUE obj;
00121     struct cfunc_data *data;
00122 
00123     obj = TypedData_Make_Struct(klass, struct cfunc_data, &dlcfunc_data_type, data);
00124     data->ptr  = 0;
00125     data->name = 0;
00126     data->type = 0;
00127     data->calltype = CFUNC_CDECL;
00128 
00129     return obj;
00130 }
00131 
00132 int
00133 rb_dlcfunc_kind_p(VALUE func)
00134 {
00135     return rb_typeddata_is_kind_of(func, &dlcfunc_data_type);
00136 }
00137 
00138 /*
00139  * call-seq:
00140  *    DL::CFunc.new(address, type=DL::TYPE_VOID, name=nil, calltype=:cdecl)
00141  *
00142  * Create a new function that points to +address+ with an optional return type
00143  * of +type+, a name of +name+ and a calltype of +calltype+.
00144  */
00145 static VALUE
00146 rb_dlcfunc_initialize(int argc, VALUE argv[], VALUE self)
00147 {
00148     VALUE addr, name, type, calltype;
00149     struct cfunc_data *data;
00150     void *saddr;
00151     const char *sname;
00152 
00153     rb_scan_args(argc, argv, "13", &addr, &type, &name, &calltype);
00154 
00155     saddr = (void*)(NUM2PTR(rb_Integer(addr)));
00156     sname = NIL_P(name) ? NULL : StringValuePtr(name);
00157 
00158     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, data);
00159     if( data->name ) xfree(data->name);
00160     data->ptr  = saddr;
00161     data->name = sname ? strdup(sname) : 0;
00162     data->type = NIL_P(type) ? DLTYPE_VOID : NUM2INT(type);
00163     data->calltype = NIL_P(calltype) ? CFUNC_CDECL : SYM2ID(calltype);
00164 
00165     return Qnil;
00166 }
00167 
00168 /*
00169  * call-seq:
00170  *    name  => str
00171  *
00172  * Get the name of this function
00173  */
00174 static VALUE
00175 rb_dlcfunc_name(VALUE self)
00176 {
00177     struct cfunc_data *cfunc;
00178 
00179     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00180     return cfunc->name ? rb_tainted_str_new2(cfunc->name) : Qnil;
00181 }
00182 
00183 /*
00184  * call-seq:
00185  *    cfunc.ctype   => num
00186  *
00187  * Get the C function return value type.  See DL for a list of constants
00188  * corresponding to this method's return value.
00189  */
00190 static VALUE
00191 rb_dlcfunc_ctype(VALUE self)
00192 {
00193     struct cfunc_data *cfunc;
00194 
00195     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00196     return INT2NUM(cfunc->type);
00197 }
00198 
00199 /*
00200  * call-seq:
00201  *    cfunc.ctype = type
00202  *
00203  * Set the C function return value type to +type+.
00204  */
00205 static VALUE
00206 rb_dlcfunc_set_ctype(VALUE self, VALUE ctype)
00207 {
00208     struct cfunc_data *cfunc;
00209 
00210     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00211     cfunc->type = NUM2INT(ctype);
00212     return ctype;
00213 }
00214 
00215 /*
00216  * call-seq:
00217  *    cfunc.calltype    => symbol
00218  *
00219  * Get the call type of this function.
00220  */
00221 static VALUE
00222 rb_dlcfunc_calltype(VALUE self)
00223 {
00224     struct cfunc_data *cfunc;
00225 
00226     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00227     return ID2SYM(cfunc->calltype);
00228 }
00229 
00230 /*
00231  * call-seq:
00232  *    cfunc.calltype = symbol
00233  *
00234  * Set the call type for this function.
00235  */
00236 static VALUE
00237 rb_dlcfunc_set_calltype(VALUE self, VALUE sym)
00238 {
00239     struct cfunc_data *cfunc;
00240 
00241     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00242     cfunc->calltype = SYM2ID(sym);
00243     return sym;
00244 }
00245 
00246 /*
00247  * call-seq:
00248  *    cfunc.ptr
00249  *
00250  * Get the underlying function pointer as a DL::CPtr object.
00251  */
00252 static VALUE
00253 rb_dlcfunc_ptr(VALUE self)
00254 {
00255     struct cfunc_data *cfunc;
00256 
00257     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00258     return PTR2NUM(cfunc->ptr);
00259 }
00260 
00261 /*
00262  * call-seq:
00263  *    cfunc.ptr = pointer
00264  *
00265  * Set the underlying function pointer to a DL::CPtr named +pointer+.
00266  */
00267 static VALUE
00268 rb_dlcfunc_set_ptr(VALUE self, VALUE addr)
00269 {
00270     struct cfunc_data *cfunc;
00271 
00272     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00273     cfunc->ptr = NUM2PTR(addr);
00274 
00275     return Qnil;
00276 }
00277 
00278 /*
00279  * call-seq: inspect
00280  *
00281  * Returns a string formatted with an easily readable representation of the
00282  * internal state of the DL::CFunc
00283  */
00284 static VALUE
00285 rb_dlcfunc_inspect(VALUE self)
00286 {
00287     VALUE val;
00288     struct cfunc_data *cfunc;
00289 
00290     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00291 
00292     val = rb_sprintf("#<DL::CFunc:%p ptr=%p type=%d name='%s'>",
00293              cfunc,
00294              cfunc->ptr,
00295              cfunc->type,
00296              cfunc->name ? cfunc->name : "");
00297     OBJ_TAINT(val);
00298     return val;
00299 }
00300 
00301 
00302 # define DECL_FUNC_CDECL(f,ret,args,val) \
00303     ret (FUNC_CDECL(*f))(args) = (ret (FUNC_CDECL(*))(args))(VALUE)(val)
00304 #ifdef FUNC_STDCALL
00305 # define DECL_FUNC_STDCALL(f,ret,args,val) \
00306     ret (FUNC_STDCALL(*f))(args) = (ret (FUNC_STDCALL(*))(args))(VALUE)(val)
00307 #endif
00308 
00309 #define CALL_CASE switch( RARRAY_LEN(ary) ){ \
00310   CASE(0); break; \
00311   CASE(1); break; CASE(2); break; CASE(3); break; CASE(4); break; CASE(5); break; \
00312   CASE(6); break; CASE(7); break; CASE(8); break; CASE(9); break; CASE(10);break; \
00313   CASE(11);break; CASE(12);break; CASE(13);break; CASE(14);break; CASE(15);break; \
00314   CASE(16);break; CASE(17);break; CASE(18);break; CASE(19);break; CASE(20);break; \
00315   default: rb_raise(rb_eArgError, "too many arguments"); \
00316 }
00317 
00318 
00319 #if defined(_MSC_VER) && defined(_M_AMD64) && _MSC_VER == 1500
00320 # pragma optimize("", off)
00321 #endif
00322 /*
00323  * call-seq:
00324  *    dlcfunc.call(ary)   => some_value
00325  *    dlcfunc[ary]        => some_value
00326  *
00327  * Calls the function pointer passing in +ary+ as values to the underlying
00328  * C function.  The return value depends on the ctype.
00329  */
00330 static VALUE
00331 rb_dlcfunc_call(VALUE self, VALUE ary)
00332 {
00333     struct cfunc_data *cfunc;
00334     int i;
00335     DLSTACK_TYPE stack[DLSTACK_SIZE];
00336     VALUE result = Qnil;
00337 
00338     rb_secure_update(self);
00339 
00340     memset(stack, 0, sizeof(DLSTACK_TYPE) * DLSTACK_SIZE);
00341     Check_Type(ary, T_ARRAY);
00342 
00343     TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00344 
00345     if( cfunc->ptr == 0 ){
00346         rb_raise(rb_eDLError, "can't call null-function");
00347         return Qnil;
00348     }
00349 
00350     for( i = 0; i < RARRAY_LEN(ary); i++ ){
00351         VALUE arg;
00352         if( i >= DLSTACK_SIZE ){
00353             rb_raise(rb_eDLError, "too many arguments (stack overflow)");
00354         }
00355         arg = rb_to_int(RARRAY_PTR(ary)[i]);
00356         rb_check_safe_obj(arg);
00357         if (FIXNUM_P(arg)) {
00358             stack[i] = (DLSTACK_TYPE)FIX2LONG(arg);
00359         }
00360         else if (RB_TYPE_P(arg, T_BIGNUM)) {
00361             stack[i] = (DLSTACK_TYPE)rb_big2ulong_pack(arg);
00362         }
00363         else {
00364             Check_Type(arg, T_FIXNUM);
00365         }
00366     }
00367 
00368     /* calltype == CFUNC_CDECL */
00369     if( cfunc->calltype == CFUNC_CDECL
00370 #ifndef FUNC_STDCALL
00371         || cfunc->calltype == CFUNC_STDCALL
00372 #endif
00373         ){
00374         switch( cfunc->type ){
00375         case DLTYPE_VOID:
00376 #define CASE(n) case n: { \
00377             DECL_FUNC_CDECL(f,void,DLSTACK_PROTO##n,cfunc->ptr); \
00378             f(DLSTACK_ARGS##n(stack)); \
00379             result = Qnil; \
00380 }
00381             CALL_CASE;
00382 #undef CASE
00383             break;
00384         case DLTYPE_VOIDP:
00385 #define CASE(n) case n: { \
00386             DECL_FUNC_CDECL(f,void*,DLSTACK_PROTO##n,cfunc->ptr); \
00387             void * ret; \
00388             ret = f(DLSTACK_ARGS##n(stack)); \
00389             result = PTR2NUM(ret); \
00390 }
00391             CALL_CASE;
00392 #undef CASE
00393             break;
00394         case DLTYPE_CHAR:
00395 #define CASE(n) case n: { \
00396             DECL_FUNC_CDECL(f,char,DLSTACK_PROTO##n,cfunc->ptr); \
00397             char ret; \
00398             ret = f(DLSTACK_ARGS##n(stack)); \
00399             result = CHR2FIX(ret); \
00400 }
00401             CALL_CASE;
00402 #undef CASE
00403             break;
00404         case DLTYPE_SHORT:
00405 #define CASE(n) case n: { \
00406             DECL_FUNC_CDECL(f,short,DLSTACK_PROTO##n,cfunc->ptr); \
00407             short ret; \
00408             ret = f(DLSTACK_ARGS##n(stack)); \
00409             result = INT2NUM((int)ret); \
00410 }
00411             CALL_CASE;
00412 #undef CASE
00413             break;
00414         case DLTYPE_INT:
00415 #define CASE(n) case n: { \
00416             DECL_FUNC_CDECL(f,int,DLSTACK_PROTO##n,cfunc->ptr); \
00417             int ret; \
00418             ret = f(DLSTACK_ARGS##n(stack)); \
00419             result = INT2NUM(ret); \
00420 }
00421             CALL_CASE;
00422 #undef CASE
00423             break;
00424         case DLTYPE_LONG:
00425 #define CASE(n) case n: { \
00426             DECL_FUNC_CDECL(f,long,DLSTACK_PROTO##n,cfunc->ptr); \
00427             long ret; \
00428             ret = f(DLSTACK_ARGS##n(stack)); \
00429             result = LONG2NUM(ret); \
00430 }
00431             CALL_CASE;
00432 #undef CASE
00433             break;
00434 #if HAVE_LONG_LONG  /* used in ruby.h */
00435         case DLTYPE_LONG_LONG:
00436 #define CASE(n) case n: { \
00437             DECL_FUNC_CDECL(f,LONG_LONG,DLSTACK_PROTO##n,cfunc->ptr); \
00438             LONG_LONG ret; \
00439             ret = f(DLSTACK_ARGS##n(stack)); \
00440             result = LL2NUM(ret); \
00441 }
00442             CALL_CASE;
00443 #undef CASE
00444             break;
00445 #endif
00446         case DLTYPE_FLOAT:
00447 #define CASE(n) case n: { \
00448             DECL_FUNC_CDECL(f,float,DLSTACK_PROTO##n,cfunc->ptr); \
00449             float ret; \
00450             ret = f(DLSTACK_ARGS##n(stack)); \
00451             result = rb_float_new(ret); \
00452 }
00453             CALL_CASE;
00454 #undef CASE
00455             break;
00456         case DLTYPE_DOUBLE:
00457 #define CASE(n) case n: { \
00458             DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n,cfunc->ptr); \
00459             double ret; \
00460             ret = f(DLSTACK_ARGS##n(stack)); \
00461             result = rb_float_new(ret); \
00462 }
00463             CALL_CASE;
00464 #undef CASE
00465             break;
00466         default:
00467             rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type);
00468         }
00469     }
00470 #ifdef FUNC_STDCALL
00471     else if( cfunc->calltype == CFUNC_STDCALL ){
00472         /* calltype == CFUNC_STDCALL */
00473         switch( cfunc->type ){
00474         case DLTYPE_VOID:
00475 #define CASE(n) case n: { \
00476             DECL_FUNC_STDCALL(f,void,DLSTACK_PROTO##n##_,cfunc->ptr); \
00477             f(DLSTACK_ARGS##n(stack)); \
00478             result = Qnil; \
00479 }
00480             CALL_CASE;
00481 #undef CASE
00482             break;
00483         case DLTYPE_VOIDP:
00484 #define CASE(n) case n: { \
00485             DECL_FUNC_STDCALL(f,void*,DLSTACK_PROTO##n##_,cfunc->ptr); \
00486             void * ret; \
00487             ret = f(DLSTACK_ARGS##n(stack)); \
00488             result = PTR2NUM(ret); \
00489 }
00490             CALL_CASE;
00491 #undef CASE
00492             break;
00493         case DLTYPE_CHAR:
00494 #define CASE(n) case n: { \
00495             DECL_FUNC_STDCALL(f,char,DLSTACK_PROTO##n##_,cfunc->ptr); \
00496             char ret; \
00497             ret = f(DLSTACK_ARGS##n(stack)); \
00498             result = CHR2FIX(ret); \
00499 }
00500             CALL_CASE;
00501 #undef CASE
00502             break;
00503         case DLTYPE_SHORT:
00504 #define CASE(n) case n: { \
00505             DECL_FUNC_STDCALL(f,short,DLSTACK_PROTO##n##_,cfunc->ptr); \
00506             short ret; \
00507             ret = f(DLSTACK_ARGS##n(stack)); \
00508             result = INT2NUM((int)ret); \
00509 }
00510             CALL_CASE;
00511 #undef CASE
00512             break;
00513         case DLTYPE_INT:
00514 #define CASE(n) case n: { \
00515             DECL_FUNC_STDCALL(f,int,DLSTACK_PROTO##n##_,cfunc->ptr); \
00516             int ret; \
00517             ret = f(DLSTACK_ARGS##n(stack)); \
00518             result = INT2NUM(ret); \
00519 }
00520             CALL_CASE;
00521 #undef CASE
00522             break;
00523         case DLTYPE_LONG:
00524 #define CASE(n) case n: { \
00525             DECL_FUNC_STDCALL(f,long,DLSTACK_PROTO##n##_,cfunc->ptr); \
00526             long ret; \
00527             ret = f(DLSTACK_ARGS##n(stack)); \
00528             result = LONG2NUM(ret); \
00529 }
00530             CALL_CASE;
00531 #undef CASE
00532             break;
00533 #if HAVE_LONG_LONG  /* used in ruby.h */
00534         case DLTYPE_LONG_LONG:
00535 #define CASE(n) case n: { \
00536             DECL_FUNC_STDCALL(f,LONG_LONG,DLSTACK_PROTO##n##_,cfunc->ptr); \
00537             LONG_LONG ret; \
00538             ret = f(DLSTACK_ARGS##n(stack)); \
00539             result = LL2NUM(ret); \
00540 }
00541             CALL_CASE;
00542 #undef CASE
00543             break;
00544 #endif
00545         case DLTYPE_FLOAT:
00546 #define CASE(n) case n: { \
00547             DECL_FUNC_STDCALL(f,float,DLSTACK_PROTO##n##_,cfunc->ptr); \
00548             float ret; \
00549             ret = f(DLSTACK_ARGS##n(stack)); \
00550             result = rb_float_new(ret); \
00551 }
00552             CALL_CASE;
00553 #undef CASE
00554             break;
00555         case DLTYPE_DOUBLE:
00556 #define CASE(n) case n: { \
00557             DECL_FUNC_STDCALL(f,double,DLSTACK_PROTO##n##_,cfunc->ptr); \
00558             double ret; \
00559             ret = f(DLSTACK_ARGS##n(stack)); \
00560             result = rb_float_new(ret); \
00561 }
00562             CALL_CASE;
00563 #undef CASE
00564             break;
00565         default:
00566             rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type);
00567         }
00568     }
00569 #endif
00570     else{
00571         rb_raise(rb_eDLError,
00572 #ifndef LONG_LONG_VALUE
00573                  "unsupported call type: %lx",
00574 #else
00575                  "unsupported call type: %llx",
00576 #endif
00577                  cfunc->calltype);
00578     }
00579 
00580     rb_dl_set_last_error(self, INT2NUM(errno));
00581 #if defined(_WIN32)
00582     rb_dl_set_win32_last_error(self, INT2NUM(GetLastError()));
00583 #endif
00584 
00585     return result;
00586 }
00587 #if defined(_MSC_VER) && defined(_M_AMD64) && _MSC_VER == 1500
00588 # pragma optimize("", on)
00589 #endif
00590 
00591 /*
00592  * call-seq:
00593  *    dlfunc.to_i   => integer
00594  *
00595  * Returns the memory location of this function pointer as an integer.
00596  */
00597 static VALUE
00598 rb_dlcfunc_to_i(VALUE self)
00599 {
00600   struct cfunc_data *cfunc;
00601 
00602   TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);
00603   return PTR2NUM(cfunc->ptr);
00604 }
00605 
00606 void
00607 Init_dlcfunc(void)
00608 {
00609     id_last_error = rb_intern("__DL2_LAST_ERROR__");
00610 #if defined(_WIN32)
00611     id_win32_last_error = rb_intern("__DL2_WIN32_LAST_ERROR__");
00612 #endif
00613     rb_cDLCFunc = rb_define_class_under(rb_mDL, "CFunc", rb_cObject);
00614     rb_define_alloc_func(rb_cDLCFunc, rb_dlcfunc_s_allocate);
00615     rb_define_module_function(rb_cDLCFunc, "last_error", rb_dl_get_last_error, 0);
00616 #if defined(_WIN32)
00617     rb_define_module_function(rb_cDLCFunc, "win32_last_error", rb_dl_get_win32_last_error, 0);
00618 #endif
00619     rb_define_method(rb_cDLCFunc, "initialize", rb_dlcfunc_initialize, -1);
00620     rb_define_method(rb_cDLCFunc, "call", rb_dlcfunc_call, 1);
00621     rb_define_method(rb_cDLCFunc, "[]",   rb_dlcfunc_call, 1);
00622     rb_define_method(rb_cDLCFunc, "name", rb_dlcfunc_name, 0);
00623     rb_define_method(rb_cDLCFunc, "ctype", rb_dlcfunc_ctype, 0);
00624     rb_define_method(rb_cDLCFunc, "ctype=", rb_dlcfunc_set_ctype, 1);
00625     rb_define_method(rb_cDLCFunc, "calltype", rb_dlcfunc_calltype, 0);
00626     rb_define_method(rb_cDLCFunc, "calltype=", rb_dlcfunc_set_calltype, 1);
00627     rb_define_method(rb_cDLCFunc, "ptr",  rb_dlcfunc_ptr, 0);
00628     rb_define_method(rb_cDLCFunc, "ptr=", rb_dlcfunc_set_ptr, 1);
00629     rb_define_method(rb_cDLCFunc, "inspect", rb_dlcfunc_inspect, 0);
00630     rb_define_method(rb_cDLCFunc, "to_s", rb_dlcfunc_inspect, 0);
00631     rb_define_method(rb_cDLCFunc, "to_i", rb_dlcfunc_to_i, 0);
00632 }
00633 

Generated on Wed Aug 10 09:16:59 2011 for Ruby by  doxygen 1.4.7