1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
#include "jni.hpp"
#include <android/log.h>
namespace {
JavaVM *g_vm;
const char *_jni_error(jint err) {
switch (err) {
case JNI_OK:
return "OK";
case JNI_ERR:
return "Unknown error";
case JNI_EDETACHED:
return "Thread detached from the VM";
case JNI_EVERSION:
return "JNI version error";
case JNI_ENOMEM:
return "Not enough memory";
case JNI_EEXIST:
return "VM already created";
case JNI_EINVAL:
return "Invalid arguments";
default:
return "Unexpected error";
}
}
} // namespace
namespace jni {
namespace internal {
void _abort_if_not_ok(const char *file, int line, jint ret) {
if (ret == JNI_OK) [[likely]] return;
__android_log_assert(nullptr, "jni", "JNI error: %s", _jni_error(ret));
}
void _abort_with_exception(const char* file, int line, JNIEnv* env) {
auto throwable = jni::ExceptionOccurred(env);
env->ExceptionClear();
if (throwable) {
auto throwable_class = jni::FindClass(env, "java/lang/Throwable");
if (throwable_class) {
auto throwable_toString = env->GetMethodID(throwable_class.get(),
"toString",
"()Ljava/lang/String;");
if (throwable_toString) {
auto description = jni::CallObjectMethod<jstring>(env, throwable,
throwable_toString);
auto str = jni::StringToUTF8(env, description);
__android_log_assert(nullptr, "jni", "JNI error: %s", str.c_str());
}
}
env->ExceptionClear();
__android_log_assert(nullptr, "jni",
"Unexpected NULL but no exception");
}
}
} // namespace internal
JNIEnv* AttachCurrentThread() {
JNIEnv* env;
auto ret = g_vm->AttachCurrentThread(&env, nullptr);
ABORT_IF_NOT_OK(ret);
return env;
}
JNIEnv* OnLoad(JavaVM* vm) {
void* v_env;
auto ret = vm->GetEnv(&v_env, JNI_VERSION);
ABORT_IF_NOT_OK(ret);
return reinterpret_cast<JNIEnv*>(v_env);
}
LocalRef<jclass> FindClass(JNIEnv *env, const char *name) {
return {env, env->FindClass(name)};
}
LocalRef<jthrowable> ExceptionOccurred(JNIEnv* env) {
return {env, env->ExceptionOccurred()};
}
std::string StringToUTF8(JNIEnv* env, const Ref<jstring>& str) {
if (!str) return "null";
auto len = env->GetStringUTFLength(str.get());
std::string ret(len, ' ');
env->GetStringUTFRegion(str.get(), 0, len, ret.data());
// This returns modified UTF-8 encoding, don't care.
return ret;
}
LocalRef<jstring> UTF8ToString(JNIEnv* env, const std::string& str) {
return {env, env->NewStringUTF(str.c_str())};
}
LocalRef<jobjectArray> CreateArray(JNIEnv* env, const Ref<jclass>& element_class, std::vector<LocalRef<jobject>> objects) {
auto ret = LocalRef<jobjectArray>(env, env->NewObjectArray(static_cast<jsize>(objects.size()), element_class.get(), nullptr));
ABORT_IF_NULL(env, ret);
jsize i = 0;
for (auto&& obj : objects) {
env->SetObjectArrayElement(ret.get(), i++, obj.release());
}
return ret;
}
} // namespace jni
|