diff options
Diffstat (limited to 'src/corelib/kernel/qjniobject.h')
-rw-r--r-- | src/corelib/kernel/qjniobject.h | 924 |
1 files changed, 791 insertions, 133 deletions
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 0f88695e73..589f6489f7 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -1,53 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <QtCore/QSharedPointer> +#include <QtCore/qsharedpointer.h> -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#if defined(Q_QDOC) || defined(Q_OS_ANDROID) #include <jni.h> -#else -class jclass; -class jobject; -#endif +#include <QtCore/qjnienvironment.h> QT_BEGIN_NAMESPACE @@ -55,94 +16,521 @@ 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); explicit QJniObject(const char *className, const char *signature, ...); + template<typename ...Args +#ifndef Q_QDOC + , std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr +#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(), + localFrame.convertToJni(std::forward<Args>(args))...) + { + } +public: explicit QJniObject(jclass clazz); explicit QJniObject(jclass clazz, const char *signature, ...); + template<typename ...Args +#ifndef Q_QDOC + , std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr +#endif + > + explicit QJniObject(jclass clazz, Args &&...args) + : QJniObject(clazz, QtJniTypes::constructorSignature<Args...>().data(), + std::forward<Args>(args)...) + {} QJniObject(jobject globalRef); + + 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 T> - T object() const; + template<typename Class, typename ...Args> + static inline QJniObject construct(Args &&...args) + { + LocalFrame<Args...> frame; + return QJniObject(QtJniTypes::Traits<Class>::className().data(), + QtJniTypes::constructorSignature<Args...>().data(), + frame.convertToJni(std::forward<Args>(args))...); + } + jobject object() const; + template <typename T> T object() const + { + QtJniTypes::assertObjectType<T>(); + return static_cast<T>(javaObject()); + } + + jclass objectClass() const; + QByteArray className() const; + + 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 frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature, + frame.convertToJni(std::forward<Args>(args))...)); + } else { + jmethodID id = getCachedMethodID(frame.jniEnv(), methodName, signature); + if (id) { + 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>(frame.jniEnv(), res, object(), id, + frame.convertToJni(std::forward<Args>(args))...); + if (frame.checkAndClearExceptions()) + res = {}; + return res; + } + } + if constexpr (!std::is_same_v<Ret, void>) + return Ret{}; + } + } + + 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...>(); + return callMethod<Ret>(methodName, signature.data(), std::forward<Args>(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...>(); + LocalFrame<Args...> frame(jniEnv()); + return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature, + frame.convertToJni(std::forward<Args>(args))...)); + } - template <typename T> - T callMethod(const char *methodName, const char *signature, ...) const; - template <typename T> - T callMethod(const char *methodName) const; - template <typename T> - QJniObject callObjectMethod(const char *methodName) const; QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const; - template <typename T> - static T callStaticMethod(const char *className, const char *methodName, - const char *signature, ...); - template <typename T> - static T callStaticMethod(const char *className, const char *methodName); - template <typename T> - static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...); - template <typename T> - static T callStaticMethod(jclass clazz, const char *methodName); - - template <typename T> - static QJniObject callStaticObjectMethod(const char *className, const char *methodName); - static QJniObject callStaticObjectMethod(const char *className, - const char *methodName, + template <typename Ret, typename ...Args> + static auto callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args) + { + 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) + { + 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 +#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 frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodId, + frame.convertToJni(std::forward<Args>(args))...)); + } else { + if (clazz && methodId) { + 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>(frame.jniEnv(), res, clazz, methodId, + frame.convertToJni(std::forward<Args>(args))...); + if (frame.checkAndClearExceptions()) + res = {}; + return res; + } + } + if constexpr (!std::is_same_v<Ret, void>) + return Ret{}; + } + } + + 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) + { + 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 +#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, ...); - template <typename T> - static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName); - static QJniObject callStaticObjectMethod(jclass clazz, - const char *methodName, + static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...); - template <typename T> - T getField(const char *fieldName) const; + static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...); - template <typename T> - static T getStaticField(const char *className, const char *fieldName); - template <typename T> - static T getStaticField(jclass clazz, const char *fieldName); - template <typename T> - QJniObject getObjectField(const char *fieldName) const; + 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...>(); + LocalFrame<Args...> frame; + return frame.template convertFromJni<Ret>(callStaticObjectMethod(className, methodName, signature.data(), + frame.convertToJni(std::forward<Args>(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...>(); + LocalFrame<Args...> frame; + return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodName, signature.data(), + frame.convertToJni(std::forward<Args>(args))...)); + } + + 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 frame.template convertFromJni<T>(getObjectField<T>(fieldName)); + } else { + T res{}; + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature); + if (id) { + getFieldForType<T>(frame.jniEnv(), res, object(), id); + if (frame.checkAndClearExceptions()) + res = {}; + } + return res; + } + } + + 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 frame.template convertFromJni<T>(getStaticObjectField<T>(className, fieldName)); + } else { + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); + if (!clazz) + return T{}; + return getStaticField<T>(clazz, fieldName); + } + } + + 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 frame.template convertFromJni<T>(getStaticObjectField<T>(clazz, fieldName)); + } else { + T res{}; + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + jfieldID id = getFieldID(frame.jniEnv(), clazz, fieldName, signature, true); + if (id) { + getStaticFieldForType<T>(frame.jniEnv(), res, clazz, id); + if (frame.checkAndClearExceptions()) + res = {}; + } + return res; + } + } + + 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 + { + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + return getObjectField(fieldName, signature); + } + QJniObject getObjectField(const char *fieldName, const char *signature) const; - template <typename T> - static QJniObject getStaticObjectField(const char *className, const char *fieldName); - static QJniObject getStaticObjectField(const char *className, - 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(const char *className, const char *fieldName) + { + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + return getStaticObjectField(className, fieldName, signature); + } + static QJniObject getStaticObjectField(const char *className, const char *fieldName, const char *signature); - template <typename T> - static QJniObject getStaticObjectField(jclass clazz, const char *fieldName); - static QJniObject getStaticObjectField(jclass clazz, 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) + { + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + return getStaticObjectField(clazz, fieldName, signature); + } + static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *signature); - template <typename T> - void setField(const char *fieldName, T value); - template <typename T> - void setField(const char *fieldName, const char *signature, T value); - template <typename T> - static void setStaticField(const char *className, const char *fieldName, T value); - template <typename T> + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + void setField(const char *fieldName, T value) + { + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature); + if (id) { + setFieldForType<T>(jniEnv(), object(), id, value); + QJniEnvironment::checkAndClearExceptions(jniEnv()); + } + } + + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + void setField(const char *fieldName, const char *signature, T value) + { + jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature); + if (id) { + setFieldForType<T>(jniEnv(), object(), id, value); + QJniEnvironment::checkAndClearExceptions(jniEnv()); + } + } + + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + static void setStaticField(const char *className, const char *fieldName, T value) + { + LocalFrame<T> frame; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); + if (!clazz) + return; + + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + jfieldID id = getCachedFieldID(frame.jniEnv(), clazz, className, fieldName, + signature, true); + if (!id) + return; + + setStaticFieldForType<T>(frame.jniEnv(), clazz, id, value); + frame.checkAndClearExceptions(); + } + + 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); - template <typename T> + const char *signature, T value) + { + JNIEnv *env = QJniEnvironment::getJniEnv(); + jclass clazz = QJniObject::loadClass(className, env); + + if (!clazz) + return; + + jfieldID id = getCachedFieldID(env, clazz, className, fieldName, + signature, true); + if (id) { + setStaticFieldForType<T>(env, clazz, id, value); + QJniEnvironment::checkAndClearExceptions(env); + } + } + + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > static void setStaticField(jclass clazz, const char *fieldName, - const char *signature, T value); - - template <typename T> - static void setStaticField(jclass clazz, const char *fieldName, T value); + const char *signature, T value) + { + JNIEnv *env = QJniEnvironment::getJniEnv(); + jfieldID id = getFieldID(env, clazz, fieldName, signature, true); + + if (id) { + setStaticFieldForType<T>(env, clazz, id, value); + QJniEnvironment::checkAndClearExceptions(env); + } + } + + template <typename T +#ifndef Q_QDOC + , QtJniTypes::IfValidFieldType<T> = true +#endif + > + static void setStaticField(jclass clazz, const char *fieldName, T value) + { + 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); QString toString() const; @@ -153,39 +541,45 @@ 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) + { + 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); - - template <typename T> - T callMethodV(const char *methodName, const char *signature, va_list args) const; - QJniObject callObjectMethodV(const char *methodName, - const char *signature, - va_list args) const; - template <typename T> - static T callStaticMethodV(const char *className, - const char *methodName, - const char *signature, - va_list args); - template <typename T> - static T callStaticMethodV(jclass clazz, - const char *methodName, - const char *signature, - va_list args); - - 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); + static jclass loadClass(const QByteArray &className, JNIEnv *env); + +#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); + 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, + 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; bool isSameObject(jobject obj) const; bool isSameObject(const QJniObject &other) const; @@ -195,6 +589,73 @@ private: friend bool operator==(const QJniObject &, const QJniObject &); friend bool operator!=(const QJniObject&, const QJniObject&); + template<typename T> + static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, ...) + { + va_list args = {}; + va_start(args, id); + QtJniTypes::Caller<T>::callMethodForType(env, res, obj, id, args); + va_end(args); + } + + template<typename T> + static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, + jmethodID id, ...) + { + if (!clazz || !id) + return; + va_list args = {}; + va_start(args, id); + 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); + va_end(args); + } + + + template<typename T> + 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) + { + 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 (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 (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; QSharedPointer<QJniObjectPrivate> d; }; @@ -208,6 +669,203 @@ 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 + #endif // QJNIOBJECT_H |