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