// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QJNIOBJECT_H #define QJNIOBJECT_H #include #if defined(Q_QDOC) || defined(Q_OS_ANDROID) #include #include #include QT_BEGIN_NAMESPACE class QJniObjectPrivate; class Q_CORE_EXPORT QJniObject { public: QJniObject(); explicit QJniObject(const char *className); explicit QJniObject(const char *className, const char *signature, ...); template>...>>* = nullptr #endif > explicit QJniObject(const char *className, Args &&...args) : QJniObject(className, QtJniTypes::constructorSignature().data(), std::forward(args)...) {} explicit QJniObject(jclass clazz); explicit QJniObject(jclass clazz, const char *signature, ...); template>...>>* = nullptr #endif > explicit QJniObject(jclass clazz, Args &&...args) : QJniObject(clazz, QtJniTypes::constructorSignature().data(), std::forward(args)...) {} QJniObject(jobject globalRef); inline QJniObject(QtJniTypes::Object wrapper) noexcept : QJniObject(jobject(wrapper)) {} ~QJniObject(); template static inline QJniObject construct(Args &&...args) { return QJniObject(QtJniTypes::className().data(), QtJniTypes::constructorSignature().data(), std::forward(args)...); } jobject object() const; template T object() const { QtJniTypes::assertObjectType(); return static_cast(javaObject()); } jclass objectClass() const; QByteArray className() const; template auto callMethod(const char *methodName, const char *signature, Args &&...args) const { if constexpr (QtJniTypes::isObjectType()) { return callObjectMethod(methodName, signature, std::forward(args)...); } else { QtJniTypes::assertPrimitiveType(); QJniEnvironment env; jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature); if (id) { if constexpr (std::is_same::value) { callVoidMethodV(env.jniEnv(), id, std::forward(args)...); env.checkAndClearExceptions(); } else { Ret res{}; callMethodForType(env.jniEnv(), res, object(), id, std::forward(args)...); if (env.checkAndClearExceptions()) res = {}; return res; } } if constexpr (!std::is_same::value) return Ret{}; } } template auto callMethod(const char *methodName, Args &&...args) const { constexpr auto signature = QtJniTypes::methodSignature(); if constexpr (std::is_same::value) { callMethod(methodName, signature.data(), std::forward(args)...); } else { return callMethod(methodName, signature.data(), std::forward(args)...); } } template QJniObject callObjectMethod(const char *methodName, Args &&...args) const { QtJniTypes::assertObjectType(); constexpr auto signature = QtJniTypes::methodSignature(); return callObjectMethod(methodName, signature.data(), std::forward(args)...); } QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const; template static auto callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args) { QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); return callStaticMethod(clazz, methodName, signature, std::forward(args)...); } template static auto callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args) { QJniEnvironment env; jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true); return callStaticMethod(clazz, id, std::forward(args)...); } template static auto callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args) { if constexpr (QtJniTypes::isObjectType()) { return callStaticObjectMethod(clazz, methodId, std::forward(args)...); } else { QtJniTypes::assertPrimitiveType(); QJniEnvironment env; if (clazz && methodId) { if constexpr (std::is_same::value) { callStaticMethodForVoid(env.jniEnv(), clazz, methodId, std::forward(args)...); env.checkAndClearExceptions(); } else { Ret res{}; callStaticMethodForType(env.jniEnv(), res, clazz, methodId, std::forward(args)...); if (env.checkAndClearExceptions()) res = {}; return res; } } if constexpr (!std::is_same::value) return Ret{}; } } template static auto callStaticMethod(const char *className, const char *methodName, Args &&...args) { QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); return callStaticMethod(clazz, methodName, std::forward(args)...); } template static auto callStaticMethod(jclass clazz, const char *methodName, Args &&...args) { constexpr auto signature = QtJniTypes::methodSignature(); return callStaticMethod(clazz, methodName, signature.data(), std::forward(args)...); } static QJniObject callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...); static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...); static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...); template static QJniObject callStaticObjectMethod(const char *className, const char *methodName, Args &&...args) { QtJniTypes::assertObjectType(); constexpr auto signature = QtJniTypes::methodSignature(); return callStaticObjectMethod(className, methodName, signature.data(), std::forward(args)...); } template static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args) { QtJniTypes::assertObjectType(); constexpr auto signature = QtJniTypes::methodSignature(); return callStaticObjectMethod(clazz, methodName, signature.data(), std::forward(args)...); } template auto getField(const char *fieldName) const { if constexpr (QtJniTypes::isObjectType()) { return getObjectField(fieldName); } else { QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); if (id) { getFieldForType(env.jniEnv(), res, object(), id); if (env.checkAndClearExceptions()) res = {}; } return res; } } template static auto getStaticField(const char *className, const char *fieldName) { if constexpr (QtJniTypes::isObjectType()) { return getStaticObjectField(className, fieldName); } else { QtJniTypes::assertPrimitiveType(); QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); T res{}; if (!clazz) return res; constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), clazz, QJniObject::toBinaryEncClassName(className), fieldName, signature, true); if (!id) return res; getStaticFieldForType(env.jniEnv(), res, clazz, id); if (env.checkAndClearExceptions()) res = {}; return res; } } template static auto getStaticField(jclass clazz, const char *fieldName) { if constexpr (QtJniTypes::isObjectType()) { return getStaticObjectField(clazz, fieldName); } else { QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); if (id) { getStaticFieldForType(env.jniEnv(), res, clazz, id); if (env.checkAndClearExceptions()) res = {}; } return res; } } template QJniObject getObjectField(const char *fieldName) const { QtJniTypes::assertObjectType(); constexpr auto signature = QtJniTypes::fieldSignature(); return getObjectField(fieldName, signature); } QJniObject getObjectField(const char *fieldName, const char *signature) const; template static QJniObject getStaticObjectField(const char *className, const char *fieldName) { QtJniTypes::assertObjectType(); constexpr auto signature = QtJniTypes::fieldSignature(); return getStaticObjectField(className, fieldName, signature); } static QJniObject getStaticObjectField(const char *className, const char *fieldName, const char *signature); template static QJniObject getStaticObjectField(jclass clazz, const char *fieldName) { QtJniTypes::assertObjectType(); constexpr auto signature = QtJniTypes::fieldSignature(); return getStaticObjectField(clazz, fieldName, signature); } static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *signature); template void setField(const char *fieldName, T value) { QtJniTypes::assertType(); QJniEnvironment env; constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); if (id) { setFieldForType(env.jniEnv(), object(), id, value); env.checkAndClearExceptions(); } } template void setField(const char *fieldName, const char *signature, T value) { QtJniTypes::assertType(); QJniEnvironment env; jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); if (id) { setFieldForType(env.jniEnv(), object(), id, value); env.checkAndClearExceptions(); } } template static void setStaticField(const char *className, const char *fieldName, T value) { QtJniTypes::assertType(); QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); if (!clazz) return; constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, signature, true); if (!id) return; setStaticFieldForType(env.jniEnv(), clazz, id, value); env.checkAndClearExceptions(); } template static void setStaticField(const char *className, const char *fieldName, const char *signature, T value) { QtJniTypes::assertType(); QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); if (!clazz) return; jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, signature, true); if (id) { setStaticFieldForType(env.jniEnv(), clazz, id, value); env.checkAndClearExceptions(); } } template static void setStaticField(jclass clazz, const char *fieldName, const char *signature, T value) { QtJniTypes::assertType(); QJniEnvironment env; jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); if (id) { setStaticFieldForType(env.jniEnv(), clazz, id, value); env.checkAndClearExceptions(); } } template static void setStaticField(jclass clazz, const char *fieldName, T value) { QtJniTypes::assertType(); QJniEnvironment env; constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); if (id) { setStaticFieldForType(env.jniEnv(), clazz, id, value); env.checkAndClearExceptions(); } } static QJniObject fromString(const QString &string); QString toString() const; static bool isClassAvailable(const char *className); bool isValid() const; // This function takes ownership of the jobject and releases the local ref. before returning. static QJniObject fromLocalRef(jobject lref); template QJniObject &operator=(T obj) { QtJniTypes::assertType(); assign(static_cast(obj)); return *this; } private: struct QVaListPrivate { operator va_list &() const { return m_args; } va_list &m_args; }; QJniObject(const char *className, const char *signature, const QVaListPrivate &args); QJniObject(jclass clazz, const char *signature, const QVaListPrivate &args); static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false); static QByteArray toBinaryEncClassName(const QByteArray &className); static QJniObject getCleanJniObject(jobject obj); static jfieldID getCachedFieldID(JNIEnv *env, jclass clazz, const QByteArray &className, const char *name, const char *signature, bool isStatic = false); jfieldID getCachedFieldID(JNIEnv *env, const char *name, const char *signature, bool isStatic = false) const; static jmethodID getCachedMethodID(JNIEnv *env, jclass clazz, const QByteArray &className, const char *name, const char *signature, bool isStatic = false); jmethodID getCachedMethodID(JNIEnv *env, const char *name, const char *signature, bool isStatic = false) const; static jfieldID getFieldID(JNIEnv *env, jclass clazz, const char *name, const char *signature, bool isStatic = false); static jmethodID getMethodID(JNIEnv *env, jclass clazz, const char *name, const char *signature, bool isStatic = false); void callVoidMethodV(JNIEnv *env, jmethodID id, ...) const; QJniObject callObjectMethodV(const char *methodName, const char *signature, va_list args) const; static QJniObject callStaticObjectMethodV(const char *className, const char *methodName, const char *signature, va_list args); static QJniObject callStaticObjectMethodV(jclass clazz, const char *methodName, const char *signature, va_list args); bool isSameObject(jobject obj) const; bool isSameObject(const QJniObject &other) const; void assign(jobject obj); jobject javaObject() const; friend bool operator==(const QJniObject &, const QJniObject &); friend bool operator!=(const QJniObject&, const QJniObject&); template static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, ...) { va_list args = {}; va_start(args, id); if constexpr(std::is_same::value) res = env->CallBooleanMethodV(obj, id, args); else if constexpr(std::is_same::value) res = env->CallByteMethodV(obj, id, args); else if constexpr(std::is_same::value) res = env->CallCharMethodV(obj, id, args); else if constexpr(std::is_same::value) res = env->CallShortMethodV(obj, id, args); else if constexpr(std::is_same::value) res = env->CallIntMethodV(obj, id, args); else if constexpr(std::is_same::value) res = env->CallLongMethodV(obj, id, args); else if constexpr(std::is_same::value) res = env->CallFloatMethodV(obj, id, args); else if constexpr(std::is_same::value) res = env->CallDoubleMethodV(obj, id, args); else QtJniTypes::staticAssertTypeMismatch(); va_end(args); } template static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, jmethodID id, ...) { va_list args = {}; va_start(args, id); if constexpr(std::is_same::value) res = env->CallStaticBooleanMethodV(clazz, id, args); else if constexpr(std::is_same::value) res = env->CallStaticByteMethodV(clazz, id, args); else if constexpr(std::is_same::value) res = env->CallStaticCharMethodV(clazz, id, args); else if constexpr(std::is_same::value) res = env->CallStaticShortMethodV(clazz, id, args); else if constexpr(std::is_same::value) res = env->CallStaticIntMethodV(clazz, id, args); else if constexpr(std::is_same::value) res = env->CallStaticLongMethodV(clazz, id, args); else if constexpr(std::is_same::value) res = env->CallStaticFloatMethodV(clazz, id, args); else if constexpr(std::is_same::value) res = env->CallStaticDoubleMethodV(clazz, id, args); else QtJniTypes::staticAssertTypeMismatch(); va_end(args); } static void callStaticMethodForVoid(JNIEnv *env, jclass clazz, jmethodID id, ...) { va_list args; va_start(args, id); env->CallStaticVoidMethodV(clazz, id, args); va_end(args); } template static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id) { if constexpr(std::is_same::value) res = env->GetBooleanField(obj, id); else if constexpr(std::is_same::value) res = env->GetByteField(obj, id); else if constexpr(std::is_same::value) res = env->GetCharField(obj, id); else if constexpr(std::is_same::value) res = env->GetShortField(obj, id); else if constexpr(std::is_same::value) res = env->GetIntField(obj, id); else if constexpr(std::is_same::value) res = env->GetLongField(obj, id); else if constexpr(std::is_same::value) res = env->GetFloatField(obj, id); else if constexpr(std::is_same::value) res = env->GetDoubleField(obj, id); else QtJniTypes::staticAssertTypeMismatch(); } template static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id) { if constexpr(std::is_same::value) res = env->GetStaticBooleanField(clazz, id); else if constexpr(std::is_same::value) res = env->GetStaticByteField(clazz, id); else if constexpr(std::is_same::value) res = env->GetStaticCharField(clazz, id); else if constexpr(std::is_same::value) res = env->GetStaticShortField(clazz, id); else if constexpr(std::is_same::value) res = env->GetStaticIntField(clazz, id); else if constexpr(std::is_same::value) res = env->GetStaticLongField(clazz, id); else if constexpr(std::is_same::value) res = env->GetStaticFloatField(clazz, id); else if constexpr(std::is_same::value) res = env->GetStaticDoubleField(clazz, id); else QtJniTypes::staticAssertTypeMismatch(); } template static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value) { if constexpr(std::is_same::value) env->SetBooleanField(obj, id, value); else if constexpr(std::is_same::value) env->SetByteField(obj, id, value); else if constexpr(std::is_same::value) env->SetCharField(obj, id, value); else if constexpr(std::is_same::value) env->SetShortField(obj, id, value); else if constexpr(std::is_same::value) env->SetIntField(obj, id, value); else if constexpr(std::is_same::value) env->SetLongField(obj, id, value); else if constexpr(std::is_same::value) env->SetFloatField(obj, id, value); else if constexpr(std::is_same::value) env->SetDoubleField(obj, id, value); else if constexpr(std::is_convertible::value) env->SetObjectField(obj, id, value); else QtJniTypes::staticAssertTypeMismatch(); } template static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value) { if constexpr(std::is_same::value) env->SetStaticBooleanField(clazz, id, value); else if constexpr(std::is_same::value) env->SetStaticByteField(clazz, id, value); else if constexpr(std::is_same::value) env->SetStaticCharField(clazz, id, value); else if constexpr(std::is_same::value) env->SetStaticShortField(clazz, id, value); else if constexpr(std::is_same::value) env->SetStaticIntField(clazz, id, value); else if constexpr(std::is_same::value) env->SetStaticLongField(clazz, id, value); else if constexpr(std::is_same::value) env->SetStaticFloatField(clazz, id, value); else if constexpr(std::is_same::value) env->SetStaticDoubleField(clazz, id, value); else if constexpr(std::is_convertible::value) env->SetStaticObjectField(clazz, id, value); else QtJniTypes::staticAssertTypeMismatch(); } friend QJniObjectPrivate; QSharedPointer d; }; inline bool operator==(const QJniObject &obj1, const QJniObject &obj2) { return obj1.isSameObject(obj2); } inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2) { return !obj1.isSameObject(obj2); } QT_END_NAMESPACE #endif #endif // QJNIOBJECT_H