Ruby  2.0.0p247(2013-06-27revision41674)
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 VALUE
00042 rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
00043 {
00044     VALUE argv[3];
00045 
00046     argv[0] = address;
00047     argv[1] = arg_types;
00048     argv[2] = ret_type;
00049 
00050     return rb_class_new_instance(3, argv, cFiddleFunction);
00051 }
00052 
00053 static int
00054 parse_keyword_arg_i(VALUE key, VALUE value, VALUE self)
00055 {
00056   if (key == ID2SYM(rb_intern("name"))) {
00057     rb_iv_set(self, "@name", value);
00058   } else {
00059     rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, key);
00060   }
00061   return ST_CONTINUE;
00062 }
00063 
00064 static VALUE
00065 initialize(int argc, VALUE argv[], VALUE self)
00066 {
00067     ffi_cif * cif;
00068     ffi_type **arg_types;
00069     ffi_status result;
00070     VALUE ptr, args, ret_type, abi, kwds;
00071     int i;
00072 
00073     rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds);
00074     if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI);
00075 
00076     Check_Type(args, T_ARRAY);
00077 
00078     rb_iv_set(self, "@ptr", ptr);
00079     rb_iv_set(self, "@args", args);
00080     rb_iv_set(self, "@return_type", ret_type);
00081     rb_iv_set(self, "@abi", abi);
00082 
00083     if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self);
00084 
00085     TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
00086 
00087     arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *));
00088 
00089     for (i = 0; i < RARRAY_LEN(args); i++) {
00090         int type = NUM2INT(RARRAY_PTR(args)[i]);
00091         arg_types[i] = INT2FFI_TYPE(type);
00092     }
00093     arg_types[RARRAY_LEN(args)] = NULL;
00094 
00095     result = ffi_prep_cif (
00096             cif,
00097             NUM2INT(abi),
00098             RARRAY_LENINT(args),
00099             INT2FFI_TYPE(NUM2INT(ret_type)),
00100             arg_types);
00101 
00102     if (result)
00103         rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
00104 
00105     return self;
00106 }
00107 
00108 static VALUE
00109 function_call(int argc, VALUE argv[], VALUE self)
00110 {
00111     ffi_cif * cif;
00112     fiddle_generic retval;
00113     fiddle_generic *generic_args;
00114     void **values;
00115     VALUE cfunc, types, cPointer;
00116     int i;
00117 
00118     cfunc    = rb_iv_get(self, "@ptr");
00119     types    = rb_iv_get(self, "@args");
00120     cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
00121 
00122     if(argc != RARRAY_LENINT(types)) {
00123         rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
00124                 argc, RARRAY_LENINT(types));
00125     }
00126 
00127     TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
00128 
00129     if (rb_safe_level() >= 1) {
00130         for (i = 0; i < argc; i++) {
00131             VALUE src = argv[i];
00132             if (OBJ_TAINTED(src)) {
00133                 rb_raise(rb_eSecurityError, "tainted parameter not allowed");
00134             }
00135         }
00136     }
00137 
00138     values = xcalloc((size_t)argc + 1, (size_t)sizeof(void *));
00139     generic_args = xcalloc((size_t)argc, (size_t)sizeof(fiddle_generic));
00140 
00141     for (i = 0; i < argc; i++) {
00142         VALUE type = RARRAY_PTR(types)[i];
00143         VALUE src = argv[i];
00144 
00145         if(NUM2INT(type) == TYPE_VOIDP) {
00146             if(NIL_P(src)) {
00147                 src = INT2NUM(0);
00148             } else if(cPointer != CLASS_OF(src)) {
00149                 src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
00150             }
00151             src = rb_Integer(src);
00152         }
00153 
00154         VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]);
00155         values[i] = (void *)&generic_args[i];
00156     }
00157     values[argc] = NULL;
00158 
00159     ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values);
00160 
00161     rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
00162 #if defined(_WIN32)
00163     rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
00164 #endif
00165 
00166     xfree(values);
00167     xfree(generic_args);
00168 
00169     return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval);
00170 }
00171 
00172 void
00173 Init_fiddle_function(void)
00174 {
00175     /*
00176      * Document-class: Fiddle::Function
00177      *
00178      * == Description
00179      *
00180      * A representation of a C function
00181      *
00182      * == Examples
00183      *
00184      * === 'strcpy'
00185      *
00186      *   @libc = Fiddle.dlopen "/lib/libc.so.6"
00187      *      #=> #<Fiddle::Handle:0x00000001d7a8d8>
00188      *   f = Fiddle::Function.new(
00189      *     @libc['strcpy'],
00190      *     [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
00191      *     Fiddle::TYPE_VOIDP)
00192      *      #=> #<Fiddle::Function:0x00000001d8ee00>
00193      *   buff = "000"
00194      *      #=> "000"
00195      *   str = f.call(buff, "123")
00196      *      #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
00197      *   str.to_s
00198      *   => "123"
00199      *
00200      * === ABI check
00201      *
00202      *   @libc = DL.dlopen "/lib/libc.so.6"
00203      *      #=> #<Fiddle::Handle:0x00000001d7a8d8>
00204      *   f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
00205      *      #=> #<Fiddle::Function:0x00000001d8ee00>
00206      *   f.abi == Fiddle::Function::DEFAULT
00207      *      #=> true
00208      */
00209     cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
00210 
00211     /*
00212      * Document-const: DEFAULT
00213      *
00214      * Default ABI
00215      *
00216      */
00217     rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
00218 
00219 #ifdef HAVE_CONST_FFI_STDCALL
00220     /*
00221      * Document-const: STDCALL
00222      *
00223      * FFI implementation of WIN32 stdcall convention
00224      *
00225      */
00226     rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
00227 #endif
00228 
00229     rb_define_alloc_func(cFiddleFunction, allocate);
00230 
00231     /*
00232      * Document-method: call
00233      *
00234      * Calls the constructed Function, with +args+
00235      *
00236      * For an example see Fiddle::Function
00237      *
00238      */
00239     rb_define_method(cFiddleFunction, "call", function_call, -1);
00240 
00241     /*
00242      * Document-method: new
00243      * call-seq: new(ptr, args, ret_type, abi = DEFAULT)
00244      *
00245      * Constructs a Function object.
00246      * * +ptr+ is a referenced function, of a Fiddle::Handle
00247      * * +args+ is an Array of arguments, passed to the +ptr+ function
00248      * * +ret_type+ is the return type of the function
00249      * * +abi+ is the ABI of the function
00250      *
00251      */
00252     rb_define_method(cFiddleFunction, "initialize", initialize, -1);
00253 }
00254 /* vim: set noet sws=4 sw=4: */
00255