From 9337b214863529997d289a8d68bd0635a5ed6156 Mon Sep 17 00:00:00 2001 From: Ryan Pavlik Date: Tue, 7 Dec 2021 15:12:45 -0600 Subject: [PATCH] jnipp: Upstream updates, including fixes for crashes. Fixes issues related to manual attaching/detaching thread from VM. --- src/external/jnipp/CMakeLists.txt | 18 ++++++ src/external/jnipp/jnipp.cpp | 55 ++++++++++--------- src/external/jnipp/jnipp.h | 49 +++++++++++++++-- src/external/jnipp/tests/CMakeLists.txt | 18 ++++++ src/external/jnipp/tests/external_create.cpp | 37 +++++++++++++ src/external/jnipp/tests/external_detatch.cpp | 34 ++++++++++++ src/external/jnipp/tests/main.cpp | 2 - 7 files changed, 181 insertions(+), 32 deletions(-) create mode 100644 src/external/jnipp/CMakeLists.txt create mode 100644 src/external/jnipp/tests/CMakeLists.txt create mode 100644 src/external/jnipp/tests/external_create.cpp create mode 100644 src/external/jnipp/tests/external_detatch.cpp diff --git a/src/external/jnipp/CMakeLists.txt b/src/external/jnipp/CMakeLists.txt new file mode 100644 index 000000000..df206fec1 --- /dev/null +++ b/src/external/jnipp/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2021, Collabora, Ltd. +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10.2) +project(jnipp) + +find_package(JNI REQUIRED) +include(CTest) + +add_library(jnipp jnipp.cpp) +target_include_directories( + jnipp + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${JNI_INCLUDE_DIRS}) +target_link_libraries(jnipp PUBLIC ${CMAKE_DL_LIBS}) + +add_subdirectory(tests) diff --git a/src/external/jnipp/jnipp.cpp b/src/external/jnipp/jnipp.cpp index 7abfd5d76..45a8e9d87 100644 --- a/src/external/jnipp/jnipp.cpp +++ b/src/external/jnipp/jnipp.cpp @@ -26,9 +26,13 @@ namespace jni static std::atomic_bool isVm(false); static JavaVM* javaVm = nullptr; + static bool getEnv(JavaVM *vm, JNIEnv **env) { + return vm->GetEnv((void **)env, JNI_VERSION_1_2) == JNI_OK; + } + static bool isAttached(JavaVM *vm) { JNIEnv *env = nullptr; - return vm->GetEnv((void **)&env, JNI_VERSION_1_2) == JNI_OK; + return getEnv(vm, &env); } /** Maintains the lifecycle of a JNIEnv. @@ -63,7 +67,7 @@ namespace jni if (vm == nullptr) throw InitializationException("JNI not initialized"); - if (!isAttached(vm)) + if (!getEnv(vm, &_env)) { #ifdef __ANDROID__ if (vm->AttachCurrentThread(&_env, nullptr) != 0) @@ -368,20 +372,20 @@ namespace jni return _handle == nullptr || env()->IsSameObject(_handle, nullptr); } - template <> void Object::callMethod(method_t method, internal::value_t* args) const + void Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { env()->CallVoidMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); } - template <> bool Object::callMethod(method_t method, internal::value_t* args) const + bool Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallBooleanMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result != 0; } - template <> bool Object::get(field_t field) const + bool Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetBooleanField(_handle, field) != 0; } @@ -391,122 +395,122 @@ namespace jni env()->SetBooleanField(_handle, field, value); } - template <> byte_t Object::callMethod(method_t method, internal::value_t* args) const + byte_t Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallByteMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result; } - template <> wchar_t Object::callMethod(method_t method, internal::value_t* args) const + wchar_t Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallCharMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result; } - template <> short Object::callMethod(method_t method, internal::value_t* args) const + short Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallShortMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result; } - template <> int Object::callMethod(method_t method, internal::value_t* args) const + int Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallIntMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result; } - template <> long long Object::callMethod(method_t method, internal::value_t* args) const + long long Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallLongMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result; } - template <> float Object::callMethod(method_t method, internal::value_t* args) const + float Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallFloatMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result; } - template <> double Object::callMethod(method_t method, internal::value_t* args) const + double Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallDoubleMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return result; } - template <> std::string Object::callMethod(method_t method, internal::value_t* args) const + std::string Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return toString(result); } - template <> std::wstring Object::callMethod(method_t method, internal::value_t* args) const + std::wstring Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return toWString(result); } - template <> jni::Object Object::callMethod(method_t method, internal::value_t* args) const + jni::Object Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const { auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args); handleJavaExceptions(); return Object(result, DeleteLocalInput); } - template <> byte_t Object::get(field_t field) const + byte_t Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetByteField(_handle, field); } - template <> wchar_t Object::get(field_t field) const + wchar_t Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetCharField(_handle, field); } - template <> short Object::get(field_t field) const + short Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetShortField(_handle, field); } - template <> int Object::get(field_t field) const + int Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetIntField(_handle, field); } - template <> long long Object::get(field_t field) const + long long Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetLongField(_handle, field); } - template <> float Object::get(field_t field) const + float Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetFloatField(_handle, field); } - template <> double Object::get(field_t field) const + double Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return env()->GetDoubleField(_handle, field); } - template <> std::string Object::get(field_t field) const + std::string Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return toString(env()->GetObjectField(_handle, field)); } - template <> std::wstring Object::get(field_t field) const + std::wstring Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return toWString(env()->GetObjectField(_handle, field)); } - template <> Object Object::get(field_t field) const + Object Object::getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const { return Object(env()->GetObjectField(_handle, field), DeleteLocalInput); } @@ -1475,7 +1479,6 @@ namespace jni } #else - fprintf(stderr, "opening %s\n", path.c_str()); void* lib = ::dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL); diff --git a/src/external/jnipp/jnipp.h b/src/external/jnipp/jnipp.h index 12d7bd16c..017bdd923 100644 --- a/src/external/jnipp/jnipp.h +++ b/src/external/jnipp/jnipp.h @@ -190,6 +190,17 @@ namespace jni value_t values[1]; }; long getArrayLength(jarray array); + + /** + * @brief Used as a tag type for dispatching internally based on return type. + * + * @tparam T The type to wrap. + */ + template + struct ReturnTypeWrapper + { + using type = T; + }; } /** @@ -296,7 +307,7 @@ namespace jni \return The method's return value. */ template - TReturn call(method_t method) const { return callMethod(method, nullptr); } + TReturn call(method_t method) const { return callMethod(method, nullptr, internal::ReturnTypeWrapper{}); } /** Calls the method on this Object with the given name, and no arguments. @@ -326,7 +337,7 @@ namespace jni template TReturn call(method_t method, const TArgs&... args) const { internal::ArgArray transform(args...); - return callMethod(method, transform.values); + return callMethod(method, transform.values, internal::ReturnTypeWrapper{}); } /** @@ -356,7 +367,11 @@ namespace jni \return The field's value. */ template - TType get(field_t field) const; + TType get(field_t field) const { + // If you get a compile error here, then you've asked for a type + // we don't know how to get from JNI directly. + return getFieldValue(field, internal::ReturnTypeWrapper{}); + } /** Gets a field value from this Object. The field must belong to the @@ -424,7 +439,33 @@ namespace jni method_t getMethod(const char* name, const char* signature) const; method_t getMethod(const char* nameAndSignature) const; field_t getField(const char* name, const char* signature) const; - template TType callMethod(method_t method, internal::value_t* values) const; + + void callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + bool callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + byte_t callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + wchar_t callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + short callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + int callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + long long callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + float callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + double callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + std::string callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + std::wstring callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + jni::Object callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper const&) const; + + + void getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + bool getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + byte_t getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + wchar_t getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + short getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + int getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + long long getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + float getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + double getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + std::string getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + std::wstring getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; + jni::Object getFieldValue(field_t field, internal::ReturnTypeWrapper const&) const; // Instance Variables jobject _handle; diff --git a/src/external/jnipp/tests/CMakeLists.txt b/src/external/jnipp/tests/CMakeLists.txt new file mode 100644 index 000000000..256538b63 --- /dev/null +++ b/src/external/jnipp/tests/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2021, Collabora, Ltd. +# +# SPDX-License-Identifier: MIT + +add_executable(main_test main.cpp testing.h) +target_link_libraries(main_test PRIVATE jnipp) +add_test(NAME main_test COMMAND main_test) + +add_executable(external_create external_create.cpp testing.h) +target_link_libraries(external_create PUBLIC jnipp ${JNI_LIBRARIES}) +target_include_directories(external_create PUBLIC ${JNI_INCLUDE_DIRS}) +add_test(NAME external_create COMMAND external_create) + + +add_executable(external_detatch external_detatch.cpp testing.h) +target_link_libraries(external_detatch PUBLIC jnipp ${JNI_LIBRARIES}) +target_include_directories(external_detatch PUBLIC ${JNI_INCLUDE_DIRS}) +add_test(NAME external_detatch COMMAND external_detatch) diff --git a/src/external/jnipp/tests/external_create.cpp b/src/external/jnipp/tests/external_create.cpp new file mode 100644 index 000000000..735c248cb --- /dev/null +++ b/src/external/jnipp/tests/external_create.cpp @@ -0,0 +1,37 @@ +// Project Dependencies +#include +#include + +// Standard Dependencies +#include + +// Local Dependencies +#include "testing.h" + +/* + jni::Vm Tests + */ +TEST(Vm_externalCreateAndAttach) { + JNIEnv *env; + JavaVMInitArgs args = {}; + args.version = JNI_VERSION_1_2; + JavaVM *javaVm{}; + auto ret = JNI_CreateJavaVM(&javaVm, (void **)&env, &args); + ASSERT(ret == 0); + + { + jni::init(env); + jni::Class cls("java/lang/String"); + } + JavaVM *localVmPointer{}; + + ret = env->GetJavaVM(&localVmPointer); + ASSERT(ret == 0); +} + +int main() { + // jni::Vm Tests + RUN_TEST(Vm_externalCreateAndAttach); + + return 0; +} diff --git a/src/external/jnipp/tests/external_detatch.cpp b/src/external/jnipp/tests/external_detatch.cpp new file mode 100644 index 000000000..8e9d19bf4 --- /dev/null +++ b/src/external/jnipp/tests/external_detatch.cpp @@ -0,0 +1,34 @@ +// Project Dependencies +#include +#include + +// Standard Dependencies +#include + +// Local Dependencies +#include "testing.h" + +/* + jni::Vm Tests + */ +TEST(Vm_externalDetatch) { + jni::Vm vm; + + jni::Class cls("java/lang/String"); + + JNIEnv *env = (JNIEnv *)jni::env(); + JavaVM *localVmPointer{}; + + auto ret = env->GetJavaVM(&localVmPointer); + ASSERT(ret == 0); + ret = localVmPointer->DetachCurrentThread(); + ASSERT(ret == 0); + + ASSERT(1); +} + +int main() { + // jni::Vm Tests + RUN_TEST(Vm_externalDetatch); + return 0; +} diff --git a/src/external/jnipp/tests/main.cpp b/src/external/jnipp/tests/main.cpp index d40355922..7c1adfcb1 100644 --- a/src/external/jnipp/tests/main.cpp +++ b/src/external/jnipp/tests/main.cpp @@ -622,8 +622,6 @@ int main() RUN_TEST(Arg_ObjectPtr); } - std::cout << "Press a key to continue..." << std::endl; - std::cin.get(); return 0; }