diff options
Diffstat (limited to 'src/corelib/kernel/qjniobject.h')
-rw-r--r-- | src/corelib/kernel/qjniobject.h | 791 |
1 files changed, 519 insertions, 272 deletions
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 3c7ca13ff2..589f6489f7 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -9,7 +9,6 @@ #if defined(Q_QDOC) || defined(Q_OS_ANDROID) #include <jni.h> #include <QtCore/qjnienvironment.h> -#include <QtCore/qjnitypes.h> QT_BEGIN_NAMESPACE @@ -17,6 +16,51 @@ class QJniObjectPrivate; class Q_CORE_EXPORT QJniObject { + friend class QJniArrayBase; + + template <typename ...Args> + struct LocalFrame { + mutable JNIEnv *env; + bool hasFrame = false; + explicit LocalFrame(JNIEnv *env = nullptr) noexcept + : env(env) + { + } + ~LocalFrame() + { + if (hasFrame) + env->PopLocalFrame(nullptr); + } + template <typename T> + auto newLocalRef(jobject object) + { + if (!hasFrame) { + if (jniEnv()->PushLocalFrame(sizeof...(Args)) < 0) + return T{}; // JVM is out of memory, avoid making matters worse + hasFrame = true; + } + return static_cast<T>(jniEnv()->NewLocalRef(object)); + } + template <typename T> + auto newLocalRef(const QJniObject &object) + { + return newLocalRef<T>(object.template object<T>()); + } + JNIEnv *jniEnv() const + { + if (!env) + env = QJniEnvironment::getJniEnv(); + return env; + } + bool checkAndClearExceptions() + { + return env ? QJniEnvironment::checkAndClearExceptions(env) : false; + } + template <typename T> + auto convertToJni(T &&value); + template <typename T> + auto convertFromJni(QJniObject &&object); + }; public: QJniObject(); explicit QJniObject(const char *className); @@ -27,9 +71,17 @@ public: #endif > explicit QJniObject(const char *className, Args &&...args) + : QJniObject(LocalFrame<Args...>{}, className, std::forward<Args>(args)...) + { + } +private: + template<typename ...Args> + explicit QJniObject(LocalFrame<Args...> localFrame, const char *className, Args &&...args) : QJniObject(className, QtJniTypes::constructorSignature<Args...>().data(), - std::forward<Args>(args)...) - {} + localFrame.convertToJni(std::forward<Args>(args))...) + { + } +public: explicit QJniObject(jclass clazz); explicit QJniObject(jclass clazz, const char *signature, ...); template<typename ...Args @@ -42,15 +94,21 @@ public: std::forward<Args>(args)...) {} QJniObject(jobject globalRef); - inline QJniObject(QtJniTypes::Object wrapper) noexcept : QJniObject(jobject(wrapper)) {} + + QJniObject(const QJniObject &other) noexcept = default; + QJniObject(QJniObject &&other) noexcept = default; + QJniObject &operator=(const QJniObject &other) noexcept = default; + QJniObject &operator=(QJniObject &&other) noexcept = default; + ~QJniObject(); template<typename Class, typename ...Args> static inline QJniObject construct(Args &&...args) { - return QJniObject(QtJniTypes::className<Class>().data(), + LocalFrame<Args...> frame; + return QJniObject(QtJniTypes::Traits<Class>::className().data(), QtJniTypes::constructorSignature<Args...>().data(), - std::forward<Args>(args)...); + frame.convertToJni(std::forward<Args>(args))...); } jobject object() const; @@ -63,49 +121,61 @@ public: jclass objectClass() const; QByteArray className() const; - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<Ret> = true +#endif + > auto callMethod(const char *methodName, const char *signature, Args &&...args) const { + LocalFrame<Args...> frame(jniEnv()); if constexpr (QtJniTypes::isObjectType<Ret>()) { - return callObjectMethod(methodName, signature, std::forward<Args>(args)...); + return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature, + frame.convertToJni(std::forward<Args>(args))...)); } else { - QtJniTypes::assertPrimitiveType<Ret>(); - QJniEnvironment env; - jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature); + jmethodID id = getCachedMethodID(frame.jniEnv(), methodName, signature); if (id) { - if constexpr (std::is_same<Ret, void>::value) { - callVoidMethodV(env.jniEnv(), id, std::forward<Args>(args)...); - env.checkAndClearExceptions(); + if constexpr (std::is_same_v<Ret, void>) { + callVoidMethodV(frame.jniEnv(), id, + frame.convertToJni(std::forward<Args>(args))...); + frame.checkAndClearExceptions(); } else { Ret res{}; - callMethodForType<Ret>(env.jniEnv(), res, object(), id, std::forward<Args>(args)...); - if (env.checkAndClearExceptions()) + callMethodForType<Ret>(frame.jniEnv(), res, object(), id, + frame.convertToJni(std::forward<Args>(args))...); + if (frame.checkAndClearExceptions()) res = {}; return res; } } - if constexpr (!std::is_same<Ret, void>::value) + if constexpr (!std::is_same_v<Ret, void>) return Ret{}; } } - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > auto callMethod(const char *methodName, Args &&...args) const { constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); - if constexpr (std::is_same<Ret, void>::value) { - callMethod<void>(methodName, signature.data(), std::forward<Args>(args)...); - } else { - return callMethod<Ret>(methodName, signature.data(), std::forward<Args>(args)...); - } + return callMethod<Ret>(methodName, signature.data(), std::forward<Args>(args)...); } - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > QJniObject callObjectMethod(const char *methodName, Args &&...args) const { QtJniTypes::assertObjectType<Ret>(); constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); - return callObjectMethod(methodName, signature.data(), std::forward<Args>(args)...); + LocalFrame<Args...> frame(jniEnv()); + return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature, + frame.convertToJni(std::forward<Args>(args))...)); } QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const; @@ -113,58 +183,91 @@ public: template <typename Ret, typename ...Args> static auto callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args) { - QJniEnvironment env; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); + JNIEnv *env = QJniEnvironment::getJniEnv(); + jclass clazz = QJniObject::loadClass(className, env); return callStaticMethod<Ret>(clazz, methodName, signature, std::forward<Args>(args)...); } template <typename Ret, typename ...Args> 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<Ret, Args...>(clazz, id, std::forward<Args>(args)...); + JNIEnv *env = QJniEnvironment::getJniEnv(); + jmethodID id = clazz ? getMethodID(env, clazz, methodName, signature, true) + : 0; + return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...); } - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<Ret> = true +#endif + > static auto callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args) { + LocalFrame<Args...> frame; if constexpr (QtJniTypes::isObjectType<Ret>()) { - return callStaticObjectMethod(clazz, methodId, std::forward<Args>(args)...); + return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodId, + frame.convertToJni(std::forward<Args>(args))...)); } else { - QtJniTypes::assertPrimitiveType<Ret>(); - QJniEnvironment env; if (clazz && methodId) { - if constexpr (std::is_same<Ret, void>::value) { - callStaticMethodForVoid(env.jniEnv(), clazz, methodId, std::forward<Args>(args)...); - env.checkAndClearExceptions(); + if constexpr (std::is_same_v<Ret, void>) { + callStaticMethodForVoid(frame.jniEnv(), clazz, methodId, + frame.convertToJni(std::forward<Args>(args))...); + frame.checkAndClearExceptions(); } else { Ret res{}; - callStaticMethodForType<Ret>(env.jniEnv(), res, clazz, methodId, std::forward<Args>(args)...); - if (env.checkAndClearExceptions()) + callStaticMethodForType<Ret>(frame.jniEnv(), res, clazz, methodId, + frame.convertToJni(std::forward<Args>(args))...); + if (frame.checkAndClearExceptions()) res = {}; return res; } } - if constexpr (!std::is_same<Ret, void>::value) + if constexpr (!std::is_same_v<Ret, void>) return Ret{}; } } - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > static auto callStaticMethod(const char *className, const char *methodName, Args &&...args) { - QJniEnvironment env; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); - return callStaticMethod<Ret, Args...>(clazz, methodName, std::forward<Args>(args)...); + JNIEnv *env = QJniEnvironment::getJniEnv(); + jclass clazz = QJniObject::loadClass(className, env); + const jmethodID id = clazz ? getMethodID(env, clazz, methodName, + QtJniTypes::methodSignature<Ret, Args...>().data(), true) + : 0; + return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...); } - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > static auto callStaticMethod(jclass clazz, const char *methodName, Args &&...args) { constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); return callStaticMethod<Ret>(clazz, methodName, signature.data(), std::forward<Args>(args)...); } + template <typename Klass, typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > + static auto callStaticMethod(const char *methodName, Args &&...args) + { + JNIEnv *env = QJniEnvironment::getJniEnv(); + const jclass clazz = QJniObject::loadClass(QtJniTypes::Traits<Klass>::className().data(), + env); + const jmethodID id = clazz ? getMethodID(env, clazz, methodName, + QtJniTypes::methodSignature<Ret, Args...>().data(), true) + : 0; + return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...); + } static QJniObject callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...); @@ -175,103 +278,128 @@ public: static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...); - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > static QJniObject callStaticObjectMethod(const char *className, const char *methodName, Args &&...args) { QtJniTypes::assertObjectType<Ret>(); constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); - return callStaticObjectMethod(className, methodName, signature.data(), std::forward<Args>(args)...); + LocalFrame<Args...> frame; + return frame.template convertFromJni<Ret>(callStaticObjectMethod(className, methodName, signature.data(), + frame.convertToJni(std::forward<Args>(args))...)); } - template <typename Ret, typename ...Args> + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args) { QtJniTypes::assertObjectType<Ret>(); constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); - return callStaticObjectMethod(clazz, methodName, signature.data(), std::forward<Args>(args)...); + LocalFrame<Args...> frame; + return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodName, signature.data(), + frame.convertToJni(std::forward<Args>(args))...)); } - template <typename T> auto getField(const char *fieldName) const + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + auto getField(const char *fieldName) const { + LocalFrame<T> frame(jniEnv()); if constexpr (QtJniTypes::isObjectType<T>()) { - return getObjectField<T>(fieldName); + return frame.template convertFromJni<T>(getObjectField<T>(fieldName)); } else { - QtJniTypes::assertPrimitiveType<T>(); - QJniEnvironment env; T res{}; constexpr auto signature = QtJniTypes::fieldSignature<T>(); - jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); + jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature); if (id) { - getFieldForType<T>(env.jniEnv(), res, object(), id); - if (env.checkAndClearExceptions()) + getFieldForType<T>(frame.jniEnv(), res, object(), id); + if (frame.checkAndClearExceptions()) res = {}; } return res; } } - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > static auto getStaticField(const char *className, const char *fieldName) { + LocalFrame<T> frame; if constexpr (QtJniTypes::isObjectType<T>()) { - return getStaticObjectField<T>(className, fieldName); + return frame.template convertFromJni<T>(getStaticObjectField<T>(className, fieldName)); } else { - QtJniTypes::assertPrimitiveType<T>(); - QJniEnvironment env; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); - T res{}; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); if (!clazz) - return res; - - constexpr auto signature = QtJniTypes::fieldSignature<T>(); - jfieldID id = getCachedFieldID(env.jniEnv(), clazz, - QJniObject::toBinaryEncClassName(className), - fieldName, - signature, true); - if (!id) - return res; - - getStaticFieldForType<T>(env.jniEnv(), res, clazz, id); - if (env.checkAndClearExceptions()) - res = {}; - return res; + return T{}; + return getStaticField<T>(clazz, fieldName); } } - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > static auto getStaticField(jclass clazz, const char *fieldName) { + LocalFrame<T> frame; if constexpr (QtJniTypes::isObjectType<T>()) { - return getStaticObjectField<T>(clazz, fieldName); + return frame.template convertFromJni<T>(getStaticObjectField<T>(clazz, fieldName)); } else { - QtJniTypes::assertPrimitiveType<T>(); - QJniEnvironment env; T res{}; constexpr auto signature = QtJniTypes::fieldSignature<T>(); - jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); + jfieldID id = getFieldID(frame.jniEnv(), clazz, fieldName, signature, true); if (id) { - getStaticFieldForType<T>(env.jniEnv(), res, clazz, id); - if (env.checkAndClearExceptions()) + getStaticFieldForType<T>(frame.jniEnv(), res, clazz, id); + if (frame.checkAndClearExceptions()) res = {}; } return res; } } - template <typename T> + template <typename Klass, typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + static auto getStaticField(const char *fieldName) + { + return getStaticField<T>(QtJniTypes::Traits<Klass>::className(), fieldName); + } + + template <typename T +#ifndef Q_QDOC + , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true +#endif + > QJniObject getObjectField(const char *fieldName) const { - QtJniTypes::assertObjectType<T>(); constexpr auto signature = QtJniTypes::fieldSignature<T>(); return getObjectField(fieldName, signature); } QJniObject getObjectField(const char *fieldName, const char *signature) const; - template <typename T> + template <typename T +#ifndef Q_QDOC + , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true +#endif + > static QJniObject getStaticObjectField(const char *className, const char *fieldName) { - QtJniTypes::assertObjectType<T>(); constexpr auto signature = QtJniTypes::fieldSignature<T>(); return getStaticObjectField(className, fieldName, signature); } @@ -280,10 +408,13 @@ public: const char *fieldName, const char *signature); - template <typename T> + template <typename T +#ifndef Q_QDOC + , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true +#endif + > static QJniObject getStaticObjectField(jclass clazz, const char *fieldName) { - QtJniTypes::assertObjectType<T>(); constexpr auto signature = QtJniTypes::fieldSignature<T>(); return getStaticObjectField(clazz, fieldName, signature); } @@ -291,93 +422,114 @@ public: static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *signature); - template <typename T> void setField(const char *fieldName, T value) + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + void setField(const char *fieldName, T value) { - QtJniTypes::assertType<T>(); - QJniEnvironment env; constexpr auto signature = QtJniTypes::fieldSignature<T>(); - jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); + jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature); if (id) { - setFieldForType<T>(env.jniEnv(), object(), id, value); - env.checkAndClearExceptions(); + setFieldForType<T>(jniEnv(), object(), id, value); + QJniEnvironment::checkAndClearExceptions(jniEnv()); } } - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > void setField(const char *fieldName, const char *signature, T value) { - QtJniTypes::assertType<T>(); - QJniEnvironment env; - jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); + jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature); if (id) { - setFieldForType<T>(env.jniEnv(), object(), id, value); - env.checkAndClearExceptions(); + setFieldForType<T>(jniEnv(), object(), id, value); + QJniEnvironment::checkAndClearExceptions(jniEnv()); } } - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > static void setStaticField(const char *className, const char *fieldName, T value) { - QtJniTypes::assertType<T>(); - QJniEnvironment env; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); + LocalFrame<T> frame; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); if (!clazz) return; constexpr auto signature = QtJniTypes::fieldSignature<T>(); - jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, + jfieldID id = getCachedFieldID(frame.jniEnv(), clazz, className, fieldName, signature, true); if (!id) return; - setStaticFieldForType<T>(env.jniEnv(), clazz, id, value); - env.checkAndClearExceptions(); + setStaticFieldForType<T>(frame.jniEnv(), clazz, id, value); + frame.checkAndClearExceptions(); } - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > static void setStaticField(const char *className, const char *fieldName, const char *signature, T value) { - QtJniTypes::assertType<T>(); - QJniEnvironment env; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); + JNIEnv *env = QJniEnvironment::getJniEnv(); + jclass clazz = QJniObject::loadClass(className, env); if (!clazz) return; - jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, + jfieldID id = getCachedFieldID(env, clazz, className, fieldName, signature, true); if (id) { - setStaticFieldForType<T>(env.jniEnv(), clazz, id, value); - env.checkAndClearExceptions(); + setStaticFieldForType<T>(env, clazz, id, value); + QJniEnvironment::checkAndClearExceptions(env); } } - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > static void setStaticField(jclass clazz, const char *fieldName, const char *signature, T value) { - QtJniTypes::assertType<T>(); - QJniEnvironment env; - jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); + JNIEnv *env = QJniEnvironment::getJniEnv(); + jfieldID id = getFieldID(env, clazz, fieldName, signature, true); if (id) { - setStaticFieldForType<T>(env.jniEnv(), clazz, id, value); - env.checkAndClearExceptions(); + setStaticFieldForType<T>(env, clazz, id, value); + QJniEnvironment::checkAndClearExceptions(env); } } - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > static void setStaticField(jclass clazz, const char *fieldName, T value) { - QtJniTypes::assertType<T>(); - QJniEnvironment env; - constexpr auto signature = QtJniTypes::fieldSignature<T>(); - jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); - if (id) { - setStaticFieldForType<T>(env.jniEnv(), clazz, id, value); - env.checkAndClearExceptions(); - } + setStaticField(clazz, fieldName, QtJniTypes::fieldSignature<T>(), value); + } + + template <typename Klass, typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + static void setStaticField(const char *fieldName, T value) + { + setStaticField(QtJniTypes::Traits<Klass>::className(), fieldName, value); } static QJniObject fromString(const QString &string); @@ -389,21 +541,27 @@ public: // This function takes ownership of the jobject and releases the local ref. before returning. static QJniObject fromLocalRef(jobject lref); - template <typename T> QJniObject &operator=(T obj) + template <typename T, + std::enable_if_t<std::is_convertible_v<T, jobject>, bool> = true> + QJniObject &operator=(T obj) { - QtJniTypes::assertType<T>(); assign(static_cast<T>(obj)); return *this; } +protected: + QJniObject(Qt::Initialization) {} + JNIEnv *jniEnv() const noexcept; + 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); - static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false); +#if QT_CORE_REMOVED_SINCE(6, 7) + // these need to stay in the ABI as they were used in inline methods before 6.7 + static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded); static QByteArray toBinaryEncClassName(const QByteArray &className); - static QJniObject getCleanJniObject(jobject obj); + void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const; +#endif static jfieldID getCachedFieldID(JNIEnv *env, jclass clazz, const QByteArray &className, const char *name, const char *signature, @@ -422,14 +580,6 @@ private: 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; @@ -440,30 +590,11 @@ private: friend bool operator!=(const QJniObject&, const QJniObject&); template<typename T> - static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, - jmethodID id, ...) + static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, ...) { va_list args = {}; va_start(args, id); - - if constexpr(std::is_same<T, jboolean>::value) - res = env->CallBooleanMethodV(obj, id, args); - else if constexpr(std::is_same<T, jbyte>::value) - res = env->CallByteMethodV(obj, id, args); - else if constexpr(std::is_same<T, jchar>::value) - res = env->CallCharMethodV(obj, id, args); - else if constexpr(std::is_same<T, jshort>::value) - res = env->CallShortMethodV(obj, id, args); - else if constexpr(std::is_same<T, jint>::value) - res = env->CallIntMethodV(obj, id, args); - else if constexpr(std::is_same<T, jlong>::value) - res = env->CallLongMethodV(obj, id, args); - else if constexpr(std::is_same<T, jfloat>::value) - res = env->CallFloatMethodV(obj, id, args); - else if constexpr(std::is_same<T, jdouble>::value) - res = env->CallDoubleMethodV(obj, id, args); - else - QtJniTypes::staticAssertTypeMismatch(); + QtJniTypes::Caller<T>::callMethodForType(env, res, obj, id, args); va_end(args); } @@ -471,31 +602,18 @@ private: static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, jmethodID id, ...) { + if (!clazz || !id) + return; va_list args = {}; va_start(args, id); - if constexpr(std::is_same<T, jboolean>::value) - res = env->CallStaticBooleanMethodV(clazz, id, args); - else if constexpr(std::is_same<T, jbyte>::value) - res = env->CallStaticByteMethodV(clazz, id, args); - else if constexpr(std::is_same<T, jchar>::value) - res = env->CallStaticCharMethodV(clazz, id, args); - else if constexpr(std::is_same<T, jshort>::value) - res = env->CallStaticShortMethodV(clazz, id, args); - else if constexpr(std::is_same<T, jint>::value) - res = env->CallStaticIntMethodV(clazz, id, args); - else if constexpr(std::is_same<T, jlong>::value) - res = env->CallStaticLongMethodV(clazz, id, args); - else if constexpr(std::is_same<T, jfloat>::value) - res = env->CallStaticFloatMethodV(clazz, id, args); - else if constexpr(std::is_same<T, jdouble>::value) - res = env->CallStaticDoubleMethodV(clazz, id, args); - else - QtJniTypes::staticAssertTypeMismatch(); + QtJniTypes::Caller<T>::callStaticMethodForType(env, res, clazz, id, args); va_end(args); } static void callStaticMethodForVoid(JNIEnv *env, jclass clazz, jmethodID id, ...) { + if (!clazz || !id) + return; va_list args; va_start(args, id); env->CallStaticVoidMethodV(clazz, id, args); @@ -504,103 +622,37 @@ private: template<typename T> - static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, - jfieldID id) - { - if constexpr(std::is_same<T, jboolean>::value) - res = env->GetBooleanField(obj, id); - else if constexpr(std::is_same<T, jbyte>::value) - res = env->GetByteField(obj, id); - else if constexpr(std::is_same<T, jchar>::value) - res = env->GetCharField(obj, id); - else if constexpr(std::is_same<T, jshort>::value) - res = env->GetShortField(obj, id); - else if constexpr(std::is_same<T, jint>::value) - res = env->GetIntField(obj, id); - else if constexpr(std::is_same<T, jlong>::value) - res = env->GetLongField(obj, id); - else if constexpr(std::is_same<T, jfloat>::value) - res = env->GetFloatField(obj, id); - else if constexpr(std::is_same<T, jdouble>::value) - res = env->GetDoubleField(obj, id); - else - QtJniTypes::staticAssertTypeMismatch(); + static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id) + { + QtJniTypes::Caller<T>::getFieldForType(env, res, obj, id); } template<typename T> - static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, - jfieldID id) - { - if constexpr(std::is_same<T, jboolean>::value) - res = env->GetStaticBooleanField(clazz, id); - else if constexpr(std::is_same<T, jbyte>::value) - res = env->GetStaticByteField(clazz, id); - else if constexpr(std::is_same<T, jchar>::value) - res = env->GetStaticCharField(clazz, id); - else if constexpr(std::is_same<T, jshort>::value) - res = env->GetStaticShortField(clazz, id); - else if constexpr(std::is_same<T, jint>::value) - res = env->GetStaticIntField(clazz, id); - else if constexpr(std::is_same<T, jlong>::value) - res = env->GetStaticLongField(clazz, id); - else if constexpr(std::is_same<T, jfloat>::value) - res = env->GetStaticFloatField(clazz, id); - else if constexpr(std::is_same<T, jdouble>::value) - res = env->GetStaticDoubleField(clazz, id); - else - QtJniTypes::staticAssertTypeMismatch(); + static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id) + { + QtJniTypes::Caller<T>::getStaticFieldForType(env, res, clazz, id); } template<typename T> - static constexpr void setFieldForType(JNIEnv *env, jobject obj, - jfieldID id, T value) - { - if constexpr(std::is_same<T, jboolean>::value) - env->SetBooleanField(obj, id, value); - else if constexpr(std::is_same<T, jbyte>::value) - env->SetByteField(obj, id, value); - else if constexpr(std::is_same<T, jchar>::value) - env->SetCharField(obj, id, value); - else if constexpr(std::is_same<T, jshort>::value) - env->SetShortField(obj, id, value); - else if constexpr(std::is_same<T, jint>::value) - env->SetIntField(obj, id, value); - else if constexpr(std::is_same<T, jlong>::value) - env->SetLongField(obj, id, value); - else if constexpr(std::is_same<T, jfloat>::value) - env->SetFloatField(obj, id, value); - else if constexpr(std::is_same<T, jdouble>::value) - env->SetDoubleField(obj, id, value); - else if constexpr(std::is_convertible<T, jobject>::value) - env->SetObjectField(obj, id, value); - else - QtJniTypes::staticAssertTypeMismatch(); + static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value) + { + if constexpr (QtJniTypes::isObjectType<T>()) { + LocalFrame<T> frame(env); + env->SetObjectField(obj, id, static_cast<jobject>(frame.convertToJni(value))); + } else { + QtJniTypes::Caller<T>::setFieldForType(env, obj, id, value); + } } template<typename T> - static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, - jfieldID id, T value) - { - if constexpr(std::is_same<T, jboolean>::value) - env->SetStaticBooleanField(clazz, id, value); - else if constexpr(std::is_same<T, jbyte>::value) - env->SetStaticByteField(clazz, id, value); - else if constexpr(std::is_same<T, jchar>::value) - env->SetStaticCharField(clazz, id, value); - else if constexpr(std::is_same<T, jshort>::value) - env->SetStaticShortField(clazz, id, value); - else if constexpr(std::is_same<T, jint>::value) - env->SetStaticIntField(clazz, id, value); - else if constexpr(std::is_same<T, jlong>::value) - env->SetStaticLongField(clazz, id, value); - else if constexpr(std::is_same<T, jfloat>::value) - env->SetStaticFloatField(clazz, id, value); - else if constexpr(std::is_same<T, jdouble>::value) - env->SetStaticDoubleField(clazz, id, value); - else if constexpr(std::is_convertible<T, jobject>::value) - env->SetStaticObjectField(clazz, id, value); - else - QtJniTypes::staticAssertTypeMismatch(); + static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value) + { + if constexpr (QtJniTypes::isObjectType<T>()) { + LocalFrame<T> frame(env); + env->SetStaticObjectField(clazz, id, static_cast<jobject>(frame.convertToJni(value))); + } else { + QtJniTypes::Caller<T>::setStaticFieldForType(env, clazz, id, value); + } } friend QJniObjectPrivate; @@ -617,6 +669,201 @@ inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2) return !obj1.isSameObject(obj2); } +namespace QtJniTypes { +struct JObjectBase +{ + operator QJniObject() const { return m_object; } + + bool isValid() const { return m_object.isValid(); } + jclass objectClass() const { return m_object.objectClass(); } + QString toString() const { return m_object.toString(); } + + template <typename T = jobject> + T object() const { + return m_object.object<T>(); + } + +protected: + JObjectBase() = default; + ~JObjectBase() = default; + + Q_IMPLICIT JObjectBase(jobject object) : m_object(object) {} + Q_IMPLICIT JObjectBase(const QJniObject &object) : m_object(object) {} + Q_IMPLICIT JObjectBase(QJniObject &&object) noexcept : m_object(std::move(object)) {} + + QJniObject m_object; +}; + +template<typename Type> +class JObject : public JObjectBase +{ +public: + using Class = Type; + + JObject() + : JObjectBase{QJniObject(QtJniTypes::Traits<Class>::className())} + {} + Q_IMPLICIT JObject(jobject object) : JObjectBase(object) {} + Q_IMPLICIT JObject(const QJniObject &object) : JObjectBase(object) {} + Q_IMPLICIT JObject(QJniObject &&object) noexcept : JObjectBase(std::move(object)) {} + + // base class destructor is protected, so need to provide all SMFs + JObject(const JObject &other) = default; + JObject(JObject &&other) noexcept = default; + JObject &operator=(const JObject &other) = default; + JObject &operator=(JObject &&other) noexcept = default; + + ~JObject() = default; + + template<typename Arg, typename ...Args + , std::enable_if_t<!std::is_same_v<Arg, JObject>, bool> = true + , IfValidSignatureTypes<Arg, Args...> = true + > + explicit JObject(Arg && arg, Args &&...args) + : JObjectBase{QJniObject(QtJniTypes::Traits<Class>::className(), + std::forward<Arg>(arg), std::forward<Args>(args)...)} + {} + + // named constructors avoid ambiguities + static Type fromJObject(jobject object) { return Type(object); } + template <typename ...Args> + static Type construct(Args &&...args) { return Type(std::forward<Args>(args)...); } + static Type fromLocalRef(jobject lref) { return Type(QJniObject::fromLocalRef(lref)); } + + static bool registerNativeMethods(std::initializer_list<JNINativeMethod> methods) + { + QJniEnvironment env; + return env.registerNativeMethods<Class>(methods); + } + + // public API forwarding to QJniObject, with the implicit Class template parameter + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > + static auto callStaticMethod(const char *name, Args &&...args) + { + return QJniObject::callStaticMethod<Class, Ret, Args...>(name, + std::forward<Args>(args)...); + } + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + static auto getStaticField(const char *field) + { + return QJniObject::getStaticField<Class, T>(field); + } + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + static void setStaticField(const char *field, T &&value) + { + QJniObject::setStaticField<Class, T>(field, std::forward<T>(value)); + } + + // keep only these overloads, the rest is made private + template <typename Ret, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true +#endif + > + auto callMethod(const char *method, Args &&...args) const + { + return m_object.callMethod<Ret>(method, std::forward<Args>(args)...); + } + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + auto getField(const char *fieldName) const + { + return m_object.getField<T>(fieldName); + } + + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + void setField(const char *fieldName, T &&value) + { + m_object.setField(fieldName, std::forward<T>(value)); + } + + QByteArray className() const { + return QtJniTypes::Traits<Class>::className().data(); + } + +private: + friend bool comparesEqual(const JObject &lhs, const JObject &rhs) noexcept + { return lhs.m_object == rhs.m_object; } + Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(JObject); +}; +} + +// This cannot be included earlier as QJniArray is a QJniObject subclass, but it +// must be included so that we can implement QJniObject::LocalFrame conversion. +QT_END_NAMESPACE +#include <QtCore/qjniarray.h> +QT_BEGIN_NAMESPACE + +template <typename ...Args> +template <typename T> +auto QJniObject::LocalFrame<Args...>::convertToJni(T &&value) +{ + using Type = q20::remove_cvref_t<T>; + if constexpr (std::is_same_v<Type, QString>) { + return newLocalRef<jstring>(QJniObject::fromString(value)); + } else if constexpr (QtJniTypes::IsJniArray<Type>::value) { + return value.arrayObject(); + } else if constexpr (QJniArrayBase::canConvert<T>) { + using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::forward<T>(value))); + using ArrayType = decltype(std::declval<QJniArrayType>().arrayObject()); + return newLocalRef<ArrayType>(QJniArrayBase::fromContainer(std::forward<T>(value)).template object<jobject>()); + } else if constexpr (std::is_base_of_v<QJniObject, Type> + || std::is_base_of_v<QtJniTypes::JObjectBase, Type>) { + return value.object(); + } else { + return std::forward<T>(value); + } +} + +template <typename ...Args> +template <typename T> +auto QJniObject::LocalFrame<Args...>::convertFromJni(QJniObject &&object) +{ + using Type = q20::remove_cvref_t<T>; + if constexpr (std::is_same_v<Type, QString>) { + return object.toString(); + } else if constexpr (QtJniTypes::IsJniArray<Type>::value) { + return T(std::move(object)); + } else if constexpr (QJniArrayBase::canConvert<Type>) { + // if we were to create a QJniArray from Type... + using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval<Type>())); + // then that QJniArray would have elements of type + using ElementType = typename QJniArrayType::Type; + // construct a QJniArray from a jobject pointer of that type + return QJniArray<ElementType>(object.template object<jarray>()).toContainer(); + } else if constexpr (std::is_array_v<Type>) { + using ElementType = std::remove_extent_t<Type>; + return QJniArray<ElementType>(std::move(object)); + } else if constexpr (std::is_base_of_v<QJniObject, Type> + && !std::is_same_v<QJniObject, Type>) { + return T{std::move(object)}; + } else if constexpr (std::is_base_of_v<QtJniTypes::JObjectBase, Type>) { + return T{std::move(object)}; + } else { + return std::move(object); + } +} + + QT_END_NAMESPACE #endif |