STATIC mp_obj_t make_func(mp_obj_t rettype_in, void *func, mp_obj_t argtypes_in) { const char *rettype = mp_obj_str_get_str(rettype_in); const char *argtypes = mp_obj_str_get_str(argtypes_in); mp_int_t nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(argtypes_in)); mp_obj_ffifunc_t *o = m_new_obj_var(mp_obj_ffifunc_t, ffi_type*, nparams); o->base.type = &ffifunc_type; o->func = func; o->rettype = *rettype; o->argtypes = argtypes; mp_obj_iter_buf_t iter_buf; mp_obj_t iterable = mp_getiter(argtypes_in, &iter_buf); mp_obj_t item; int i = 0; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { o->params[i++] = get_ffi_type(item); } int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params); if (res != FFI_OK) { mp_raise_ValueError("Error in ffi_prep_cif"); } return MP_OBJ_FROM_PTR(o); }
STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t paramtypes_in) { const char *rettype = mp_obj_str_get_str(rettype_in); mp_int_t nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(paramtypes_in)); mp_obj_fficallback_t *o = m_new_obj_var(mp_obj_fficallback_t, ffi_type*, nparams); o->base.type = &fficallback_type; o->clo = ffi_closure_alloc(sizeof(ffi_closure), &o->func); o->rettype = *rettype; mp_obj_iter_buf_t iter_buf; mp_obj_t iterable = mp_getiter(paramtypes_in, &iter_buf); mp_obj_t item; int i = 0; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { o->params[i++] = get_ffi_type(item); } int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params); if (res != FFI_OK) { mp_raise_ValueError("Error in ffi_prep_cif"); } res = ffi_prep_closure_loc(o->clo, &o->cif, call_py_func, MP_OBJ_TO_PTR(func_in), o->func); if (res != FFI_OK) { mp_raise_ValueError("ffi_prep_closure_loc"); } return MP_OBJ_FROM_PTR(o); }
STATIC mp_obj_t ffimod_func(mp_uint_t n_args, const mp_obj_t *args) { mp_obj_ffimod_t *self = args[0]; const char *rettype = mp_obj_str_get_str(args[1]); const char *symname = mp_obj_str_get_str(args[2]); void *sym = dlsym(self->handle, symname); if (sym == NULL) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno))); } int nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(args[3])); mp_obj_ffifunc_t *o = m_new_obj_var(mp_obj_ffifunc_t, ffi_type*, nparams); o->base.type = &ffifunc_type; o->func = sym; o->rettype = *rettype; mp_obj_t iterable = mp_getiter(args[3]); mp_obj_t item; int i = 0; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { o->params[i++] = get_ffi_type(item); } int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params); if (res != FFI_OK) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Error in ffi_prep_cif")); } return o; }
static SQInteger sq_lib_bind_func(HSQUIRRELVM v) { void **modbuf; void *mod; void *sym; const SQChar *symname; const char *rettype; sq_getuserdata(v, 1, (void**)&modbuf, NULL); mod = *modbuf; sq_getstring(v, 2, &rettype); sq_getstring(v, 3, &symname); sym = GET_SYM(mod, symname); if (!sym) return sq_throwerror(v, "Cannot find symbol"); int nparam = sq_getsize(v, 4); int size = sizeof(FFIFunc) + sizeof(ffi_type*) * nparam; FFIFunc *ffibuf = (FFIFunc*)sq_newuserdata(v, size); sq_push_delegate_table(v, FFI_LIB_FUNC_TAG); sq_setdelegate(v, -2); // printf("Allocated %d bytes at %p\n", size, ffibuf); ffibuf->func = sym; ffibuf->rettype = *rettype; int i; for (i = 0; i < nparam; i++) { sq_pushinteger(v, i); sq_get(v, 4); ffibuf->params[i] = get_ffi_type(v, -1); if (!ffibuf->params[i]) return SQ_ERROR; sq_poptop(v); } int res = ffi_prep_cif(&ffibuf->cif, FFI_DEFAULT_ABI, nparam, char2ffi_type(*rettype), ffibuf->params); if (res != FFI_OK) return sq_throwerror(v, "Error in ffi_prep_cif"); return 1; }
ffi_type * create_ffi_struct(ZARRAYP args, int *i, storage *mem) { ffi_type *st = alloc(mem, sizeof(ffi_type)); if (*i >= args->len - 1) { logger("Invalid index\n"); return NULL; } ++*i; unsigned char j, n = args->data[*i]; st->type = FFI_TYPE_STRUCT; st->size = 0; st->alignment = 0; st->elements = alloc(mem, sizeof(ffi_type *) * (n + 1)); ++*i; for (j = 0; j < n; ++j, ++*i) { st->elements[j] = get_ffi_type(args, i, mem); if (!st->elements[j]) { return NULL; } if (st->elements[j] == &ffi_type_void) { logger("You can not use CNA_VOID in structures\n"); return NULL; } } --*i; st->elements[j] = NULL; return st; }
callback* create_callback(JNIEnv* env, jobject obj, jobject method, jobjectArray arg_classes, jclass return_class, callconv_t calling_convention, jint options, jstring encoding) { jboolean direct = options & CB_OPTION_DIRECT; jboolean in_dll = options & CB_OPTION_IN_DLL; callback* cb; ffi_abi abi = (calling_convention == CALLCONV_C ? FFI_DEFAULT_ABI : (ffi_abi)calling_convention); ffi_abi java_abi = FFI_DEFAULT_ABI; ffi_type* return_type; ffi_status status; jsize argc; JavaVM* vm; int rtype; char msg[MSG_SIZE]; int i; int cvt = 0; const char* throw_type = NULL; const char* throw_msg = NULL; if ((*env)->GetJavaVM(env, &vm) != JNI_OK) { throwByName(env, EUnsatisfiedLink, "Couldn't obtain Java VM reference when creating native callback"); return NULL; } argc = (*env)->GetArrayLength(env, arg_classes); cb = (callback *)malloc(sizeof(callback)); cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->x_closure); cb->saved_x_closure = cb->x_closure; cb->object = (*env)->NewWeakGlobalRef(env, obj); cb->methodID = (*env)->FromReflectedMethod(env, method); cb->vm = vm; cb->arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * argc); cb->java_arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * (argc + 3)); cb->arg_jtypes = (char*)malloc(sizeof(char) * argc); cb->conversion_flags = (int *)malloc(sizeof(int) * argc); cb->rflag = CVT_DEFAULT; cb->arg_classes = (jobject*)malloc(sizeof(jobject) * argc); cb->direct = direct; cb->java_arg_types[0] = cb->java_arg_types[1] = cb->java_arg_types[2] = &ffi_type_pointer; cb->encoding = newCStringUTF8(env, encoding); for (i=0;i < argc;i++) { int jtype; jclass cls = (*env)->GetObjectArrayElement(env, arg_classes, i); if ((cb->conversion_flags[i] = get_conversion_flag(env, cls)) != CVT_DEFAULT) { cb->arg_classes[i] = (*env)->NewWeakGlobalRef(env, cls); cvt = 1; } else { cb->arg_classes[i] = NULL; } jtype = get_java_type(env, cls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported callback argument at index %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = cb->arg_types[i] = get_ffi_type(env, cls, cb->arg_jtypes[i]); if (!cb->java_arg_types[i+3]) { goto failure_cleanup; } if (cb->conversion_flags[i] == CVT_NATIVE_MAPPED || cb->conversion_flags[i] == CVT_POINTER_TYPE || cb->conversion_flags[i] == CVT_INTEGER_TYPE) { jclass ncls; ncls = getNativeType(env, cls); jtype = get_java_type(env, ncls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported NativeMapped callback argument native type at argument %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = &ffi_type_pointer; cb->arg_types[i] = get_ffi_type(env, ncls, cb->arg_jtypes[i]); if (!cb->arg_types[i]) { goto failure_cleanup; } } // Java callback method is called using varargs, so promote floats to // double where appropriate for the platform if (cb->arg_types[i]->type == FFI_TYPE_FLOAT) { cb->java_arg_types[i+3] = &ffi_type_double; cb->conversion_flags[i] = CVT_FLOAT; cvt = 1; } else if (cb->java_arg_types[i+3]->type == FFI_TYPE_STRUCT) { // All callback structure arguments are passed as a jobject cb->java_arg_types[i+3] = &ffi_type_pointer; } } if (!direct || !cvt) { free(cb->conversion_flags); cb->conversion_flags = NULL; free(cb->arg_classes); cb->arg_classes = NULL; } if (direct) { cb->rflag = get_conversion_flag(env, return_class); if (cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_INTEGER_TYPE || cb->rflag == CVT_POINTER_TYPE) { return_class = getNativeType(env, return_class); } } #if defined(_WIN32) if (calling_convention == CALLCONV_STDCALL) { #if defined(_WIN64) || defined(_WIN32_WCE) // Ignore requests for stdcall on win64/wince abi = FFI_DEFAULT_ABI; #else abi = FFI_STDCALL; // All JNI entry points on win32 use stdcall java_abi = FFI_STDCALL; #endif } #endif // _WIN32 if (!(abi > FFI_FIRST_ABI && abi < FFI_LAST_ABI)) { snprintf(msg, sizeof(msg), "Invalid calling convention %d", abi); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } rtype = get_java_type(env, return_class); if (rtype == -1) { throw_type = EIllegalArgument; throw_msg = "Unsupported callback return type"; goto failure_cleanup; } return_type = get_ffi_return_type(env, return_class, (char)rtype); if (!return_type) { throw_type = EIllegalArgument; throw_msg = "Error in callback return type"; goto failure_cleanup; } status = ffi_prep_cif(&cb->cif, abi, argc, return_type, cb->arg_types); if (!ffi_error(env, "callback setup", status)) { ffi_type* java_return_type = return_type; if (cb->rflag == CVT_STRUCTURE_BYVAL || cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_POINTER_TYPE || cb->rflag == CVT_INTEGER_TYPE) { // Java method returns a jobject, not a struct java_return_type = &ffi_type_pointer; rtype = '*'; } switch(rtype) { #define OFFSETOF(ENV,METHOD) ((size_t)((char *)&(*(ENV))->METHOD - (char *)(*(ENV)))) case 'V': cb->fptr_offset = OFFSETOF(env, CallVoidMethod); break; case 'Z': cb->fptr_offset = OFFSETOF(env, CallBooleanMethod); break; case 'B': cb->fptr_offset = OFFSETOF(env, CallByteMethod); break; case 'S': cb->fptr_offset = OFFSETOF(env, CallShortMethod); break; case 'C': cb->fptr_offset = OFFSETOF(env, CallCharMethod); break; case 'I': cb->fptr_offset = OFFSETOF(env, CallIntMethod); break; case 'J': cb->fptr_offset = OFFSETOF(env, CallLongMethod); break; case 'F': cb->fptr_offset = OFFSETOF(env, CallFloatMethod); break; case 'D': cb->fptr_offset = OFFSETOF(env, CallDoubleMethod); break; default: cb->fptr_offset = OFFSETOF(env, CallObjectMethod); break; } status = ffi_prep_cif_var(&cb->java_cif, java_abi, 2, argc+3, java_return_type, cb->java_arg_types); if (!ffi_error(env, "callback setup (2)", status)) { ffi_prep_closure_loc(cb->closure, &cb->cif, dispatch_callback, cb, cb->x_closure); #ifdef DLL_FPTRS // Find an available function pointer and assign it if (in_dll) { for (i=0;i < DLL_FPTRS;i++) { if (fn[i] == NULL) { fn[i] = cb->x_closure; cb->x_closure = dll_fptrs[i]; break; } } if (i == DLL_FPTRS) { throw_type = EOutOfMemory; throw_msg = "No more DLL callback slots available"; goto failure_cleanup; } } #endif return cb; } } failure_cleanup: free_callback(env, cb); if (throw_type) { throwByName(env, throw_type, throw_msg); } return NULL; }
callback* create_callback(JNIEnv* env, jobject obj, jobject method, jobjectArray param_types, jclass return_type, callconv_t calling_convention, jboolean direct) { callback* cb; ffi_abi abi = FFI_DEFAULT_ABI; ffi_abi java_abi = FFI_DEFAULT_ABI; ffi_type* ffi_rtype; ffi_status status; jsize argc; JavaVM* vm; int rtype; char msg[64]; int i; int cvt = 0; const char* throw_type = NULL; const char* throw_msg = NULL; if ((*env)->GetJavaVM(env, &vm) != JNI_OK) { throwByName(env, EUnsatisfiedLink, "Can't get Java VM"); return NULL; } argc = (*env)->GetArrayLength(env, param_types); cb = (callback *)malloc(sizeof(callback)); cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->x_closure); cb->object = (*env)->NewWeakGlobalRef(env, obj); cb->methodID = (*env)->FromReflectedMethod(env, method); cb->vm = vm; cb->arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * argc); cb->java_arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * (argc + 3)); cb->arg_jtypes = (char*)malloc(sizeof(char) * argc); cb->flags = (int *)malloc(sizeof(int) * argc); cb->rflag = CVT_DEFAULT; cb->arg_classes = (jobject*)malloc(sizeof(jobject) * argc); cb->direct = direct; cb->java_arg_types[0] = cb->java_arg_types[1] = cb->java_arg_types[2] = &ffi_type_pointer; for (i=0;i < argc;i++) { int jtype; jclass cls = (*env)->GetObjectArrayElement(env, param_types, i); if ((cb->flags[i] = get_conversion_flag(env, cls)) != CVT_DEFAULT) { cb->arg_classes[i] = (*env)->NewWeakGlobalRef(env, cls); cvt = 1; } jtype = get_jtype(env, cls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported argument at index %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = cb->arg_types[i] = get_ffi_type(env, cls, cb->arg_jtypes[i]); if (cb->flags[i] == CVT_NATIVE_MAPPED || cb->flags[i] == CVT_POINTER_TYPE || cb->flags[i] == CVT_INTEGER_TYPE) { jclass ncls; ncls = getNativeType(env, cls); jtype = get_jtype(env, ncls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported NativeMapped argument native type at argument %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = &ffi_type_pointer; cb->arg_types[i] = get_ffi_type(env, ncls, cb->arg_jtypes[i]); } if (cb->arg_types[i]->type == FFI_TYPE_FLOAT) { // Java method is varargs, so promote floats to double cb->java_arg_types[i+3] = &ffi_type_double; cb->flags[i] = CVT_FLOAT; cvt = 1; } else if (cb->java_arg_types[i+3]->type == FFI_TYPE_STRUCT) { // All callback structure arguments are passed as a jobject cb->java_arg_types[i+3] = &ffi_type_pointer; } } if (!direct || !cvt) { free(cb->flags); cb->flags = NULL; free(cb->arg_classes); cb->arg_classes = NULL; } if (direct) { cb->rflag = get_conversion_flag(env, return_type); if (cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_INTEGER_TYPE || cb->rflag == CVT_POINTER_TYPE) { return_type = getNativeType(env, return_type); } } #if defined(_WIN32) && !defined(_WIN64) if (calling_convention == CALLCONV_STDCALL) { abi = FFI_STDCALL; } java_abi = FFI_STDCALL; #endif // _WIN32 rtype = get_jtype(env, return_type); if (rtype == -1) { throw_type = EIllegalArgument; throw_msg = "Unsupported return type"; goto failure_cleanup; } ffi_rtype = get_ffi_rtype(env, return_type, (char)rtype); if (!ffi_rtype) { throw_type = EIllegalArgument; throw_msg = "Error in return type"; goto failure_cleanup; } status = ffi_prep_cif(&cb->cif, abi, argc, ffi_rtype, cb->arg_types); if (!ffi_error(env, "callback setup", status)) { ffi_type* java_ffi_rtype = ffi_rtype; if (cb->rflag == CVT_STRUCTURE_BYVAL || cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_POINTER_TYPE || cb->rflag == CVT_INTEGER_TYPE) { // Java method returns a jobject, not a struct java_ffi_rtype = &ffi_type_pointer; rtype = '*'; } switch(rtype) { case 'V': cb->fptr = (*env)->CallVoidMethod; break; case 'Z': cb->fptr = (*env)->CallBooleanMethod; break; case 'B': cb->fptr = (*env)->CallByteMethod; break; case 'S': cb->fptr = (*env)->CallShortMethod; break; case 'C': cb->fptr = (*env)->CallCharMethod; break; case 'I': cb->fptr = (*env)->CallIntMethod; break; case 'J': cb->fptr = (*env)->CallLongMethod; break; case 'F': cb->fptr = (*env)->CallFloatMethod; break; case 'D': cb->fptr = (*env)->CallDoubleMethod; break; default: cb->fptr = (*env)->CallObjectMethod; break; } status = ffi_prep_cif(&cb->java_cif, java_abi, argc+3, java_ffi_rtype, cb->java_arg_types); if (!ffi_error(env, "callback setup (2)", status)) { ffi_prep_closure_loc(cb->closure, &cb->cif, callback_dispatch, cb, cb->x_closure); return cb; } } failure_cleanup: free_callback(env, cb); if (throw_type) { throwByName(env, throw_type, msg); } return NULL; }
int call_function(ZARRAYP libID, const char *funcname, ZARRAYP argtypes, ZARRAYP args, ZARRAYP retval) { /* Last value in argtypes and ffi_types is the type of "funcname" return value */ int maxargs = argtypes->len - 1, nargs; ffi_cif cif; ffi_type *ffi_types[maxargs + 1]; void *ffi_values[maxargs]; int i, j; size_t fullsize = 0, size = 0; storage mem; init_storage(&mem); for (i = 0, j = 0; i < maxargs + 1; ++j, ++i) { size = get_size(argtypes->data[i]); ffi_types[j] = get_ffi_type(argtypes, &i, &mem); if (!ffi_types[j]) { return ZF_FAILURE; } if (ffi_types[j] == &ffi_type_void && i != maxargs) { logger("CNA_VOID type may be used only for return value\n"); return ZF_FAILURE; } if (size == 0 && ffi_types[j] != &ffi_type_void && i != maxargs) { size = *((size_t *)(args->data + fullsize)); fullsize += sizeof(size_t); } if (i != maxargs) { ffi_values[j] = args->data + fullsize; } fullsize += size; } retval->len = size; nargs = j - 1; if (fullsize != args->len + retval->len) { logger("Wrong size of ZARRAYP\n\tfullsize: %u\tZARRAYP args: %u\tretsize: %u\n", fullsize, args->len, retval->len); return ZF_FAILURE; } void *handle; if (assign_ZARRAYP_to_pointer(&handle, libID)) { return ZF_FAILURE; } if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs, ffi_types[nargs], ffi_types) != FFI_OK) { logger("ffi_prep_cif() failed\n"); return ZF_FAILURE; } if (retval->len == 0 && ffi_types[nargs] != &ffi_type_void) { retval->len = cif.arg_types[nargs]->size; } void *funcpointer = FIND_ENTRY(handle, funcname); if (!funcpointer) { logger("FIND_ENTRY() failed\n\thandle:%d\tfuncname:%s\n", handle, funcname); return ZF_FAILURE; } ffi_call(&cif, funcpointer, retval->data, ffi_values); /* TODO: handle error */ free_storage(&mem); return ZF_SUCCESS; }