Ruby  1.9.3p448(2013-06-27revision41675)
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     if (rb_safe_level() >= 1) {
00105         for (i = 0; i < argc; i++) {
00106             VALUE src = argv[i];
00107             if (OBJ_TAINTED(src)) {
00108                 rb_raise(rb_eSecurityError, "tainted parameter not allowed");
00109             }
00110         }
00111     }
00112 
00113     values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
00114     generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
00115 
00116     for (i = 0; i < argc; i++) {
00117         VALUE type = RARRAY_PTR(types)[i];
00118         VALUE src = argv[i];
00119 
00120         if(NUM2INT(type) == TYPE_VOIDP) {
00121             if(NIL_P(src)) {
00122                 src = INT2NUM(0);
00123             } else if(cPointer != CLASS_OF(src)) {
00124                 src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
00125             }
00126             src = rb_Integer(src);
00127         }
00128 
00129         VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
00130         values[i] = (void *)&generic_args[i];
00131     }
00132     values[argc] = NULL;
00133 
00134     ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
00135 
00136     rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
00137 #if defined(_WIN32)
00138     rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
00139 #endif
00140 
00141     xfree(values);
00142     xfree(generic_args);
00143 
00144     return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
00145 }
00146 
00147 void
00148 Init_fiddle_function(void)
00149 {
00150     /*
00151      * Document-class: Fiddle::Function
00152      *
00153      * == Description
00154      *
00155      * A representation of a C function
00156      *
00157      * == Examples
00158      *
00159      * === 'strcpy'
00160      *
00161      *   @libc = DL.dlopen "/lib/libc.so.6"
00162      *   => #<DL::Handle:0x00000001d7a8d8>
00163      *   f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
00164      *   => #<Fiddle::Function:0x00000001d8ee00>
00165      *   buff = "000"
00166      *   => "000"
00167      *   str = f.call(buff, "123")
00168      *   => #<DL::CPtr:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
00169      *   str.to_s
00170      *   => "123"
00171      *
00172      * === ABI check
00173      *
00174      *   @libc = DL.dlopen "/lib/libc.so.6"
00175      *   => #<DL::Handle:0x00000001d7a8d8>
00176      *   f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
00177      *   => #<Fiddle::Function:0x00000001d8ee00>
00178      *   f.abi == Fiddle::Function::DEFAULT
00179      *   => true
00180      */
00181     cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
00182 
00183     /*
00184      * Document-const: DEFAULT
00185      *
00186      * Default ABI
00187      *
00188      */
00189     rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
00190 
00191 #ifdef HAVE_CONST_FFI_STDCALL
00192     /*
00193      * Document-const: STDCALL
00194      *
00195      * FFI implementation of WIN32 stdcall convention
00196      *
00197      */
00198     rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
00199 #endif
00200 
00201     rb_define_alloc_func(cFiddleFunction, allocate);
00202 
00203     /*
00204      * Document-method: call
00205      *
00206      * Calls the constructed Function, with +args+
00207      *
00208      * For an example see Fiddle::Function
00209      *
00210      */
00211     rb_define_method(cFiddleFunction, "call", function_call, -1);
00212 
00213     /*
00214      * Document-method: new
00215      * call-seq: new(ptr, args, ret_type, abi = DEFAULT)
00216      *
00217      * Constructs a Function object.
00218      * * +ptr+ is a referenced function, of a DL::Handle
00219      * * +args+ is an Array of arguments, passed to the +ptr+ function
00220      * * +ret_type+ is the return type of the function
00221      * * +abi+ is the ABI of the function
00222      *
00223      */
00224     rb_define_method(cFiddleFunction, "initialize", initialize, -1);
00225 }
00226 /* vim: set noet sws=4 sw=4: */
00227