Skip to content

Commit

Permalink
jnipp: Upstream updates, including fixes for crashes.
Browse files Browse the repository at this point in the history
Fixes issues related to manual attaching/detaching
thread from VM.
  • Loading branch information
rpavlik committed Dec 7, 2021
1 parent 2949efb commit 5e421bb
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 32 deletions.
4 changes: 4 additions & 0 deletions changes/sdk/pr.286.gh.OpenXR-SDK-Source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
- pr.285.gh.OpenXR-SDK-Source
---
Android loader: Update vendored jnipp project, including crash/exception fixes if an application manually attached or detached a thread.
18 changes: 18 additions & 0 deletions src/external/jnipp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
55 changes: 29 additions & 26 deletions src/external/jnipp/jnipp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<void> 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<bool> 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<bool> const&) const
{
return env()->GetBooleanField(_handle, field) != 0;
}
Expand All @@ -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<byte_t> 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<wchar_t> 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<short> 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<int> 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<long long> 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<float> 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<double> 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<std::string> 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<std::wstring> 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<jni::Object> 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<byte_t> 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<wchar_t> const&) const
{
return env()->GetCharField(_handle, field);
}

template <> short Object::get(field_t field) const
short Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<short> const&) const
{
return env()->GetShortField(_handle, field);
}

template <> int Object::get(field_t field) const
int Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<int> 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<long long> const&) const
{
return env()->GetLongField(_handle, field);
}

template <> float Object::get(field_t field) const
float Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<float> const&) const
{
return env()->GetFloatField(_handle, field);
}

template <> double Object::get(field_t field) const
double Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<double> 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<std::string> 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<std::wstring> const&) const
{
return toWString(env()->GetObjectField(_handle, field));
}

template <> Object Object::get(field_t field) const
Object Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<Object> const&) const
{
return Object(env()->GetObjectField(_handle, field), DeleteLocalInput);
}
Expand Down Expand Up @@ -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);

Expand Down
49 changes: 45 additions & 4 deletions src/external/jnipp/jnipp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename T>
struct ReturnTypeWrapper
{
using type = T;
};
}

/**
Expand Down Expand Up @@ -296,7 +307,7 @@ namespace jni
\return The method's return value.
*/
template <class TReturn>
TReturn call(method_t method) const { return callMethod<TReturn>(method, nullptr); }
TReturn call(method_t method) const { return callMethod(method, nullptr, internal::ReturnTypeWrapper<TReturn>{}); }

/**
Calls the method on this Object with the given name, and no arguments.
Expand Down Expand Up @@ -326,7 +337,7 @@ namespace jni
template <class TReturn, class... TArgs>
TReturn call(method_t method, const TArgs&... args) const {
internal::ArgArray<TArgs...> transform(args...);
return callMethod<TReturn>(method, transform.values);
return callMethod(method, transform.values, internal::ReturnTypeWrapper<TReturn>{});
}

/**
Expand Down Expand Up @@ -356,7 +367,11 @@ namespace jni
\return The field's value.
*/
template <class TType>
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<TType>{});
}

/**
Gets a field value from this Object. The field must belong to the
Expand Down Expand Up @@ -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 <class TType> TType callMethod(method_t method, internal::value_t* values) const;

void callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<void> const&) const;
bool callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<bool> const&) const;
byte_t callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<byte_t> const&) const;
wchar_t callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<wchar_t> const&) const;
short callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<short> const&) const;
int callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<int> const&) const;
long long callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<long long> const&) const;
float callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<float> const&) const;
double callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<double> const&) const;
std::string callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<std::string> const&) const;
std::wstring callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<std::wstring> const&) const;
jni::Object callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<jni::Object> const&) const;


void getFieldValue(field_t field, internal::ReturnTypeWrapper<void> const&) const;
bool getFieldValue(field_t field, internal::ReturnTypeWrapper<bool> const&) const;
byte_t getFieldValue(field_t field, internal::ReturnTypeWrapper<byte_t> const&) const;
wchar_t getFieldValue(field_t field, internal::ReturnTypeWrapper<wchar_t> const&) const;
short getFieldValue(field_t field, internal::ReturnTypeWrapper<short> const&) const;
int getFieldValue(field_t field, internal::ReturnTypeWrapper<int> const&) const;
long long getFieldValue(field_t field, internal::ReturnTypeWrapper<long long> const&) const;
float getFieldValue(field_t field, internal::ReturnTypeWrapper<float> const&) const;
double getFieldValue(field_t field, internal::ReturnTypeWrapper<double> const&) const;
std::string getFieldValue(field_t field, internal::ReturnTypeWrapper<std::string> const&) const;
std::wstring getFieldValue(field_t field, internal::ReturnTypeWrapper<std::wstring> const&) const;
jni::Object getFieldValue(field_t field, internal::ReturnTypeWrapper<jni::Object> const&) const;

// Instance Variables
jobject _handle;
Expand Down
18 changes: 18 additions & 0 deletions src/external/jnipp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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_detach external_detach.cpp testing.h)
target_link_libraries(external_detach PUBLIC jnipp ${JNI_LIBRARIES})
target_include_directories(external_detach PUBLIC ${JNI_INCLUDE_DIRS})
add_test(NAME external_detach COMMAND external_detach)
37 changes: 37 additions & 0 deletions src/external/jnipp/tests/external_create.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Project Dependencies
#include <jni.h>
#include <jnipp.h>

// Standard Dependencies
#include <cmath>

// 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;
}
34 changes: 34 additions & 0 deletions src/external/jnipp/tests/external_detach.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Project Dependencies
#include <jni.h>
#include <jnipp.h>

// Standard Dependencies
#include <cmath>

// Local Dependencies
#include "testing.h"

/*
jni::Vm Tests
*/
TEST(Vm_externalDetach) {
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_externalDetach);
return 0;
}
Loading

0 comments on commit 5e421bb

Please sign in to comment.