#pragma once #include #include #include #include #include #include #include #include namespace jni { inline jint GetVersion(JNIEnv& env) { return env.GetVersion(); } inline jclass& DefineClass(JNIEnv& env, const char* name, jobject& loader, const jbyte* buf, jsize size) { return *CheckJavaException(env, Wrap(env.DefineClass(name, Unwrap(loader), buf, Unwrap(size)))); } template < class Array > auto DefineClass(JNIEnv& env, const char* name, jobject& loader, const Array& buf) -> std::enable_if_t< IsArraylike::value, jclass& > { return DefineClass(env, name, loader, ArraylikeData(buf), ArraylikeSize(buf)); } inline jclass& FindClass(JNIEnv& env, const char* name) { return *CheckJavaException(env, Wrap(env.FindClass(name))); } inline jmethodID* FromReflectedMethod(JNIEnv& env, jobject* obj) { return CheckJavaException(env, Wrap(env.FromReflectedMethod(Unwrap(obj)))); } inline jfieldID* FromReflectedField(JNIEnv& env, jobject* obj) { return CheckJavaException(env, Wrap(env.FromReflectedField(Unwrap(obj)))); } inline jobject& ToReflectedMethod(JNIEnv& env, jclass& clazz, jmethodID& method, bool isStatic) { return *CheckJavaException(env, Wrap(env.ToReflectedMethod(Unwrap(clazz), Unwrap(method), isStatic))); } inline jobject& ToReflectedField(JNIEnv& env, jclass& clazz, jfieldID& field, bool isStatic) { return *CheckJavaException(env, Wrap(env.ToReflectedField(Unwrap(clazz), Unwrap(field), isStatic))); } inline jclass* GetSuperclass(JNIEnv& env, jclass& clazz) { return CheckJavaException(env, Wrap(env.GetSuperclass(Unwrap(clazz)))); } inline bool IsAssignableFrom(JNIEnv& env, jclass& clazz1, jclass& clazz2) { return CheckJavaException(env, env.IsAssignableFrom(Unwrap(clazz1), Unwrap(clazz2))); } [[noreturn]] inline void Throw(JNIEnv& env, jthrowable& obj) { CheckErrorCode(env.Throw(Unwrap(obj))); throw PendingJavaException(); } [[noreturn]] inline void ThrowNew(JNIEnv& env, jclass& clazz, const char* msg = nullptr) { CheckErrorCode(env.ThrowNew(Unwrap(clazz), msg)); throw PendingJavaException(); } inline bool ExceptionCheck(JNIEnv& env) { return env.ExceptionCheck(); } inline jthrowable* ExceptionOccurred(JNIEnv& env) { return Wrap(env.ExceptionOccurred()); } inline void ExceptionDescribe(JNIEnv& env) { env.ExceptionDescribe(); } inline void ExceptionClear(JNIEnv& env) { env.ExceptionClear(); } [[noreturn]] inline void FatalError(JNIEnv& env, const char* msg) { env.FatalError(msg); std::abort(); } inline UniqueLocalFrame PushLocalFrame(JNIEnv& env, jint capacity) { CheckJavaExceptionThenErrorCode(env, env.PushLocalFrame(capacity)); return UniqueLocalFrame(&env, LocalFrameDeleter()); } inline jobject* PopLocalFrame(JNIEnv& env, UniqueLocalFrame&& frame, jobject* result = nullptr) { frame.release(); return CheckJavaException(env, Wrap(env.PopLocalFrame(Unwrap(result)))); } template < template < RefDeletionMethod > class Deleter, class T > UniqueGlobalRef NewGlobalRef(JNIEnv& env, T* t) { jobject* obj = Wrap(env.NewGlobalRef(Unwrap(t))); CheckJavaException(env); if (t && !obj) throw std::bad_alloc(); return UniqueGlobalRef(reinterpret_cast(obj), Deleter<&JNIEnv::DeleteGlobalRef>(env)); } template < class T > UniqueGlobalRef NewGlobalRef(JNIEnv& env, T* t) { return NewGlobalRef(env, t); } // Attempt to promote a weak reference to a strong one. Returns an empty result // if the weak reference has expired. template < template < RefDeletionMethod > class Deleter, class T, template < RefDeletionMethod > class WeakDeleter > UniqueGlobalRef NewGlobalRef(JNIEnv& env, const UniqueWeakGlobalRef& t) { jobject* obj = Wrap(env.NewGlobalRef(Unwrap(t))); CheckJavaException(env); return UniqueGlobalRef(reinterpret_cast(obj), Deleter<&JNIEnv::DeleteGlobalRef>(env)); } template < class T, template < RefDeletionMethod > class WeakDeleter > UniqueGlobalRef NewGlobalRef(JNIEnv& env, const UniqueWeakGlobalRef& t) { return NewGlobalRef(env, t); } template < class T, template < RefDeletionMethod > class Deleter > void DeleteGlobalRef(JNIEnv& env, UniqueGlobalRef&& ref) { env.DeleteGlobalRef(Unwrap(ref.release())); CheckJavaException(env); } template < class T > UniqueLocalRef NewLocalRef(JNIEnv& env, T* t) { jobject* obj = Wrap(env.NewLocalRef(Unwrap(t))); CheckJavaException(env); if (t && !obj) throw std::bad_alloc(); return UniqueLocalRef(reinterpret_cast(obj), DefaultRefDeleter<&JNIEnv::DeleteLocalRef>(env)); } // Attempt to promote a weak reference to a strong one. Returns an empty result // if the weak reference has expired. template < class T, template < RefDeletionMethod > class WeakDeleter > UniqueLocalRef NewLocalRef(JNIEnv& env, const UniqueWeakGlobalRef& t) { jobject* obj = Wrap(env.NewLocalRef(Unwrap(t))); CheckJavaException(env); return UniqueLocalRef(reinterpret_cast(obj), DefaultRefDeleter<&JNIEnv::DeleteLocalRef>(env)); } template < class T > void DeleteLocalRef(JNIEnv& env, UniqueLocalRef&& ref) { env.DeleteLocalRef(Unwrap(ref.release())); CheckJavaException(env); } inline void EnsureLocalCapacity(JNIEnv& env, jint capacity) { CheckJavaExceptionThenErrorCode(env, env.EnsureLocalCapacity(capacity)); } template < template < RefDeletionMethod > class Deleter, class T > UniqueWeakGlobalRef NewWeakGlobalRef(JNIEnv& env, T* t) { jobject* obj = Wrap(env.NewWeakGlobalRef(Unwrap(t))); CheckJavaException(env); if (t && !obj) throw std::bad_alloc(); return UniqueWeakGlobalRef(reinterpret_cast(obj), Deleter<&JNIEnv::DeleteWeakGlobalRef>(env)); } template < class T > UniqueWeakGlobalRef NewWeakGlobalRef(JNIEnv& env, T* t) { return NewWeakGlobalRef(env, t); } template < class T, template < RefDeletionMethod > class Deleter > void DeleteWeakGlobalRef(JNIEnv& env, UniqueWeakGlobalRef&& ref) { env.DeleteWeakGlobalRef(Unwrap(ref.release())); CheckJavaException(env); } inline bool IsSameObject(JNIEnv& env, jobject* ref1, jobject* ref2) { return CheckJavaException(env, env.IsSameObject(Unwrap(ref1), Unwrap(ref2))); } inline jobject& AllocObject(JNIEnv& env, jclass& clazz) { return *CheckJavaException(env, Wrap(env.AllocObject(Unwrap(clazz)))); } template < class... Args > jobject& NewObject(JNIEnv& env, jclass& clazz, jmethodID& method, Args&&... args) { return *CheckJavaException(env, Wrap(env.NewObject(Unwrap(clazz), Unwrap(method), Unwrap(std::forward(args))...))); } inline jclass& GetObjectClass(JNIEnv& env, jobject& obj) { return *CheckJavaException(env, Wrap(env.GetObjectClass(Unwrap(obj)))); } inline bool IsInstanceOf(JNIEnv& env, jobject* obj, jclass& clazz) { return CheckJavaException(env, env.IsInstanceOf(Unwrap(obj), Unwrap(clazz))) == JNI_TRUE; } inline jmethodID& GetMethodID(JNIEnv& env, jclass& clazz, const char* name, const char* sig) { return *CheckJavaException(env, Wrap(env.GetMethodID(Unwrap(clazz), name, sig))); } template < class R, class... Args > std::enable_if_t::value, R> CallMethod(JNIEnv& env, jobject* obj, jmethodID& method, Args&&... args) { return CheckJavaException(env, Wrap((env.*(TypedMethods::CallMethod))(Unwrap(obj), Unwrap(method), Unwrap(std::forward(args))...))); } template < class R, class... Args > std::enable_if_t::value, R> CallMethod(JNIEnv& env, jobject* obj, jmethodID& method, Args&&... args) { env.CallVoidMethod(Unwrap(obj), Unwrap(method), Unwrap(std::forward(args))...); CheckJavaException(env); } template < class R, class... Args > std::enable_if_t::value, R> CallNonvirtualMethod(JNIEnv& env, jobject* obj, jclass& clazz, jmethodID& method, Args&&... args) { return CheckJavaException(env, Wrap((env.*(TypedMethods::CallNonvirtualMethod))(Unwrap(obj), Unwrap(clazz), Unwrap(method), Unwrap(std::forward(args))...))); } template < class R, class... Args > std::enable_if_t::value, R> CallNonvirtualMethod(JNIEnv& env, jobject* obj, jclass& clazz, jmethodID& method, Args&&... args) { env.CallNonvirtualVoidMethod(Unwrap(obj), Unwrap(clazz), Unwrap(method), Unwrap(std::forward(args))...); CheckJavaException(env); } inline jfieldID& GetFieldID(JNIEnv& env, jclass& clazz, const char* name, const char* sig) { return *CheckJavaException(env, Wrap(env.GetFieldID(Unwrap(clazz), name, sig))); } template < class T > T GetField(JNIEnv& env, jobject* obj, jfieldID& field) { return CheckJavaException(env, Wrap((env.*(TypedMethods::GetField))(Unwrap(obj), Unwrap(field)))); } template < class T > void SetField(JNIEnv& env, jobject* obj, jfieldID& field, T value) { (env.*(TypedMethods::SetField))(Unwrap(obj), Unwrap(field), Unwrap(value)); CheckJavaException(env); } inline jmethodID& GetStaticMethodID(JNIEnv& env, jclass& clazz, const char* name, const char* sig) { return *CheckJavaException(env, Wrap(env.GetStaticMethodID(Unwrap(clazz), name, sig))); } template < class R, class... Args > std::enable_if_t::value, R> CallStaticMethod(JNIEnv& env, jclass& clazz, jmethodID& method, Args&&... args) { return CheckJavaException(env, Wrap((env.*(TypedMethods::CallStaticMethod))(Unwrap(clazz), Unwrap(method), Unwrap(std::forward(args))...))); } template < class R, class... Args > std::enable_if_t::value, R> CallStaticMethod(JNIEnv& env, jclass& clazz, jmethodID& method, Args&&... args) { env.CallStaticVoidMethod(Unwrap(clazz), Unwrap(method), Unwrap(std::forward(args))...); CheckJavaException(env); } inline jfieldID& GetStaticFieldID(JNIEnv& env, jclass& clazz, const char* name, const char* sig) { return *CheckJavaException(env, Wrap(env.GetStaticFieldID(Unwrap(clazz), name, sig))); } template < class T > T GetStaticField(JNIEnv& env, jclass& clazz, jfieldID& field) { return CheckJavaException(env, Wrap((env.*(TypedMethods::GetStaticField))(Unwrap(clazz), Unwrap(field)))); } template < class T > void SetStaticField(JNIEnv& env, jclass& clazz, jfieldID& field, T value) { (env.*(TypedMethods::SetStaticField))(Unwrap(clazz), Unwrap(field), Unwrap(value)); CheckJavaException(env); } inline jstring& NewString(JNIEnv& env, const char16_t* chars, jsize len) { return *CheckJavaException(env, Wrap(env.NewString(Unwrap(chars), Unwrap(len)))); } template < class Array > auto NewString(JNIEnv& env, const Array& chars) -> std::enable_if_t< IsArraylike::value, jstring& > { return NewString(env, ArraylikeData(chars), ArraylikeSize(chars)); } inline jsize GetStringLength(JNIEnv& env, jstring& string) { return CheckJavaException(env, Wrap(env.GetStringLength(Unwrap(string)))); } inline std::tuple GetStringChars(JNIEnv& env, jstring& string) { ::jboolean isCopy = JNI_FALSE; const char16_t* result = CheckJavaException(env, Wrap(env.GetStringChars(Unwrap(string), &isCopy))); return std::make_tuple(UniqueStringChars(result, StringCharsDeleter(env, string)), isCopy); } inline void ReleaseStringChars(JNIEnv& env, jstring& string, UniqueStringChars&& chars) { env.ReleaseStringChars(Unwrap(string), Unwrap(chars.release())); CheckJavaException(env); } inline jstring& NewStringUTF(JNIEnv& env, const char* bytes) { return *CheckJavaException(env, Wrap(env.NewStringUTF(bytes))); } inline jsize GetStringUTFLength(JNIEnv& env, jstring& string) { return CheckJavaException(env, Wrap(env.GetStringUTFLength(Unwrap(string)))); } inline std::tuple GetStringUTFChars(JNIEnv& env, jstring& string) { ::jboolean isCopy = JNI_FALSE; const char* result = CheckJavaException(env, env.GetStringUTFChars(Unwrap(string), &isCopy)); return std::make_tuple(UniqueStringUTFChars(result, StringUTFCharsDeleter(env, string)), isCopy); } inline void ReleaseStringUTFChars(JNIEnv& env, jstring& string, UniqueStringUTFChars&& chars) { env.ReleaseStringUTFChars(Unwrap(string), chars.release()); CheckJavaException(env); } inline void GetStringRegion(JNIEnv& env, jstring& string, jsize start, jsize len, char16_t* buf) { env.GetStringRegion(Unwrap(string), Unwrap(start), Unwrap(len), Unwrap(buf)); CheckJavaException(env); } template < class Array > auto GetStringRegion(JNIEnv& env, jstring& string, jsize start, Array& buf) -> std::enable_if_t< IsArraylike::value > { GetStringRegion(env, string, start, ArraylikeSize(buf), ArraylikeData(buf)); } inline void GetStringUTFRegion(JNIEnv& env, jstring& string, jsize start, jsize len, char* buf) { env.GetStringUTFRegion(Unwrap(string), Unwrap(start), Unwrap(len), buf); CheckJavaException(env); } template < class Array > auto GetStringUTFRegion(JNIEnv& env, jstring& string, jsize start, Array& buf) -> std::enable_if_t< IsArraylike::value > { GetStringUTFRegion(env, string, start, ArraylikeSize(buf), ArraylikeData(buf)); } inline std::tuple GetStringCritical(JNIEnv& env, jstring& string) { ::jboolean isCopy = JNI_FALSE; const char16_t* result = CheckJavaException(env, Wrap(env.GetStringCritical(Unwrap(string), &isCopy))); return std::make_tuple(UniqueStringCritical(result, StringCriticalDeleter(env, string)), isCopy); } inline void ReleaseStringCritical(JNIEnv& env, jstring& string, UniqueStringCritical&& chars) { env.ReleaseStringCritical(Unwrap(string), Unwrap(chars.release())); CheckJavaException(env); } template < class E > jsize GetArrayLength(JNIEnv& env, jarray& array) { return CheckJavaException(env, Wrap(env.GetArrayLength(Unwrap(array)))); } template < class E > jarray& NewArray(JNIEnv& env, jsize length) { return *CheckJavaException(env, Wrap*>((env.*(TypedMethods::NewArray))(Unwrap(length)))); } template < class E > std::tuple, bool> GetArrayElements(JNIEnv& env, jarray& array) { ::jboolean isCopy = JNI_FALSE; E* result = CheckJavaException(env, (env.*(TypedMethods::GetArrayElements))(Unwrap(array), &isCopy)); return std::make_tuple(UniqueArrayElements(result, ArrayElementsDeleter(env, array)), isCopy); } template < class E > void ReleaseArrayElements(JNIEnv& env, jarray& array, E* elems) { (env.*(TypedMethods::ReleaseArrayElements))(Unwrap(array), elems, JNI_COMMIT); CheckJavaException(env); } template < class E > void ReleaseArrayElements(JNIEnv& env, jarray& array, UniqueArrayElements&& elems) { (env.*(TypedMethods::ReleaseArrayElements))(Unwrap(array), elems.release(), 0); CheckJavaException(env); } template < class E > std::tuple, bool> GetPrimitiveArrayCritical(JNIEnv& env, jarray& array) { ::jboolean isCopy = JNI_FALSE; void* result = CheckJavaException(env, env.GetPrimitiveArrayCritical(Unwrap(array), &isCopy)); return std::make_tuple(UniquePrimitiveArrayCritical(result, PrimitiveArrayCriticalDeleter(env, array)), isCopy); } template < class E > void ReleasePrimitiveArrayCritical(JNIEnv& env, jarray& array, void* carray) { env.ReleasePrimitiveArrayCritical(Unwrap(array), carray, 0); CheckJavaException(env); } template < class E > void ReleasePrimitiveArrayCritical(JNIEnv& env, jarray& array, UniquePrimitiveArrayCritical&& carray) { env.ReleasePrimitiveArrayCritical(Unwrap(array), carray.release(), JNI_COMMIT); CheckJavaException(env); } template < class T > void GetArrayRegion(JNIEnv& env, jarray& array, jsize start, jsize len, T* buf) { (env.*(TypedMethods::GetArrayRegion))(Unwrap(array), Unwrap(start), Unwrap(len), buf); CheckJavaException(env); } template < class T, class Array > auto GetArrayRegion(JNIEnv& env, jarray& array, jsize start, Array& buf) -> std::enable_if_t< IsArraylike::value > { GetArrayRegion(env, array, start, ArraylikeSize(buf), ArraylikeData(buf)); } template < class T > void SetArrayRegion(JNIEnv& env, jarray& array, jsize start, jsize len, const T* buf) { (env.*(TypedMethods::SetArrayRegion))(Unwrap(array), Unwrap(start), Unwrap(len), buf); CheckJavaException(env); } template < class T, class Array > auto SetArrayRegion(JNIEnv& env, jarray& array, jsize start, const Array& buf) -> std::enable_if_t< IsArraylike::value > { SetArrayRegion(env, array, start, ArraylikeSize(buf), ArraylikeData(buf)); } inline jarray& NewObjectArray(JNIEnv& env, jsize length, jclass& elementClass, jobject* initialElement = nullptr) { return *CheckJavaException(env, Wrap*>(env.NewObjectArray(Unwrap(length), Unwrap(elementClass), Unwrap(initialElement)))); } inline jobject* GetObjectArrayElement(JNIEnv& env, jarray& array, jsize index) { return CheckJavaException(env, Wrap(env.GetObjectArrayElement(Unwrap(array), Unwrap(index)))); } inline void SetObjectArrayElement(JNIEnv& env, jarray& array, jsize index, jobject* value) { env.SetObjectArrayElement(Unwrap(array), Unwrap(index), Unwrap(value)); CheckJavaException(env); } template < class... Methods > inline void RegisterNatives(JNIEnv& env, jclass& clazz, const Methods&... methods) { ::JNINativeMethod unwrapped[sizeof...(methods)] = { Unwrap(methods)... }; CheckJavaExceptionThenErrorCode(env, env.RegisterNatives(Unwrap(clazz), unwrapped, sizeof...(methods))); } inline void UnregisterNatives(JNIEnv& env, jclass& clazz) { CheckJavaExceptionThenErrorCode(env, env.UnregisterNatives(Unwrap(clazz))); } inline UniqueMonitor MonitorEnter(JNIEnv& env, jobject* obj) { CheckJavaExceptionThenErrorCode(env, env.MonitorEnter(Unwrap(obj))); return UniqueMonitor(obj, MonitorDeleter(env)); } inline void MonitorExit(JNIEnv& env, UniqueMonitor&& monitor) { CheckJavaExceptionThenErrorCode(env, env.MonitorExit(Unwrap(monitor.release()))); } inline JavaVM& GetJavaVM(JNIEnv& env) { JavaVM* result = nullptr; CheckJavaExceptionThenErrorCode(env, env.GetJavaVM(&result)); return *result; } inline jobject& NewDirectByteBuffer(JNIEnv& env, void* address, jlong capacity) { return *CheckJavaException(env, Wrap(env.NewDirectByteBuffer(address, Unwrap(capacity)))); } inline void* GetDirectBufferAddress(JNIEnv& env, jobject& buf) { return CheckJavaException(env, env.GetDirectBufferAddress(Unwrap(buf))); } inline jlong GetDirectBufferCapacity(JNIEnv& env, jobject& buf) { return CheckJavaException(env, env.GetDirectBufferCapacity(Unwrap(buf))); } inline jobjectRefType GetObjectRefType(JNIEnv& env, jobject* obj) { return env.GetObjectRefType(Unwrap(obj)); } namespace { // Some implementations type the parameter as JNIEnv**, others as void**. // See https://bugs.openjdk.java.net/browse/JDK-6569899 struct JNIEnvCast { using FunVoid = jint (JavaVM::*)(void**, void*); using FunEnv = jint (JavaVM::*)(JNIEnv**, void*); template ::value>> void** operator()(JNIEnv** env, Fun) noexcept { return reinterpret_cast(env); } template ::value>> JNIEnv** operator()(JNIEnv** env, Fun) noexcept { return env; } }; } inline UniqueEnv AttachCurrentThread(JavaVM& vm) { JNIEnv* result; CheckErrorCode(vm.AttachCurrentThread(JNIEnvCast()(&result, &JavaVM::AttachCurrentThread), nullptr)); return UniqueEnv(result, JNIEnvDeleter(vm)); } inline void DetachCurrentThread(JavaVM& vm, UniqueEnv&& env) { env.release(); CheckErrorCode(vm.DetachCurrentThread()); } inline JNIEnv& GetEnv(JavaVM& vm, version version = jni_version_1_1) { JNIEnv* env = nullptr; CheckErrorCode(vm.GetEnv(reinterpret_cast(&env), Unwrap(version))); return *env; } inline UniqueEnv GetAttachedEnv(JavaVM& vm, version version = jni_version_1_1) { JNIEnv* env = nullptr; auto code = vm.GetEnv(reinterpret_cast(&env), Unwrap(version)); switch (code) { case JNI_OK: return UniqueEnv(env,JNIEnvDeleter(vm, false)); case JNI_EDETACHED: return AttachCurrentThread(vm); default: CheckErrorCode(code); return nullptr; } } }