Ruby 1.9.3p327(2012-11-10revision37606)
ext/fiddle/function.c
Go to the documentation of this file.
00001 #include <fiddle.h>
00002 
00003 VALUE cFiddleFunction;
00004 
00005 static void
00006 deallocate(void *p)
00007 {
00008     ffi_cif *ptr = p;
00009     if (ptr->arg_types) xfree(ptr->arg_types);
00010     xfree(ptr);
00011 }
00012 
00013 static size_t
00014 function_memsize(const void *p)
00015 {
00016     /* const */ffi_cif *ptr = (ffi_cif *)p;
00017     size_t size = 0;
00018 
00019     if (ptr) {
00020         size += sizeof(*ptr);
00021 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
00022         size += ffi_raw_size(ptr);
00023 #endif
00024     }
00025     return size;
00026 }
00027 
00028 const rb_data_type_t function_data_type = {
00029     "fiddle/function",
00030     {0, deallocate, function_memsize,},
00031 };
00032 
00033 static VALUE
00034 allocate(VALUE klass)
00035 {
00036     ffi_cif * cif;
00037 
00038     return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
00039 }
00040 
00041 static VALUE
00042 initialize(int argc, VALUE argv[], VALUE self)
00043 {
00044     ffi_cif * cif;
00045     ffi_type **arg_types;
00046     ffi_status result;
00047     VALUE ptr, args, ret_type, abi;
00048     int i;
00049 
00050     rb_scan_args(argc, argv, "31", &ptr, &args, &ret_type, &abi);
00051     if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
00052 
00053     Check_Type(args, T_ARRAY);
00054 
00055     rb_iv_set(self, "@ptr", ptr);
00056     rb_iv_set(self, "@args", args);
00057     rb_iv_set(self, "@return_type", ret_type);
00058     rb_iv_set(self, "@abi", abi);
00059 
00060     TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
00061 
00062     arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
00063 
00064     for (i = 0; i < RARRAY_LEN(args); i++) {
00065         int type = NUM2INT(RARRAY_PTR(args)[i]);
00066         arg_types[i] = INT2FFI_TYPE(type);
00067     }
00068     arg_types[RARRAY_LEN(args)] = NULL;
00069 
00070     result = ffi_prep_cif (
00071             cif,
00072             NUM2INT(abi),
00073             RARRAY_LENINT(args),
00074             INT2FFI_TYPE(NUM2INT(ret_type)),
00075             arg_types);
00076 
00077     if (result)
00078         rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
00079 
00080     return self;
00081 }
00082 
00083 static VALUE
00084 function_call(int argc, VALUE argv[], VALUE self)
00085 {
00086     ffi_cif * cif;
00087     fiddle_generic retval;
00088     fiddle_generic *generic_args;
00089     void **values;
00090     VALUE cfunc, types, cPointer;
00091     int i;
00092 
00093     cfunc    = rb_iv_get(self, "@ptr");
00094     types    = rb_iv_get(self, "@args");
00095     cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
00096 
00097     if(argc != RARRAY_LENINT(types)) {
00098         rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
00099                 argc, RARRAY_LENINT(types));
00100     }
00101 
00102     TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
00103 
00104     values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
00105     generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
00106 
00107     for (i = 0; i < argc; i++) {
00108         VALUE type = RARRAY_PTR(types)[i];
00109         VALUE src = argv[i];
00110 
00111         if(NUM2INT(type) == TYPE_VOIDP) {
00112             if(NIL_P(src)) {
00113                 src = INT2NUM(0);
00114             } else if(cPointer != CLASS_OF(src)) {
00115                 src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
00116             }
00117             src = rb_Integer(src);
00118         }
00119 
00120         VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
00121         values[i] = (void *)&generic_args[i];
00122     }
00123     values[argc] = NULL;
00124 
00125     ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
00126 
00127     rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
00128 #if defined(_WIN32)
00129     rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
00130 #endif
00131 
00132     xfree(values);
00133     xfree(generic_args);
00134 
00135     return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
00136 }
00137 
00138 void
00139 Init_fiddle_function(void)
00140 {
00141     /*
00142      * Document-class: Fiddle::Function
00143      *
00144      * == Description
00145      *
00146      * A representation of a C function
00147      *
00148      * == Examples
00149      *
00150      * === 'strcpy'
00151      *
00152      *   @libc = DL.dlopen "/lib/libc.so.6"
00153      *   => #<DL::Handle:0x00000001d7a8d8>
00154      *   f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
00155      *   => #<Fiddle::Function:0x00000001d8ee00>
00156      *   buff = "000"
00157      *   => "000"
00158      *   str = f.call(buff, "123")
00159      *   => #<DL::CPtr:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
00160      *   str.to_s
00161      *   => "123"
00162      *
00163      * === ABI check
00164      *
00165      *   @libc = DL.dlopen "/lib/libc.so.6"
00166      *   => #<DL::Handle:0x00000001d7a8d8>
00167      *   f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
00168      *   => #<Fiddle::Function:0x00000001d8ee00>
00169      *   f.abi == Fiddle::Function::DEFAULT
00170      *   => true
00171      */
00172     cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
00173 
00174     /*
00175      * Document-const: DEFAULT
00176      *
00177      * Default ABI
00178      *
00179      */
00180     rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
00181 
00182 #ifdef FFI_STDCALL
00183     /*
00184      * Document-const: STDCALL
00185      *
00186      * FFI implementation of WIN32 stdcall convention
00187      *
00188      */
00189     rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
00190 #endif
00191 
00192     rb_define_alloc_func(cFiddleFunction, allocate);
00193 
00194     /*
00195      * Document-method: call
00196      *
00197      * Calls the constructed Function, with +args+
00198      *
00199      * For an example see Fiddle::Function
00200      *
00201      */
00202     rb_define_method(cFiddleFunction, "call", function_call, -1);
00203 
00204     /*
00205      * Document-method: new
00206      * call-seq: new(ptr, *args, ret_type, abi = DEFAULT)
00207      *
00208      * Constructs a Function object.
00209      * * +ptr+ is a referenced function, of a DL::Handle
00210      * * +args+ is an Array of arguments, passed to the +ptr+ function
00211      * * +ret_type+ is the return type of the function
00212      * * +abi+ is the ABI of the function
00213      *
00214      */
00215     rb_define_method(cFiddleFunction, "initialize", initialize, -1);
00216 }
00217 /* vim: set noet sws=4 sw=4: */
00218