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; } }
static VALUE returnL(FunctionType* fnInfo, L* result) { if (unlikely(!fnInfo->ignoreErrno)) { rbffi_save_errno(); } /* * This needs to do custom boxing of the return value, since a function * may only fill out the lower 8, 16 or 32 bits of %al, %ah, %eax, %rax, and * the upper part will be garbage. This will truncate the value again, then * sign extend it. */ switch (fnInfo->returnType->nativeType) { case NATIVE_VOID: return Qnil; case NATIVE_INT8: return INT2NUM(*(signed char *) result); case NATIVE_INT16: return INT2NUM(*(signed short *) result); case NATIVE_INT32: return INT2NUM(*(signed int *) result); case NATIVE_LONG: return LONG2NUM(*(signed long *) result); case NATIVE_UINT8: return UINT2NUM(*(unsigned char *) result); case NATIVE_UINT16: return UINT2NUM(*(unsigned short *) result); case NATIVE_UINT32: return UINT2NUM(*(unsigned int *) result); case NATIVE_ULONG: return ULONG2NUM(*(unsigned long *) result); #ifdef __x86_64__ case NATIVE_INT64: return LL2NUM(*(signed long long *) result); case NATIVE_UINT64: return ULL2NUM(*(unsigned long long *) result); #endif /* __x86_64__ */ case NATIVE_STRING: return *(void **) result != 0 ? rb_tainted_str_new2(*(char **) result) : Qnil; case NATIVE_POINTER: return rbffi_Pointer_NewInstance(*(void **) result); case NATIVE_BOOL: return *(char *) result != 0 ? Qtrue : Qfalse; default: rb_raise(rb_eRuntimeError, "invalid return type: %d", fnInfo->returnType->nativeType); return Qnil; } }
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; } }