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; } }
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 {
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 {
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); }
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 {
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); }
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); }