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