Class: Fiddle::Function
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | ext/fiddle/function.c, ext/fiddle/pinned.c, 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 =
# File 'ext/fiddle/function.c', line 440
Default ABI
INT2NUM(FFI_DEFAULT_ABI)
-
STDCALL =
# File 'ext/fiddle/function.c', line 449
FFI implementation of WIN32 stdcall convention
INT2NUM(FFI_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(argv[], self)
Calls the constructed
Function, withargs. -
#to_i
The integer memory location of this function.
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.
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(argv[], self)
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 209
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))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)
int socket_error = WSAGetLastError();
rb_funcall(mFiddle, rb_intern("win32_last_error="), 1,
INT2NUM(errno_keep));
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