Ejemplo n.º 1
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);
}
Ejemplo n.º 2
0
static void
callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
{
    Closure* closure = (Closure *) user_data;
    Function* fn = (Function *) closure->info;
    FunctionType *cbInfo = fn->info;
    VALUE* rbParams;
    VALUE rbReturnValue;
    int i;

    rbParams = ALLOCA_N(VALUE, cbInfo->parameterCount);
    for (i = 0; i < cbInfo->parameterCount; ++i) {
        VALUE param;
        switch (cbInfo->parameterTypes[i]->nativeType) {
            case NATIVE_INT8:
                param = INT2NUM(*(int8_t *) parameters[i]);
                break;
            case NATIVE_UINT8:
                param = UINT2NUM(*(uint8_t *) parameters[i]);
                break;
            case NATIVE_INT16:
                param = INT2NUM(*(int16_t *) parameters[i]);
                break;
            case NATIVE_UINT16:
                param = UINT2NUM(*(uint16_t *) parameters[i]);
                break;
            case NATIVE_INT32:
                param = INT2NUM(*(int32_t *) parameters[i]);
                break;
            case NATIVE_UINT32:
                param = UINT2NUM(*(uint32_t *) parameters[i]);
                break;
            case NATIVE_INT64:
                param = LL2NUM(*(int64_t *) parameters[i]);
                break;
            case NATIVE_UINT64:
                param = ULL2NUM(*(uint64_t *) parameters[i]);
                break;
            case NATIVE_LONG:
                param = LONG2NUM(*(long *) parameters[i]);
                break;
            case NATIVE_ULONG:
                param = ULONG2NUM(*(unsigned long *) parameters[i]);
                break;
            case NATIVE_FLOAT32:
                param = rb_float_new(*(float *) parameters[i]);
                break;
            case NATIVE_FLOAT64:
                param = rb_float_new(*(double *) parameters[i]);
                break;
            case NATIVE_STRING:
                param = (*(void **) parameters[i] != NULL) ? rb_tainted_str_new2(*(char **) parameters[i]) : Qnil;
                break;
            case NATIVE_POINTER:
                param = rbffi_Pointer_NewInstance(*(void **) parameters[i]);
                break;
            case NATIVE_BOOL:
                param = (*(uint8_t *) parameters[i]) ? Qtrue : Qfalse;
                break;

            case NATIVE_FUNCTION:
            case NATIVE_CALLBACK:
                param = rbffi_NativeValue_ToRuby(cbInfo->parameterTypes[i],
                     rb_ary_entry(cbInfo->rbParameterTypes, i), parameters[i], Qnil);
                break;
            default:
                param = Qnil;
                break;
        }
        rbParams[i] = param;
    }
    rbReturnValue = rb_funcall2(fn->rbProc, id_call, cbInfo->parameterCount, rbParams);
    if (rbReturnValue == Qnil || TYPE(rbReturnValue) == T_NIL) {
        memset(retval, 0, cbInfo->ffiReturnType->size);
    } else switch (cbInfo->returnType->nativeType) {
        case NATIVE_INT8:
        case NATIVE_INT16:
        case NATIVE_INT32:
            *((ffi_sarg *) retval) = NUM2INT(rbReturnValue);
            break;
        case NATIVE_UINT8:
        case NATIVE_UINT16:
        case NATIVE_UINT32:
            *((ffi_arg *) retval) = NUM2UINT(rbReturnValue);
            break;
        case NATIVE_INT64:
            *((int64_t *) retval) = NUM2LL(rbReturnValue);
            break;
        case NATIVE_UINT64:
            *((uint64_t *) retval) = NUM2ULL(rbReturnValue);
            break;
        case NATIVE_LONG:
            *((ffi_sarg *) retval) = NUM2LONG(rbReturnValue);
            break;
        case NATIVE_ULONG:
            *((ffi_arg *) retval) = NUM2ULONG(rbReturnValue);
            break;
        case NATIVE_FLOAT32:
            *((float *) retval) = (float) NUM2DBL(rbReturnValue);
            break;
        case NATIVE_FLOAT64:
            *((double *) retval) = NUM2DBL(rbReturnValue);
            break;
        case NATIVE_POINTER:
            if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
                *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
            } else {
                // Default to returning NULL if not a value pointer object.  handles nil case as well
                *((void **) retval) = NULL;
            }
            break;

        case NATIVE_BOOL:
            *((ffi_arg *) retval) = rbReturnValue == Qtrue;
            break;

        case NATIVE_FUNCTION:
        case NATIVE_CALLBACK:
            if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {

                *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;

            } else if (rb_obj_is_kind_of(rbReturnValue, rb_cProc) || rb_respond_to(rbReturnValue, id_call)) {
                VALUE function;

                function = rbffi_Function_ForProc(cbInfo->rbReturnType, rbReturnValue);

                *((void **) retval) = ((AbstractMemory *) DATA_PTR(function))->address;
            } else {
                *((void **) retval) = NULL;
            }
            break;

        default:
            *((ffi_arg *) retval) = 0;
            break;
    }
}
Ejemplo n.º 3
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);
}
Ejemplo n.º 4
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);
}
Ejemplo n.º 5
0
Archivo: Types.c Proyecto: SFEley/ffi
VALUE
rbffi_NativeValue_ToRuby(Type* type, VALUE rbType, const void* ptr)
{
    switch (type->nativeType) {
        case NATIVE_VOID:
            return Qnil;
        case NATIVE_INT8:
          return INT2NUM((signed char) *(ffi_sarg *) ptr);
        case NATIVE_INT16:
          return INT2NUM((signed short) *(ffi_sarg *) ptr);
        case NATIVE_INT32:
          return INT2NUM((signed int) *(ffi_sarg *) ptr);
        case NATIVE_LONG:
            return LONG2NUM((signed long) *(ffi_sarg *) ptr);
        case NATIVE_INT64:
            return LL2NUM(*(signed long long *) ptr);

        case NATIVE_UINT8:
          return UINT2NUM((unsigned char) *(ffi_arg *) ptr);
        case NATIVE_UINT16:
          return UINT2NUM((unsigned short) *(ffi_arg *) ptr);
        case NATIVE_UINT32:
          return UINT2NUM((unsigned int) *(ffi_arg *) ptr);
        case NATIVE_ULONG:
            return ULONG2NUM((unsigned long) *(ffi_arg *) ptr);
        case NATIVE_UINT64:
            return ULL2NUM(*(unsigned long long *) ptr);

        case NATIVE_FLOAT32:
            return rb_float_new(*(float *) ptr);
        case NATIVE_FLOAT64:
            return rb_float_new(*(double *) ptr);

        case NATIVE_LONGDOUBLE:
	  return rbffi_longdouble_new(*(long double *) ptr);

        case NATIVE_STRING:
            return (*(void **) ptr != NULL) ? rb_tainted_str_new2(*(char **) ptr) : Qnil;
        case NATIVE_POINTER:
            return rbffi_Pointer_NewInstance(*(void **) ptr);
        case NATIVE_BOOL:
            return ((unsigned char) *(ffi_arg *) ptr) ? Qtrue : Qfalse;
        
        case NATIVE_FUNCTION:
        case NATIVE_CALLBACK: {
            return *(void **) ptr != NULL 
                    ? rbffi_Function_NewInstance(rbType, rbffi_Pointer_NewInstance(*(void **) ptr))
                    : Qnil;
        }

        case NATIVE_STRUCT: {
            StructByValue* sbv = (StructByValue *)type;
            AbstractMemory* mem;
            VALUE rbMemory = rbffi_MemoryPointer_NewInstance(1, sbv->base.ffiType->size, false);

            Data_Get_Struct(rbMemory, AbstractMemory, mem);
            memcpy(mem->address, ptr, sbv->base.ffiType->size);
            RB_GC_GUARD(rbMemory);
            RB_GC_GUARD(rbType);

            return rb_class_new_instance(1, &rbMemory, sbv->rbStructClass);
        }

        case NATIVE_MAPPED: {
            /*
             * For mapped types, first convert to the real native type, then upcall to
             * ruby to convert to the expected return type
             */
            MappedType* m = (MappedType *) type;
            VALUE values[2], rbReturnValue;

            values[0] = rbffi_NativeValue_ToRuby(m->type, m->rbType, ptr);
            values[1] = Qnil;
            

            rbReturnValue = rb_funcall2(m->rbConverter, id_from_native, 2, values);
            RB_GC_GUARD(values[0]);
            RB_GC_GUARD(rbType);
            
            return rbReturnValue;
        }
    
        default:
            rb_raise(rb_eRuntimeError, "Unknown type: %d", type->nativeType);
            return Qnil;
    }
}