Class: FFI::VariadicInvoker
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | ext/ffi_c/Variadic.c, lib/ffi/variadic.rb |
Class Method Summary
Instance Method Summary
-
#attach(mod, mname)
Attach the invoker to module
modasmname - #call(*args, &block)
- #invoke(parameterTypes, parameterValues)
-
#param_types ⇒ Array<FFI::Type>
Retrieve Array of parameter types.
- #return_type
Constructor Details
.new(rbFunction, rbParameterTypes, rbReturnType, options)
[ GitHub ]# File 'ext/ffi_c/Variadic.c', line 129
static VALUE
variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE rbReturnType, VALUE options)
{
VariadicInvoker* invoker = NULL;
VALUE retval = Qnil;
VALUE convention = Qnil;
VALUE fixed = Qnil;
#if defined(X86_WIN32)
VALUE rbConventionStr;
#endif
int i;
Check_Type(options, T_HASH);
convention = rb_hash_aref(options, ID2SYM(rb_intern("convention")));
TypedData_Get_Struct(self, VariadicInvoker, &variadic_data_type, invoker);
RB_OBJ_WRITE(self, &invoker->rbEnums, rb_hash_aref(options, ID2SYM(rb_intern("enums"))));
RB_OBJ_WRITE(self, &invoker->rbAddress, rbFunction);
invoker->function = rbffi_AbstractMemory_Cast(rbFunction, &rbffi_pointer_data_type)->address;
invoker->blocking = RTEST(rb_hash_aref(options, ID2SYM(rb_intern("blocking"))));
#if defined(X86_WIN32)
rbConventionStr = rb_funcall2(convention, rb_intern("to_s"), 0, NULL);
invoker->abi = (RTEST(convention) && strcmp(StringValueCStr(rbConventionStr), "stdcall") == 0)
? FFI_STDCALL : FFI_DEFAULT_ABI;
#else
invoker->abi = FFI_DEFAULT_ABI;
#endif
RB_OBJ_WRITE(self, &invoker->rbReturnType, rbffi_Type_Lookup(rbReturnType));
if (!RTEST(invoker->rbReturnType)) {
VALUE typeName = rb_funcall2(rbReturnType, rb_intern("inspect"), 0, NULL);
rb_raise(rb_eTypeError, "Invalid return type (%s)", RSTRING_PTR(typeName));
}
TypedData_Get_Struct(rbReturnType, Type, &rbffi_type_data_type, invoker->returnType);
invoker->paramCount = -1;
fixed = rb_ary_new2(RARRAY_LEN(rbParameterTypes) - 1);
for (i = 0; i < RARRAY_LEN(rbParameterTypes); ++i) {
VALUE entry = rb_ary_entry(rbParameterTypes, i);
VALUE rbType = rbffi_Type_Lookup(entry);
Type* type;
if (!RTEST(rbType)) {
VALUE typeName = rb_funcall2(entry, rb_intern("inspect"), 0, NULL);
rb_raise(rb_eTypeError, "Invalid parameter type (%s)", RSTRING_PTR(typeName));
}
TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, type);
if (type->nativeType != NATIVE_VARARGS) {
rb_ary_push(fixed, entry);
}
}
/*
* @fixed and @type_map are used by the parameter mangling ruby code
*/
rb_iv_set(self, "@fixed", rb_obj_freeze(fixed));
rb_iv_set(self, "@type_map", rb_hash_aref(options, ID2SYM(rb_intern("type_map"))));
return retval;
}
Instance Method Details
#attach(mod, mname)
Attach the invoker to module mod as mname
# File 'lib/ffi/variadic.rb', line 53
def attach(mod, mname) invoker = self params = "*args" call = "call" mname = mname.to_sym mod.module_eval <<-code, __FILE__, __LINE__ @ffi_functions = {} unless defined?(@ffi_functions) @ffi_functions[#{mname.inspect}] = invoker def self.#{mname}(#{params}) @ffi_functions[#{mname.inspect}].#{call}(#{params}) end define_method(#{mname.inspect}, &method(#{mname.inspect})) code invoker end
#call(*args, &block)
[ GitHub ]# File 'lib/ffi/variadic.rb', line 35
def call(*args, &block) param_types = Array.new(@fixed) param_values = Array.new @fixed.each_with_index do |t, i| param_values << args[i] end i = @fixed.length while i < args.length param_types << FFI.find_type(args[i], @type_map) param_values << args[i + 1] i += 2 end invoke(param_types, param_values, &block) end
#invoke(parameterTypes, parameterValues)
[ GitHub ]# File 'ext/ffi_c/Variadic.c', line 192
static VALUE
variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
{
VariadicInvoker* invoker;
FFIStorage* params;
void* retval;
ffi_cif cif;
void** ffiValues;
ffi_type** ffiParamTypes;
ffi_type* ffiReturnType;
Type** paramTypes;
VALUE* argv;
VALUE* callbackParameters;
VALUE callbackProc;
int paramCount = 0, fixedCount = 0, callbackCount = 0, i;
ffi_status ffiStatus;
rbffi_frame_t frame = { 0 };
Check_Type(parameterTypes, T_ARRAY);
Check_Type(parameterValues, T_ARRAY);
TypedData_Get_Struct(self, VariadicInvoker, &variadic_data_type, invoker);
paramCount = RARRAY_LENINT(parameterTypes);
paramTypes = ALLOCA_N(Type *, paramCount);
ffiParamTypes = ALLOCA_N(ffi_type *, paramCount);
params = ALLOCA_N(FFIStorage, paramCount);
ffiValues = ALLOCA_N(void*, paramCount);
argv = ALLOCA_N(VALUE, paramCount);
callbackParameters = ALLOCA_N(VALUE, paramCount);
retval = alloca(MAX(invoker->returnType->ffiType->size, FFI_SIZEOF_ARG));
for (i = 0; i < paramCount; ++i) {
VALUE rbType = rb_ary_entry(parameterTypes, i);
if (!rb_obj_is_kind_of(rbType, rbffi_TypeClass)) {
rb_raise(rb_eTypeError, "wrong type. Expected (FFI::Type)");
}
TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
switch (paramTypes[i]->nativeType) {
case NATIVE_INT8:
case NATIVE_INT16:
case NATIVE_INT32:
rbType = rb_const_get(rbffi_TypeClass, rb_intern("INT32"));
TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
break;
case NATIVE_UINT8:
case NATIVE_UINT16:
case NATIVE_UINT32:
rbType = rb_const_get(rbffi_TypeClass, rb_intern("UINT32"));
TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
break;
case NATIVE_FLOAT32:
rbType = rb_const_get(rbffi_TypeClass, rb_intern("DOUBLE"));
TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
break;
case NATIVE_FUNCTION:
if (!rb_obj_is_kind_of(rbType, rbffi_FunctionTypeClass)) {
VALUE typeName = rb_funcall2(rbType, rb_intern("inspect"), 0, NULL);
rb_raise(rb_eTypeError, "Incorrect parameter type (%s)", RSTRING_PTR(typeName));
}
callbackParameters[callbackCount++] = rbType;
break;
default:
break;
}
ffiParamTypes[i] = paramTypes[i]->ffiType;
if (ffiParamTypes[i] == NULL) {
rb_raise(rb_eArgError, "Invalid parameter type #%x", paramTypes[i]->nativeType);
}
argv[i] = rb_ary_entry(parameterValues, i);
}
ffiReturnType = invoker->returnType->ffiType;
if (ffiReturnType == NULL) {
rb_raise(rb_eArgError, "Invalid return type");
}
/*Get the number of fixed args from @fixed array*/
fixedCount = RARRAY_LEN(rb_iv_get(self, "@fixed"));
#ifdef HAVE_FFI_PREP_CIF_VAR
ffiStatus = ffi_prep_cif_var(&cif, invoker->abi, fixedCount, paramCount, ffiReturnType, ffiParamTypes);
#else
ffiStatus = ffi_prep_cif(&cif, invoker->abi, paramCount, ffiReturnType, ffiParamTypes);
#endif
switch (ffiStatus) {
case FFI_BAD_ABI:
rb_raise(rb_eArgError, "Invalid ABI specified");
case FFI_BAD_TYPEDEF:
rb_raise(rb_eArgError, "Invalid argument type specified");
case FFI_OK:
break;
default:
rb_raise(rb_eArgError, "Unknown FFI error");
}
callbackProc = rbffi_SetupCallParams(paramCount, argv, -1, paramTypes, params,
ffiValues, callbackParameters, callbackCount,
invoker->rbEnums);
rbffi_frame_push(&frame);
if(unlikely(invoker->blocking)) {
rbffi_blocking_call_t* bc;
bc = ALLOCA_N(rbffi_blocking_call_t, 1);
bc->retval = retval;
bc->function = invoker->function;
bc->ffiValues = ffiValues;
bc->params = params;
bc->frame = &frame;
bc->cif = cif;
rb_rescue2(rbffi_do_blocking_call, (VALUE) bc, rbffi_save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0);
} else {
ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
}
RB_GC_GUARD(callbackProc);
rbffi_frame_pop(&frame);
rbffi_save_errno();
if (RTEST(frame.exc) && frame.exc != Qnil) {
rb_exc_raise(frame.exc);
}
return rbffi_NativeValue_ToRuby(invoker->returnType, invoker->rbReturnType, retval);
}
#param_types ⇒ Array<FFI::Type>
Retrieve Array of parameter types
This method returns an Array of ::FFI types accepted as function parameters.
#return_type
[ GitHub ]# File 'ext/ffi_c/Variadic.c', line 327
static VALUE
variadic_return_type(VALUE self)
{
VariadicInvoker* invoker;
TypedData_Get_Struct(self, VariadicInvoker, &variadic_data_type, invoker);
return invoker->rbReturnType;
}