static thread_storage* get_thread_storage(JNIEnv* env) { thread_storage* tls = (thread_storage *)TLS_GET(tls_thread_data_key); if (tls == NULL) { tls = (thread_storage*)malloc(sizeof(thread_storage)); if (!tls) { throwByName(env, EOutOfMemory, "JNA: Can't allocate thread storage"); } else { snprintf(tls->name, sizeof(tls->name), "<uninitialized thread name>"); tls->jvm_thread = JNI_TRUE; tls->last_error = 0; tls->termination_flag = NULL; if ((*env)->GetJavaVM(env, &tls->jvm) != JNI_OK) { free(tls); throwByName(env, EIllegalState, "JNA: Could not get JavaVM"); tls = NULL; } else if (!TLS_SET(tls_thread_data_key, tls)) { free(tls); throwByName(env, EOutOfMemory, "JNA: Internal TLS error"); tls = NULL; } } } return tls; }
/* * Class: org_catacombae_catacombae_storage_io_win32_Win32FileStream * Method: write * Signature: ([BII[B)V */ JNIEXPORT void JNICALL Java_org_catacombae_catacombae_storage_io_win32_Win32FileStream_write(JNIEnv *env, jclass cls, jbyteArray data, jint offset, jint length, jbyteArray handleData) { // Microsoft's old-school C compiler forces me to go C90 strict. Hopefully MinGW GCC will be 64-bit soon. HANDLE hnd; LARGE_INTEGER distance; LARGE_INTEGER position; BYTE *buffer; DWORD bytesWritten = 0; //_tprintf(_T("Java_WindowsLowLevelIO_read called\n")); if(DEBUG) printf("Java_WritableWin32File_write called\n"); hnd = getHandle(env, handleData); distance.QuadPart = 0; if(!SetFilePointerEx(hnd, distance, &position, FILE_CURRENT)) { position.QuadPart = 0x7FFFFFFFFFFFFFFFLL; } buffer = (BYTE*)malloc(length); if(!buffer) { throwByName(env, "java/lang/RuntimeException", "Error %d (%s) while allocating %d-byte temporary buffer for " "writing.", errno, strerror(errno), length); return; } (*env)->GetByteArrayRegion(env, data, offset, length, buffer); // (jbyteArray)data->(BYTE*)buffer //printf("Calling: ReadFile(0x%x, 0x%x, %d, 0x%x, NULL);\n", hnd, buffer, length, &bytesRead); if(WriteFile(hnd, buffer, length, &bytesWritten, NULL) == FALSE) { //printf("bytesRead: %u\n", (int)bytesRead); //printf("GetLastError(): %u\n", (int)GetLastError()); throwByName(env, "java/lang/RuntimeException", "Error 0x%08X while attempting to write %d bytes to position " "%lld in file (wrote %lu bytes).", GetLastError(), length, (long long) position.QuadPart, (unsigned long) bytesWritten); //bytesRead = -1; } else if(bytesWritten != length) throwByName(env, "java/lang/RuntimeException", "Could not write entire buffer to file! Managed to write %lu / " "%d bytes.", (unsigned long) bytesWritten, length); free(buffer); // Clean up }
/** Store the requested detach state for the current thread. */ void JNA_detach(JNIEnv* env, jboolean needs_detach, void* termination_flag) { thread_storage* tls = get_thread_storage(env); if (tls) { tls->needs_detach = needs_detach; tls->termination_flag = (int *)termination_flag; if (needs_detach && tls->jvm_thread) { throwByName(env, EIllegalState, "Can not detach from a JVM thread"); } } }
/* Takes care of parsing the error after a call to MultiByteToWideChar as well as converting it to a java exception with an appropriate error message. */ void handleMultiByteToWideCharError(JNIEnv *env, DWORD errorCode) { char* message; DWORD error = GetLastError(); if(error == ERROR_INSUFFICIENT_BUFFER) message = "MultiByteToWideChar says: Insufficient buffer space."; else if(error == ERROR_INVALID_FLAGS) message = "MultiByteToWideChar says: Invalid flags."; else if(error == ERROR_INVALID_PARAMETER) message = "MultiByteToWideChar says: Invalid parameters."; else if(error == ERROR_NO_UNICODE_TRANSLATION) message = "MultiByteToWideChar says: No Unicode translation."; else message = "Unknown MultiByteToWideChar error!"; throwByName(env, "java/lang/RuntimeException", message); }
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; }
jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) { assert(_system); const int MAX_NARGS = 32; int res = -1; int argc = env->GetArrayLength(args); if (argc > MAX_NARGS) { throwByName(env, "java/lang/IllegalArgumentException", "too many arguments"); return 0; } char *argv[MAX_NARGS]; // note use in cleanup loop below int nargs; for (nargs = 0; nargs < argc; ++nargs) { jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); if (arg == 0) { argv[nargs] = 0; } else { const char *cstr = env->GetStringUTFChars(arg, 0); argv[nargs] = const_cast<char *>(cstr); // exception already thrown? if (cstr == 0) goto cleanup; } env->DeleteLocalRef(arg); } LOGI("Entering scummvm_main with %d args", argc); res = scummvm_main(argc, argv); LOGI("scummvm_main exited with code %d", res); _system->quit(); cleanup: nargs--; for (int i = 0; i < nargs; ++i) { if (argv[i] == 0) continue; jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); // Exception already thrown? if (arg == 0) return res; env->ReleaseStringUTFChars(arg, argv[i]); env->DeleteLocalRef(arg); } return res; }
void JNI::throwRuntimeException(JNIEnv *env, const char *msg) { throwByName(env, "java/lang/RuntimeException", msg); }
/* * Throws java.io.IOException */ void throwIOException(JNIEnv *env, const char *msg) { throwByName(env, "java/io/IOException", msg); }
/* * Throws java.lang.NullPointerException */ void throwNullPointerException(JNIEnv *env, const char *msg) { throwByName(env, "java/lang/NullPointerException", msg); }
/* * Common code for Java_org_catacombae_hfsexplorer_win32_WritableWin32File_openNative * and Java_org_catacombae_hfsexplorer_win32_WindowsLowLevelIO_openNative. */ jbyteArray openWin32File(JNIEnv *env, jstring str, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { // Microsoft's old-school C compiler forces me to go C90 strict. Hopefully MinGW GCC will be 64-bit soon. jsize utf8FilenameLength; const jbyte *utf8Filename; int wcFilenameSize; WCHAR *wcFilename; HANDLE hnd; if(DEBUG) printf("openWin32File called\n"); if(str == NULL) { // Must check input, or we can crash the jvm. throwByName(env, "java/lang/NullPointerException", "Filename is null."); return 0; } /* First, we convert the jstring to a jbyte array with the string encoded into UTF-8. */ utf8FilenameLength = (*env)->GetStringUTFLength(env, str); utf8Filename = (*env)->GetStringUTFChars(env, str, NULL); if(DEBUG) printf("utf8FilenameLength: %d bytes\n", utf8FilenameLength); /* Then, we convert the UTF-8 string into windows WCHARs */ wcFilenameSize = MultiByteToWideChar(CP_UTF8, 0, utf8Filename, utf8FilenameLength, NULL, 0); if(wcFilenameSize == 0) { handleMultiByteToWideCharError(env, GetLastError()); (*env)->ReleaseStringUTFChars(env, str, utf8Filename); return 0; } ++wcFilenameSize; // the last WCHAR is the null terminator if(DEBUG) printf("wcFilenameSize: %d\n", wcFilenameSize); wcFilename = (WCHAR*)malloc(sizeof(WCHAR)*wcFilenameSize); if(MultiByteToWideChar(CP_UTF8, 0, utf8Filename, utf8FilenameLength, wcFilename, wcFilenameSize) == 0) { handleMultiByteToWideCharError(env, GetLastError()); (*env)->ReleaseStringUTFChars(env, str, utf8Filename); free(wcFilename); return 0; } wcFilename[wcFilenameSize-1] = L'\0'; // Null termination. (*env)->ReleaseStringUTFChars(env, str, utf8Filename); // Release allocated resources. /* Perfect. */ if(DEBUG) printf("Attempting to open \""); if(DEBUG) wprintf(L"%s", wcFilename); if(DEBUG) printf("\"\n"); hnd = CreateFileW(wcFilename, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); free(wcFilename); // Done with that one now if(hnd == INVALID_HANDLE_VALUE) { throwByName(env, "java/lang/RuntimeException", "Could not open file."); return 0; } else { return getHandleData(env, hnd); } }
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; }
/* * This function simply throws a PKCS#11RuntimeException with the given * string as its message. * * @param env Used to call JNI funktions and to get the Exception class. * @param jmessage The message string of the Exception object. */ void throwPKCS11RuntimeException(JNIEnv *env, const char *message) { throwByName(env, CLASS_PKCS11RUNTIMEEXCEPTION, message); }
/* * Throws java.lang.OutOfMemoryError */ void throwOutOfMemoryError(JNIEnv *env, const char *msg) { throwByName(env, "java/lang/OutOfMemoryError", msg); }