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