Beispiel #1
0
static VALUE
rbffi_InvokeLongParams(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
{
    void **ffiValues = NULL;
    FFIStorage* params = NULL;
    L result;

    if (fnInfo->parameterCount > 0) {
        ffiValues = ALLOCA_N(void *, fnInfo->parameterCount);
        params = ALLOCA_N(FFIStorage, fnInfo->parameterCount);

        rbffi_SetupCallParams(argc, argv,
                              fnInfo->parameterCount, fnInfo->nativeParameterTypes, params, ffiValues,
                              fnInfo->callbackParameters, fnInfo->callbackCount, fnInfo->rbEnums);

        switch (fnInfo->parameterCount) {
        case 0:
            result = ((L(*)(void)) function)();
            break;

        case 1:
            result = ((L(*)(L)) function)(*(L *) ffiValues[0]);
            break;

        case 2:
            result = ((L(*)(L, L)) function)(*(L *) ffiValues[0],
                                             *(L *) ffiValues[1]);
            break;

        case 3:
            result = ((L(*)(L, L, L)) function)(*(L *) ffiValues[0],
                                                *(L *) ffiValues[1], *(L *) ffiValues[2]);
            break;

        case 4:
            result = ((L(*)(L, L, L, L)) function)(*(L *) ffiValues[0],
                                                   *(L *) ffiValues[1], *(L *) ffiValues[2], *(L *) ffiValues[3]);
            break;

        case 5:
            result = ((L(*)(L, L, L, L, L)) function)(*(L *) ffiValues[0],
                     *(L *) ffiValues[1], *(L *) ffiValues[2], *(L *) ffiValues[3],
                     *(L *) ffiValues[4]);
            break;

        case 6:
            result = ((L(*)(L, L, L, L, L, L)) function)(*(L *) ffiValues[0],
                     *(L *) ffiValues[1], *(L *) ffiValues[2], *(L *) ffiValues[3],
                     *(L *) ffiValues[4], *(L *) ffiValues[5]);
            break;

        default:
            rb_raise(rb_eRuntimeError, "BUG: should not reach this point");
            return Qnil;
        }
    }
Beispiel #2
0
VALUE
rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
{
    void* retval;
    void** ffiValues;
    FFIStorage* params;
    VALUE rbReturnValue;
    rbffi_frame_t frame = { 0 };
    
    retval = alloca(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
    
    if (unlikely(fnInfo->blocking)) {
        BlockingCall* bc;

        /*
         * due to the way thread switching works on older ruby variants, we
         * cannot allocate anything passed to the blocking function on the stack
         */
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
        ffiValues = ALLOCA_N(void *, fnInfo->parameterCount);
        params = ALLOCA_N(FFIStorage, fnInfo->parameterCount);
        bc = ALLOCA_N(BlockingCall, 1);
        bc->retval = retval;
#else
        ffiValues = ALLOC_N(void *, fnInfo->parameterCount);
        params = ALLOC_N(FFIStorage, fnInfo->parameterCount);
        bc = ALLOC_N(BlockingCall, 1);
        bc->retval = xmalloc(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
        bc->stkretval = retval;
#endif
        bc->info = fnInfo;
        bc->function = function;
        bc->ffiValues = ffiValues;
        bc->params = params;
        bc->frame = &frame;

        rbffi_SetupCallParams(argc, argv,
            fnInfo->parameterCount, fnInfo->parameterTypes, params, ffiValues,
            fnInfo->callbackParameters, fnInfo->callbackCount, fnInfo->rbEnums);

        rbffi_frame_push(&frame); 
        rb_rescue2(do_blocking_call, (VALUE) bc, save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0);
        rbffi_frame_pop(&frame);

#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
        memcpy(bc->stkretval, bc->retval, MAX(bc->info->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
        xfree(bc->params);
        xfree(bc->ffiValues);
        xfree(bc->retval);
        xfree(bc);
#endif
    
    } else {
Beispiel #3
0
VALUE
rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
{
    void* retval;
    void** ffiValues;
    FFIStorage* params;
    VALUE rbReturnValue, exc;    
    rbffi_thread_t oldThread;

    retval = alloca(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
    
    if (unlikely(fnInfo->blocking)) {
        BlockingCall* bc;

        /*
         * due to the way thread switching works on older ruby variants, we
         * cannot allocate anything passed to the blocking function on the stack
         */
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
        ffiValues = ALLOCA_N(void *, fnInfo->parameterCount);
        params = ALLOCA_N(FFIStorage, fnInfo->parameterCount);
        bc = ALLOCA_N(BlockingCall, 1);
        bc->retval = retval;
#else
        ffiValues = ALLOC_N(void *, fnInfo->parameterCount);
        params = ALLOC_N(FFIStorage, fnInfo->parameterCount);
        bc = ALLOC_N(BlockingCall, 1);
        bc->retval = xmalloc(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
        bc->stkretval = retval;
#endif
        bc->info = fnInfo;
        bc->function = function;
        bc->ffiValues = ffiValues;
        bc->params = params;

        rbffi_SetupCallParams(argc, argv,
            fnInfo->parameterCount, fnInfo->parameterTypes, params, ffiValues,
            fnInfo->callbackParameters, fnInfo->callbackCount, fnInfo->rbEnums);

#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) 
        rb_thread_call_without_gvl(call_blocking_function, bc, (void *) -1, NULL);

#elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
        rb_thread_blocking_region(call_blocking_function, bc, (void *) -1, NULL);

#else
        rb_ensure(do_blocking_call, (VALUE) bc, cleanup_blocking_call, (VALUE) bc);
#endif
        
    } else {
Beispiel #4
0
VALUE
rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
{
    void* retval;
    void** ffiValues;
    FFIStorage* params;

    ffiValues = ALLOCA_N(void *, fnInfo->parameterCount);
    params = ALLOCA_N(FFIStorage, fnInfo->parameterCount);
    retval = alloca(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));

    rbffi_SetupCallParams(argc, argv,
                          fnInfo->parameterCount, fnInfo->nativeParameterTypes, params, ffiValues,
                          fnInfo->callbackParameters, fnInfo->callbackCount, fnInfo->rbEnums);

#if defined(HAVE_NATIVETHREAD) && defined(HAVE_RB_THREAD_BLOCKING_REGION)
    if (unlikely(fnInfo->blocking)) {
        BlockingCall bc;

        bc.info = fnInfo;
        bc.function = function;
        bc.ffiValues = ffiValues;
        bc.retval = retval;

        rb_thread_blocking_region(call_blocking_function, &bc, (void *) -1, NULL);
    } else {
        ffi_call(&fnInfo->ffi_cif, FFI_FN(function), retval, ffiValues);
    }
#else
    ffi_call(&fnInfo->ffi_cif, FFI_FN(function), retval, ffiValues);
#endif

    if (!fnInfo->ignoreErrno) {
        rbffi_save_errno();
    }

    return rbffi_NativeValue_ToRuby(fnInfo->returnType, fnInfo->rbReturnType, retval,
                                    fnInfo->rbEnums);
}
Beispiel #5
0
VALUE
rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
{
    void* retval;
    void** ffiValues;
    FFIStorage* params;
    VALUE rbReturnValue;
    
#if !defined(HAVE_RUBY_THREAD_HAS_GVL_P)
    rbffi_thread_t oldThread;
#endif

    retval = alloca(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
    
    if (unlikely(fnInfo->blocking)) {
        BlockingCall* bc;

        /*
         * due to the way thread switching works on older ruby variants, we
         * cannot allocate anything passed to the blocking function on the stack
         */
        ffiValues = ALLOC_N(void *, fnInfo->parameterCount);
        params = ALLOC_N(FFIStorage, fnInfo->parameterCount);
        bc = ALLOC_N(BlockingCall, 1);
        bc->info = fnInfo;
        bc->function = function;
        bc->ffiValues = ffiValues;
        bc->params = params;
        bc->retval = xmalloc(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
        bc->stkretval = retval;

        rbffi_SetupCallParams(argc, argv,
            fnInfo->parameterCount, fnInfo->parameterTypes, params, ffiValues,
            fnInfo->callbackParameters, fnInfo->callbackCount, fnInfo->rbEnums);
        
        rb_ensure(do_blocking_call, (VALUE) bc, cleanup_blocking_call, (VALUE) bc);
        
    } else {
Beispiel #6
0
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;
    int paramCount = 0, fixedCount = 0, i;
    ffi_status ffiStatus;
    rbffi_frame_t frame = { 0 };

    Check_Type(parameterTypes, T_ARRAY);
    Check_Type(parameterValues, T_ARRAY);

    Data_Get_Struct(self, VariadicInvoker, invoker);
    paramCount = (int) RARRAY_LEN(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);
    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)");
        }
        Data_Get_Struct(rbType, 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"));
            Data_Get_Struct(rbType, Type, paramTypes[i]);
            break;
        case NATIVE_UINT8:
        case NATIVE_UINT16:
        case NATIVE_UINT32:
            rbType = rb_const_get(rbffi_TypeClass, rb_intern("UINT32"));
            Data_Get_Struct(rbType, Type, paramTypes[i]);
            break;

        case NATIVE_FLOAT32:
            rbType = rb_const_get(rbffi_TypeClass, rb_intern("DOUBLE"));
            Data_Get_Struct(rbType, Type, paramTypes[i]);
            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");
    }

    rbffi_SetupCallParams(paramCount, argv, -1, paramTypes, params,
                          ffiValues, NULL, 0, invoker->rbEnums);

    rbffi_frame_push(&frame);
    ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
    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);
}
Beispiel #7
0
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;
    int paramCount = 0, fixedCount = 0, i;
    ffi_status ffiStatus;
    rbffi_frame_t frame = { 0 };

    Check_Type(parameterTypes, T_ARRAY);
    Check_Type(parameterValues, T_ARRAY);

    Data_Get_Struct(self, VariadicInvoker, invoker);
    paramCount = (int) RARRAY_LEN(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);
    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)");
        }
        Data_Get_Struct(rbType, 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"));
                Data_Get_Struct(rbType, Type, paramTypes[i]);
                break;
            case NATIVE_UINT8:
            case NATIVE_UINT16:
            case NATIVE_UINT32:
                rbType = rb_const_get(rbffi_TypeClass, rb_intern("UINT32"));
                Data_Get_Struct(rbType, Type, paramTypes[i]);
                break;
            
            case NATIVE_FLOAT32:
                rbType = rb_const_get(rbffi_TypeClass, rb_intern("DOUBLE"));
                Data_Get_Struct(rbType, Type, paramTypes[i]);
                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");
    }

    rbffi_SetupCallParams(paramCount, argv, -1, paramTypes, params,
        ffiValues, NULL, 0, invoker->rbEnums);
    
    rbffi_frame_push(&frame);
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
    /* In Call.c, blocking: true is supported on older ruby variants
     * without rb_thread_call_without_gvl by allocating on the heap instead
     * of the stack. Since this functionality is being added later,
     * we’re skipping support for old rubies here. */
    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);
    }
#else
    ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
#endif
    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);
}