123456789_123456789_123456789_123456789_123456789_

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

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

[ GitHub ]

  
# 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_typesArray<FFI::Type>

Retrieve Array of parameter types

This method returns an Array of ::FFI types accepted as function parameters.

[ GitHub ]

  
# File 'lib/ffi/variadic.rb', line 76

def param_types
  [*@fixed, Type::Builtin::VARARGS]
end

#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;
}