#include "jni.hpp" #include 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(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(v_env); } LocalRef FindClass(JNIEnv *env, const char *name) { return {env, env->FindClass(name)}; } LocalRef ExceptionOccurred(JNIEnv* env) { return {env, env->ExceptionOccurred()}; } std::string StringToUTF8(JNIEnv* env, const Ref& 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 UTF8ToString(JNIEnv* env, const std::string& str) { return {env, env->NewStringUTF(str.c_str())}; } LocalRef CreateArray(JNIEnv* env, const Ref& element_class, std::vector> objects) { auto ret = LocalRef(env, env->NewObjectArray(static_cast(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