Class: Fiddle::Function
Relationships & Source Files | |
Inherits: | Object |
Defined in: | ext/fiddle/function.c, ext/fiddle/pinned.c, ext/fiddle/lib/fiddle/ffi_backend.rb, ext/fiddle/lib/fiddle/function.rb |
Overview
Description
A representation of a C function
Examples
‘strcpy’
@libc = Fiddle.dlopen "/lib/libc.so.6"
#=> #<Fiddle::Handle:0x00000001d7a8d8>
f = Fiddle::Function.new(
@libc['strcpy'],
[Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
Fiddle::TYPE_VOIDP)
#=> #<Fiddle::Function:0x00000001d8ee00>
buff = "000"
#=> "000"
str = f.call(buff, "123")
#=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
str.to_s
#=> "123"
ABI check
@libc = Fiddle.dlopen "/lib/libc.so.6"
#=> #<Fiddle::Handle:0x00000001d7a8d8>
f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
#=> #<Fiddle::Function:0x00000001d8ee00>
f.abi == Fiddle::Function::DEFAULT
#=> true
Constant Summary
-
DEFAULT =
Default ABI
"default"
-
STDCALL =
FFI implementation of WIN32 stdcall convention
"stdcall"
Class Method Summary
-
.new(object) ⇒ Pinned
constructor
Alias for Closure.new.
Instance Attribute Summary
-
#abi
readonly
The ABI of the
Function
. -
#name
readonly
The name of this function.
-
#need_gvl? ⇒ Boolean
readonly
Whether GVL is needed to call this function.
-
#ptr
readonly
The address of this function.
Instance Method Summary
-
#call(*args, &block)
Calls the constructed
Function
, withargs
. - #initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil) ⇒ Function constructor
-
#to_i
The integer memory location of this function.
-
#to_proc
Turn this function in to a proc.
Constructor Details
.new(object) ⇒ Pinned
Alias for Closure.new. Create a new pinned object reference. The Pinned
instance will prevent the GC from moving object
.
#initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil) ⇒ Function
# File 'ext/fiddle/lib/fiddle/ffi_backend.rb', line 125
def initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil) if kwargs.nil? if abi.kind_of? Hash kwargs = abi abi = DEFAULT end end @name = kwargs[:name] if kwargs.kind_of? Hash @ptr, @args, @return_type, @abi = ptr, args, return_type, abi raise TypeError.new "invalid argument types" unless args.is_a?(Array) ffi_return_type = Fiddle::FFIBackend.to_ffi_type(@return_type) ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) } pointer = FFI::Pointer.new(ptr.to_i) = {convention: @abi} if ffi_args.last == FFI::Type::Builtin::VARARGS @function = FFI::VariadicInvoker.new( pointer, ffi_args, ffi_return_type, ) else @function = FFI::Function.new(ffi_return_type, ffi_args, pointer, ) end end
Instance Attribute Details
#abi (readonly)
The ABI of the Function
.
# File 'ext/fiddle/lib/fiddle/function.rb', line 5
attr_reader :abi
#name (readonly)
The name of this function
# File 'ext/fiddle/lib/fiddle/function.rb', line 11
attr_reader :name
#need_gvl? ⇒ Boolean
(readonly)
Whether GVL is needed to call this function
# File 'ext/fiddle/lib/fiddle/function.rb', line 14
def need_gvl? @need_gvl end
#ptr (readonly)
The address of this function
# File 'ext/fiddle/lib/fiddle/function.rb', line 8
attr_reader :ptr
Instance Method Details
#call(*args, &block)
Calls the constructed Function
, with args
. Caller must ensure the underlying function is called in a thread-safe manner if running in a multi-threaded process.
Note that it is not thread-safe to use this method to directly or indirectly call many Ruby C-extension APIs unless you don’t pass need_gvl: true to Fiddle::Function#new
.
For an example see Function
# File 'ext/fiddle/function.c', line 219
static VALUE function_call(int argc, VALUE argv[], VALUE self) { struct nogvl_ffi_call_args args = { 0 }; fiddle_generic *generic_args; VALUE cfunc; VALUE abi; VALUE arg_types; VALUE cPointer; VALUE is_variadic; VALUE need_gvl; int n_arg_types; int n_fixed_args = 0; int n_call_args = 0; int i; int i_call; VALUE converted_args = Qnil; VALUE alloc_buffer = 0; cfunc = rb_iv_get(self, "@ptr"); abi = rb_iv_get(self, "@abi"); arg_types = rb_iv_get(self, "@argument_types"); cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); is_variadic = rb_iv_get(self, "@is_variadic"); need_gvl = rb_iv_get(self, "@need_gvl"); n_arg_types = RARRAY_LENINT(arg_types); n_fixed_args = n_arg_types; if (RTEST(is_variadic)) { if (argc < n_arg_types) { rb_error_arity(argc, n_arg_types, UNLIMITED_ARGUMENTS); } if (((argc - n_arg_types) % 2) != 0) { rb_raise(rb_eArgError, "variadic arguments must be type and value pairs: " "%"PRIsVALUE, rb_ary_new_from_values(argc, argv)); } n_call_args = n_arg_types + ((argc - n_arg_types) / 2); } else { if (argc != n_arg_types) { rb_error_arity(argc, n_arg_types, n_arg_types); } n_call_args = n_arg_types; } Check_Max_Args("the number of arguments", n_call_args); TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif); if (is_variadic && args.cif->arg_types) { xfree(args.cif->arg_types); args.cif->arg_types = NULL; } if (!args.cif->arg_types) { VALUE fixed_arg_types = arg_types; VALUE return_type; int c_return_type; ffi_type *ffi_return_type; ffi_type **ffi_arg_types; ffi_status result; arg_types = rb_ary_dup(fixed_arg_types); for (i = n_fixed_args; i < argc; i += 2) { VALUE arg_type = argv[i]; int c_arg_type; arg_type = rb_fiddle_type_ensure(arg_type); c_arg_type = NUM2INT(arg_type); (void)INT2FFI_TYPE(c_arg_type); /* raise */ rb_ary_push(arg_types, INT2FIX(c_arg_type)); } return_type = rb_iv_get(self, "@return_type"); c_return_type = FIX2INT(return_type); ffi_return_type = INT2FFI_TYPE(c_return_type); ffi_arg_types = xcalloc(n_call_args + 1, sizeof(ffi_type *)); for (i_call = 0; i_call < n_call_args; i_call++) { VALUE arg_type; int c_arg_type; arg_type = RARRAY_AREF(arg_types, i_call); c_arg_type = FIX2INT(arg_type); ffi_arg_types[i_call] = INT2FFI_TYPE(c_arg_type); } ffi_arg_types[i_call] = NULL; if (is_variadic) { #ifdef HAVE_FFI_PREP_CIF_VAR result = ffi_prep_cif_var(args.cif, FIX2INT(abi), n_fixed_args, n_call_args, ffi_return_type, ffi_arg_types); #else /* This code is never used because ffi_prep_cif_var() * availability check is done in #initialize. */ result = FFI_BAD_TYPEDEF; #endif } else { result = ffi_prep_cif(args.cif, FIX2INT(abi), n_call_args, ffi_return_type, ffi_arg_types); } if (result != FFI_OK) { xfree(ffi_arg_types); args.cif->arg_types = NULL; rb_raise(rb_eRuntimeError, "error creating CIF %d", result); } } generic_args = ALLOCV(alloc_buffer, sizeof(fiddle_generic) * n_call_args + sizeof(void *) * (n_call_args + 1)); args.values = (void **)((char *)generic_args + sizeof(fiddle_generic) * n_call_args); for (i = 0, i_call = 0; i < argc && i_call < n_call_args; i++, i_call++) { VALUE arg_type; int c_arg_type; VALUE original_src; VALUE src; arg_type = RARRAY_AREF(arg_types, i_call); c_arg_type = FIX2INT(arg_type); if (i >= n_fixed_args) { i++; } src = argv[i]; if (c_arg_type == TYPE_VOIDP) { if (NIL_P(src)) { src = INT2FIX(0); } else if (cPointer != CLASS_OF(src)) { src = rb_funcall(cPointer, rb_intern("[]"), 1, src); if (NIL_P(converted_args)) { converted_args = rb_ary_new(); } rb_ary_push(converted_args, src); } src = rb_Integer(src); } original_src = src; VALUE2GENERIC(c_arg_type, src, &generic_args[i_call]); if (src != original_src) { if (NIL_P(converted_args)) { converted_args = rb_ary_new(); } rb_ary_push(converted_args, src); } args.values[i_call] = (void *)&generic_args[i_call]; } args.values[i_call] = NULL; args.fn = (void(*)(void))(VALUE)NUM2PTR(cfunc); if (RTEST(need_gvl)) { ffi_call(args.cif, args.fn, &(args.retval), args.values); } else { (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0); } { int errno_keep = errno; #if defined(_WIN32) DWORD error = WSAGetLastError(); int socket_error = WSAGetLastError(); rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, ULONG2NUM(error)); rb_funcall(mFiddle, rb_intern("win32_last_socket_error="), 1, INT2NUM(socket_error)); #endif rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno_keep)); } ALLOCV_END(alloc_buffer); return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.retval); }
#to_i
The integer memory location of this function
# File 'ext/fiddle/lib/fiddle/function.rb', line 19
def to_i ptr.to_i end
#to_proc
Turn this function in to a proc
# File 'ext/fiddle/lib/fiddle/function.rb', line 24
def to_proc this = self lambda { |*args| this.call(*args) } end