#ifndef CLEVERSYNC_JNI_HPP #define CLEVERSYNC_JNI_HPP #include #include #include #include #define ABORT_IF_NOT_OK(x) (::jni::internal::_abort_if_not_ok(__FILE__, __LINE__, (x))) #define ABORT_IF_NULL(env, x) (::jni::internal::_abort_if_null(__FILE__, __LINE__, (env), (x))) namespace jni { namespace internal { void _abort_if_not_ok(const char *file, int line, jint ret); void _abort_with_exception(const char* file, int line, JNIEnv* env); JNIEnv* NonFatalAttachCurrentThread(); } // namespace internal template class Ref { public: constexpr Ref() : ptr_(nullptr) {} constexpr Ref(std::nullptr_t) : ptr_(nullptr) {} Ref(const Ref&) = delete; [[nodiscard]] T get() const { return ptr_; } [[nodiscard]] T release() { auto ret = release_to_local(); ptr_ = nullptr; return ret; } void reset() { del(); ptr_ = nullptr; } explicit operator bool() const { return ptr_ != nullptr; } protected: Ref(JNIEnv* env, T ptr): env_(env), ptr_(ptr) {} virtual ~Ref() = default; virtual T release_to_local() = 0; virtual void del() = 0; JNIEnv* env_; T ptr_; }; constexpr jint JNI_VERSION = JNI_VERSION_1_4; JNIEnv* AttachCurrentThread(); JNIEnv* OnLoad(JavaVM* vm); template class LocalRef : public Ref { public: constexpr LocalRef(): Ref() {} constexpr LocalRef(std::nullptr_t): Ref() {} LocalRef(JNIEnv* env, T ptr): Ref(env, ptr) {} ~LocalRef() override { free(); } LocalRef(LocalRef &&ref) noexcept : Ref(ref.env_, ref.release()) {} LocalRef& operator=(const Ref& other) = delete; LocalRef& operator=(LocalRef &&ref) noexcept { free(); this->env_ = ref.env_; this->ptr_ = ref.release(); return *this; } protected: T release_to_local() override { return this->ptr_; } void del() override { free(); } private: void free() { if (this->ptr_) this->env_->DeleteLocalRef(this->ptr_); } }; template class ParamRef : public Ref { public: ParamRef(JNIEnv* env, T ptr) : Ref(env, ptr) {} ~ParamRef() override = default; ParamRef& operator=(const Ref& other) = delete; protected: T release_to_local() override { if (this->ptr_) return static_cast(this->env_->NewLocalRef(static_cast(this->ptr_))); return nullptr; } void del() override {} }; template class GlobalRef : public Ref { public: constexpr GlobalRef() : Ref() {} constexpr GlobalRef(std::nullptr_t) : Ref() {} GlobalRef(JNIEnv* env, T ptr) : Ref() { if (ptr) { if (!env) env = AttachCurrentThread(); this->ptr_ = static_cast(env->NewGlobalRef(static_cast(ptr))); } } GlobalRef(const Ref& other) : Ref() { if (other) { auto* env = AttachCurrentThread(); this->ptr_ = static_cast(env->NewGlobalRef(static_cast(other.get()))); } } GlobalRef(GlobalRef&& other) noexcept : Ref(nullptr, other.ptr_) { other.ptr_ = nullptr; return *this; } ~GlobalRef() override { free(); } GlobalRef& operator=(const Ref& other) { free(); if (other) { auto* env = AttachCurrentThread(); this->ptr_ = other ? static_cast(env->NewGlobalRef(static_cast(other.get()))) : nullptr; } else { this->ptr_ = nullptr; } return *this; } GlobalRef& operator=(GlobalRef&& other) { free(); this->ptr_ = other.ptr_; other.ptr_ = nullptr; } protected: T release_to_local() override { if (this->ptr_) { auto* env = AttachCurrentThread(); auto ret = static_cast(env->NewLocalRef(static_cast(this->ptr_))); env->DeleteGlobalRef(static_cast(this->ptr_)); return ret; } return nullptr; } void del() override { free(); } private: void free() { if (this->ptr_) { auto* env = internal::NonFatalAttachCurrentThread(); if (env) env->DeleteGlobalRef(static_cast(this->ptr_)); } } }; LocalRef FindClass(JNIEnv *env, const char *name); LocalRef ExceptionOccurred(JNIEnv* env); template LocalRef CallObjectMethod(JNIEnv* env, const Ref& object, jmethodID method, Params... params) { return {env, static_cast(env->CallObjectMethod(object.get(), method, params...))}; } template LocalRef CallStaticObjectMethod(JNIEnv* env, const Ref& clazz, jmethodID method, Params... params) { return {env, static_cast(env->CallStaticObjectMethod(clazz.get(), method, params...))}; } std::string StringToUTF8(JNIEnv* env, const Ref& str); LocalRef UTF8ToString(JNIEnv* env, const std::string& str); LocalRef UTF8ToString(JNIEnv* env, const char* str); LocalRef VectorToByteArray(JNIEnv* env, const std::vector& data); std::vector ByteArrayToVector(JNIEnv* env, const Ref& data); LocalRef CreateArray(JNIEnv* env, const Ref& element_class, std::vector> objects); namespace internal { template inline void _abort_if_null(const char* file, int line, JNIEnv* env, const jni::Ref& ref) { if (ref) [[likely]] return; _abort_with_exception(file, line, env); } inline void _abort_if_null(const char* file, int line, JNIEnv* env, jmethodID ref) { if (ref) [[likely]] return; _abort_with_exception(file, line, env); } } // namespace internal } // namespace jni #endif // CLEVERSYNC_JNI_HPP