From a085a14d76553ebd1fa4a4a11a27110ee544a531 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 20 Apr 2022 17:07:42 +0200 Subject: [PATCH] Generate JNI signature strings at compile time Introduce an internal QtJniTypes namespace with types that allow us to concatenate string literals at compile time. This makes it possible to generate arbitrary strings based on types, which we can then use as signatures to JNI method calls. Move some of the private members of QJniObject into the QtJniTypes namespace for consistency, and to allow further template specialization by user code to make other types and their JNI signature string known. Remove the "Jni" prefix from names. Use the compile-time generated string in QJniObject methods that created the signature string at runtime, which involved a temporary memory allocation. Treat 'void' as a primitive type (with signature string 'V'), and remove redundant template specializations. Add a test case to verify the the strings are constructed correctly at compile time. Change-Id: I5e3895a97f7dc1b86961f7a7855b899d9203037d Reviewed-by: Assam Boudjelthia Reviewed-by: Marc Mutz Reviewed-by: Fabian Kosmale --- src/corelib/CMakeLists.txt | 1 + src/corelib/kernel/qjniobject.h | 217 +++--------- src/corelib/kernel/qjnitypes.h | 318 ++++++++++++++++++ tests/auto/corelib/kernel/CMakeLists.txt | 1 + .../corelib/kernel/qjnitypes/CMakeLists.txt | 4 + .../kernel/qjnitypes/tst_qjnitypes.cpp | 118 +++++++ 6 files changed, 495 insertions(+), 164 deletions(-) create mode 100644 src/corelib/kernel/qjnitypes.h create mode 100644 tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt create mode 100644 tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 3c5301fee2..176711ef90 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -949,6 +949,7 @@ qt_internal_extend_target(Core CONDITION ANDROID SOURCES io/qstandardpaths_android.cpp io/qstorageinfo_unix.cpp + kernel/qjnitypes.h kernel/qjnienvironment.cpp kernel/qjnienvironment.h kernel/qjniobject.cpp kernel/qjniobject.h kernel/qjnihelpers.cpp kernel/qjnihelpers_p.h diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 80568b2970..829823efec 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -45,6 +45,7 @@ #if defined(Q_QDOC) || defined(Q_OS_ANDROID) #include #include +#include QT_BEGIN_NAMESPACE @@ -64,7 +65,7 @@ public: jobject object() const; template T object() const { - assertJniObjectType(); + QtJniTypes::assertObjectType(); return static_cast(javaObject()); } @@ -74,7 +75,7 @@ public: template T callMethod(const char *methodName, const char *signature, ...) const { - assertJniPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature); @@ -106,23 +107,17 @@ public: template T callMethod(const char *methodName) const { - assertJniPrimitiveType(); - constexpr const char *signature = getTypeSignature(); - return callMethod(methodName, QByteArray(signature).prepend("()").constData()); - } - - template <> - void callMethod(const char *methodName) const - { - callMethod(methodName, "()V"); + QtJniTypes::assertPrimitiveType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callMethod(methodName, signature); } template QJniObject callObjectMethod(const char *methodName) const { - assertJniObjectType(); - constexpr const char *signature = getTypeSignature(); - return callObjectMethod(methodName, QByteArray(signature).prepend("()").constData()); + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callObjectMethod(methodName, signature); } QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const; @@ -131,7 +126,7 @@ public: static T callStaticMethod(const char *className, const char *methodName, const char *signature, ...) { - assertJniPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); @@ -174,21 +169,15 @@ public: template static T callStaticMethod(const char *className, const char *methodName) { - assertJniPrimitiveType(); - constexpr const char *signature = getTypeSignature(); - return callStaticMethod(className, methodName, QByteArray(signature).prepend("()").constData()); - } - - template <> - void callStaticMethod(const char *className, const char *methodName) - { - callStaticMethod(className, methodName, "()V"); + QtJniTypes::assertPrimitiveType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callStaticMethod(className, methodName, signature); } template static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...) { - assertJniPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; if (clazz) { @@ -225,7 +214,7 @@ public: template static T callStaticMethod(jclass clazz, jmethodID methodId, ...) { - assertJniPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; if (clazz && methodId) { @@ -254,23 +243,17 @@ public: template static T callStaticMethod(jclass clazz, const char *methodName) { - assertJniPrimitiveType(); - constexpr const char *signature = getTypeSignature(); - return callStaticMethod(clazz, methodName, QByteArray(signature).prepend("()").constData()); - } - - template <> - void callStaticMethod(jclass clazz, const char *methodName) - { - callStaticMethod(clazz, methodName, "()V"); + QtJniTypes::assertPrimitiveType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callStaticMethod(clazz, methodName, signature); } template static QJniObject callStaticObjectMethod(const char *className, const char *methodName) { - assertJniObjectType(); - constexpr const char *signature = getTypeSignature(); - return callStaticObjectMethod(className, methodName, QByteArray(signature).prepend("()").constData()); + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callStaticObjectMethod(className, methodName, signature); } static QJniObject callStaticObjectMethod(const char *className, const char *methodName, @@ -279,9 +262,9 @@ public: template static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName) { - assertJniObjectType(); - constexpr const char *signature = getTypeSignature(); - return callStaticObjectMethod(clazz, methodName, QByteArray(signature).prepend("()").constData()); + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callStaticObjectMethod(clazz, methodName, signature); } static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, @@ -291,10 +274,10 @@ public: template T getField(const char *fieldName) const { - assertJniPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; - constexpr const char *signature = getTypeSignature(); + constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); if (id) { getFieldForType(env.jniEnv(), res, object(), id); @@ -307,13 +290,13 @@ public: template static T getStaticField(const char *className, const char *fieldName) { - assertJniPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); if (!clazz) return 0; - constexpr const char *signature = getTypeSignature(); + constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), clazz, QJniObject::toBinaryEncClassName(className), fieldName, @@ -331,10 +314,10 @@ public: template static T getStaticField(jclass clazz, const char *fieldName) { - assertJniPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; T res{}; - constexpr const char *signature = getTypeSignature(); + constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); if (id) { getStaticFieldForType(env.jniEnv(), res, clazz, id); @@ -347,8 +330,8 @@ public: template QJniObject getObjectField(const char *fieldName) const { - assertJniObjectType(); - constexpr const char *signature = getTypeSignature(); + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::fieldSignature(); return getObjectField(fieldName, signature); } @@ -357,8 +340,8 @@ public: template static QJniObject getStaticObjectField(const char *className, const char *fieldName) { - assertJniObjectType(); - constexpr const char *signature = getTypeSignature(); + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::fieldSignature(); return getStaticObjectField(className, fieldName, signature); } @@ -369,8 +352,8 @@ public: template static QJniObject getStaticObjectField(jclass clazz, const char *fieldName) { - assertJniObjectType(); - constexpr const char *signature = getTypeSignature(); + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::fieldSignature(); return getStaticObjectField(clazz, fieldName, signature); } @@ -379,9 +362,9 @@ public: template void setField(const char *fieldName, T value) { - assertJniType(); + QtJniTypes::assertType(); QJniEnvironment env; - constexpr const char *signature = getTypeSignature(); + constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); if (id) { setFieldForType(env.jniEnv(), object(), id, value); @@ -392,7 +375,7 @@ public: template void setField(const char *fieldName, const char *signature, T value) { - assertJniType(); + QtJniTypes::assertType(); QJniEnvironment env; jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); if (id) { @@ -404,13 +387,13 @@ public: template static void setStaticField(const char *className, const char *fieldName, T value) { - assertJniType(); + QtJniTypes::assertType(); QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); if (!clazz) return; - constexpr const char *signature = getTypeSignature(); + constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, signature, true); if (!id) @@ -424,7 +407,7 @@ public: static void setStaticField(const char *className, const char *fieldName, const char *signature, T value) { - assertJniType(); + QtJniTypes::assertType(); QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); @@ -443,7 +426,7 @@ public: static void setStaticField(jclass clazz, const char *fieldName, const char *signature, T value) { - assertJniType(); + QtJniTypes::assertType(); QJniEnvironment env; jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); @@ -456,9 +439,9 @@ public: template static void setStaticField(jclass clazz, const char *fieldName, T value) { - assertJniType(); + QtJniTypes::assertType(); QJniEnvironment env; - constexpr const char *signature = getTypeSignature(); + constexpr auto signature = QtJniTypes::fieldSignature(); jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); if (id) { setStaticFieldForType(env.jniEnv(), clazz, id, value); @@ -477,7 +460,7 @@ public: template QJniObject &operator=(T obj) { - assertJniType(); + QtJniTypes::assertType(); assign(static_cast(obj)); return *this; } @@ -525,100 +508,6 @@ private: friend bool operator==(const QJniObject &, const QJniObject &); friend bool operator!=(const QJniObject&, const QJniObject&); - template - static void staticAssertTypeMismatch() - { - static_assert(flag, "The used type is not supported by this template call. " - "Use a JNI based type instead."); - } - - template - static constexpr bool isJniPrimitiveType() - { - if constexpr(!std::is_same::value - && !std::is_same::value - && !std::is_same::value - && !std::is_same::value - && !std::is_same::value - && !std::is_same::value - && !std::is_same::value - && !std::is_same::value) { - return false; - } - - return true; - } - - template - static constexpr void assertJniPrimitiveType() - { - if constexpr(!isJniPrimitiveType()) - staticAssertTypeMismatch(); - } - - template - static constexpr void assertJniObjectType() - { - if constexpr(!std::is_convertible::value) - staticAssertTypeMismatch(); - } - - template - static constexpr void assertJniType() - { - if constexpr(!isJniPrimitiveType() && !std::is_convertible::value) - staticAssertTypeMismatch(); - } - - template - static constexpr const char* getTypeSignature() - { - if constexpr(std::is_same::value) - return "Ljava/lang/Object;"; - else if constexpr(std::is_same::value) - return "Ljava/lang/Class;"; - else if constexpr(std::is_same::value) - return "Ljava/lang/String;"; - else if constexpr(std::is_same::value) - return "[Ljava/lang/Object;"; - else if constexpr(std::is_same::value) - return "Ljava/lang/Throwable;"; - else if constexpr(std::is_same::value) - return "[Z"; - else if constexpr(std::is_same::value) - return "[B"; - else if constexpr(std::is_same::value) - return "[S"; - else if constexpr(std::is_same::value) - return "[I"; - else if constexpr(std::is_same::value) - return "[J"; - else if constexpr(std::is_same::value) - return "[F"; - else if constexpr(std::is_same::value) - return "[D"; - else if constexpr(std::is_same::value) - return "[C"; - else if constexpr(std::is_same::value) - return "Z"; - else if constexpr(std::is_same::value) - return "B"; - else if constexpr(std::is_same::value) - return "C"; - else if constexpr(std::is_same::value) - return "S"; - else if constexpr(std::is_same::value) - return "I"; - else if constexpr(std::is_same::value) - return "J"; - else if constexpr(std::is_same::value) - return "F"; - else if constexpr(std::is_same::value) - return "D"; - else - staticAssertTypeMismatch(); - } - template static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, va_list args) @@ -640,7 +529,7 @@ private: else if constexpr(std::is_same::value) res = env->CallDoubleMethodV(obj, id, args); else - staticAssertTypeMismatch(); + QtJniTypes::staticAssertTypeMismatch(); } template @@ -664,7 +553,7 @@ private: else if constexpr(std::is_same::value) res = env->CallStaticDoubleMethodV(clazz, id, args); else - staticAssertTypeMismatch(); + QtJniTypes::staticAssertTypeMismatch(); } template @@ -688,7 +577,7 @@ private: else if constexpr(std::is_same::value) res = env->GetDoubleField(obj, id); else - staticAssertTypeMismatch(); + QtJniTypes::staticAssertTypeMismatch(); } template @@ -712,7 +601,7 @@ private: else if constexpr(std::is_same::value) res = env->GetStaticDoubleField(clazz, id); else - staticAssertTypeMismatch(); + QtJniTypes::staticAssertTypeMismatch(); } template @@ -738,7 +627,7 @@ private: else if constexpr(std::is_convertible::value) env->SetObjectField(obj, id, value); else - staticAssertTypeMismatch(); + QtJniTypes::staticAssertTypeMismatch(); } template @@ -764,7 +653,7 @@ private: else if constexpr(std::is_convertible::value) env->SetStaticObjectField(clazz, id, value); else - staticAssertTypeMismatch(); + QtJniTypes::staticAssertTypeMismatch(); } friend QJniObjectPrivate; diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h new file mode 100644 index 0000000000..0dde398ef2 --- /dev/null +++ b/src/corelib/kernel/qjnitypes.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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$ +** +****************************************************************************/ + +#ifndef QJNITYPES_H +#define QJNITYPES_H + +#include + +#if defined(Q_QDOC) || defined(Q_OS_ANDROID) +#include + +QT_BEGIN_NAMESPACE + +namespace QtJniTypes +{ + +// a constexpr type for string literals of any character width, aware of the length +// of the string. +template +struct String +{ + BaseType m_data[N_WITH_NULL] = {}; + + constexpr String() noexcept {} + // Can be instantiated (only) with a string literal + constexpr explicit String(const BaseType (&data)[N_WITH_NULL]) noexcept + { + for (size_t i = 0; i < N_WITH_NULL - 1; ++i) + m_data[i] = data[i]; + } + + constexpr BaseType at(size_t i) const { return m_data[i]; } + constexpr BaseType operator[](size_t i) const { return at(i); } + static constexpr size_t size() noexcept { return N_WITH_NULL; } + constexpr operator const BaseType *() const noexcept { return m_data; } + constexpr const BaseType *data() const noexcept { return m_data; } + template + constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept + { + if constexpr (N2_WITH_NULL > N_WITH_NULL) { + return false; + } else { + for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) { + if (m_data[i] != lit[i]) + return false; + } + } + return true; + } + constexpr bool startsWith(BaseType c) const noexcept + { + return N_WITH_NULL > 1 && m_data[0] == c; + } + template + constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept + { + if constexpr (N2_WITH_NULL > N_WITH_NULL) { + return false; + } else { + for (size_t i = 0; i < N2_WITH_NULL; ++i) { + if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1]) + return false; + } + } + return true; + } + constexpr bool endsWith(BaseType c) const noexcept + { + return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c; + } + + template + friend inline constexpr bool operator==(const String &lhs, + const String &rhs) noexcept + { + if constexpr (N_WITH_NULL != N2_WITH_NULL) { + return false; + } else { + for (size_t i = 0; i < N_WITH_NULL - 1; ++i) { + if (lhs.at(i) != rhs.at(i)) + return false; + } + } + return true; + } + + template + friend inline constexpr bool operator!=(const String &lhs, + const String &rhs) noexcept + { + return !operator==(lhs, rhs); + } + + template + friend inline constexpr bool operator==(const String &lhs, + const BaseType (&rhs)[N2_WITH_NULL]) noexcept + { + return operator==(lhs, String(rhs)); + } + template + friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL], + const String &rhs) noexcept + { + return operator==(String(lhs), rhs); + } + + template + friend inline constexpr bool operator!=(const String &lhs, + const BaseType (&rhs)[N2_WITH_NULL]) noexcept + { + return operator!=(lhs, String(rhs)); + } + template + friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL], + const String &rhs) noexcept + { + return operator!=(String(lhs), rhs); + } + + template + friend inline constexpr auto operator+(const String &lhs, + const String &rhs) noexcept + { + char data[N_WITH_NULL + N2_WITH_NULL - 1] = {}; + for (size_t i = 0; i < N_WITH_NULL - 1; ++i) + data[i] = lhs[i]; + for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) + data[N_WITH_NULL - 1 + i] = rhs[i]; + return String(data); + } +}; + + +// Helper types that allow us to disable variadic overloads that would conflict +// with overloads that take a const char*. +template struct IsStringType : std::false_type {}; +template<> struct IsStringType : std::true_type {}; +template struct IsStringType> : std::true_type {}; +template struct IsStringType : std::true_type {}; + +template +static void staticAssertTypeMismatch() +{ + static_assert(flag, "The used type is not supported by this template call. " + "Use a JNI based type instead."); +} + +template +constexpr auto typeSignature() +{ + if constexpr(std::is_same::value) + return String("Ljava/lang/Object;"); + else if constexpr(std::is_same::value) + return String("Ljava/lang/Class;"); + else if constexpr(std::is_same::value) + return String("Ljava/lang/String;"); + else if constexpr(std::is_same::value) + return String("[Ljava/lang/Object;"); + else if constexpr(std::is_same::value) + return String("Ljava/lang/Throwable;"); + else if constexpr(std::is_same::value) + return String("[Z"); + else if constexpr(std::is_same::value) + return String("[B"); + else if constexpr(std::is_same::value) + return String("[S"); + else if constexpr(std::is_same::value) + return String("[I"); + else if constexpr(std::is_same::value) + return String("[J"); + else if constexpr(std::is_same::value) + return String("[F"); + else if constexpr(std::is_same::value) + return String("[D"); + else if constexpr(std::is_same::value) + return String("[C"); + else if constexpr(std::is_same::value) + return String("Z"); + else if constexpr(std::is_same::value) + return String("Z"); + else if constexpr(std::is_same::value) + return String("B"); + else if constexpr(std::is_same::value) + return String("C"); + else if constexpr(std::is_same::value) + return String("C"); + else if constexpr(std::is_same::value) + return String("S"); + else if constexpr(std::is_same::value) + return String("S"); + else if constexpr(std::is_same::value) + return String("I"); + else if constexpr(std::is_same::value) + return String("I"); + else if constexpr(std::is_same::value) + return String("I"); + else if constexpr(std::is_same::value) + return String("J"); + else if constexpr(std::is_same::value) + return String("J"); + else if constexpr(std::is_same::value) + return String("F"); + else if constexpr(std::is_same::value) + return String("F"); + else if constexpr(std::is_same::value) + return String("D"); + else if constexpr(std::is_same::value) + return String("D"); + else if constexpr(std::is_same::value) + return String("V"); + else if constexpr(IsStringType::value) + static_assert(!IsStringType::value, "Don't use a literal type, call data!"); + else + staticAssertTypeMismatch(); +} + +template +static constexpr bool isPrimitiveType() +{ + return typeSignature().size() == 2; +} + +template +static constexpr bool isObjectType() +{ + if constexpr(std::is_convertible::value) { + return true; + } else { + constexpr auto signature = typeSignature(); + return signature.startsWith('L') && signature.endsWith(';'); + } +} + +template +static constexpr void assertPrimitiveType() +{ + static_assert(isPrimitiveType(), "Type needs to be a primitive JNI type!"); +} + +template +static constexpr void assertObjectType() +{ + static_assert(isObjectType(), + "Type needs to be a JNI object type (convertible to jobject, or with " + "an object type signature registered)!"); +} + +template +static constexpr void assertType() +{ + static_assert(isPrimitiveType() || isObjectType(), + "Type needs to be a JNI type!"); +} + +template +static constexpr auto methodSignature() +{ + return (String("(") + + ... + typeSignature>()) + + String(")") + + typeSignature(); +} + +template +static constexpr auto fieldSignature() +{ + return QtJniTypes::typeSignature(); +} + +template +static constexpr auto constructorSignature() +{ + return methodSignature(); +} + +} // namespace QtJniTypes + +QT_END_NAMESPACE + +#endif + +#endif // QJNITYPES_H diff --git a/tests/auto/corelib/kernel/CMakeLists.txt b/tests/auto/corelib/kernel/CMakeLists.txt index 18f20bfadc..3083652945 100644 --- a/tests/auto/corelib/kernel/CMakeLists.txt +++ b/tests/auto/corelib/kernel/CMakeLists.txt @@ -49,4 +49,5 @@ endif() if(ANDROID) add_subdirectory(qjnienvironment) add_subdirectory(qjniobject) + add_subdirectory(qjnitypes) endif() diff --git a/tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt b/tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt new file mode 100644 index 0000000000..8184e91a11 --- /dev/null +++ b/tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt @@ -0,0 +1,4 @@ +qt_internal_add_test(tst_qjnitypes + SOURCES + tst_qjnitypes.cpp +) diff --git a/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp new file mode 100644 index 0000000000..559fcd47c9 --- /dev/null +++ b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +class tst_QJniTypes : public QObject +{ + Q_OBJECT + +public: + tst_QJniTypes() = default; + +private slots: + void initTestCase(); +}; + +struct QtJavaWrapper {}; +template<> +constexpr auto QtJniTypes::typeSignature() +{ + return QtJniTypes::String("Lorg/qtproject/qt/android/QtJavaWrapper;"); +} + +template<> +constexpr auto QtJniTypes::typeSignature() +{ + return QtJniTypes::String("Ljava/lang/Object;"); +} + +struct QtCustomJniObject : QJniObject {}; +template<> +constexpr auto QtJniTypes::typeSignature() +{ + return QtJniTypes::String("Lorg/qtproject/qt/android/QtCustomJniObject;"); +} + +static_assert(QtJniTypes::typeSignature() == "Lorg/qtproject/qt/android/QtJavaWrapper;"); +static_assert(QtJniTypes::typeSignature() != "Ljava/lang/Object;"); +static_assert(!(QtJniTypes::typeSignature() == "X")); + +static_assert(QtJniTypes::fieldSignature() == "I"); +static_assert(QtJniTypes::fieldSignature() != "X"); +static_assert(QtJniTypes::fieldSignature() != "Ljava/lang/Object;"); +static_assert(QtJniTypes::fieldSignature() == "J"); +static_assert(QtJniTypes::fieldSignature() == "Ljava/lang/String;"); +static_assert(QtJniTypes::fieldSignature() == "Ljava/lang/Object;"); +static_assert(QtJniTypes::fieldSignature() == "[Ljava/lang/Object;"); +static_assert(QtJniTypes::fieldSignature() == "Ljava/lang/Object;"); +static_assert(QtJniTypes::fieldSignature() == "Lorg/qtproject/qt/android/QtJavaWrapper;"); +static_assert(QtJniTypes::fieldSignature() == "Lorg/qtproject/qt/android/QtCustomJniObject;"); + +static_assert(QtJniTypes::methodSignature() == "()V"); +static_assert(QtJniTypes::methodSignature() != "()X"); +static_assert(QtJniTypes::methodSignature() == "(I)V"); +static_assert(QtJniTypes::methodSignature() == "(ILjava/lang/String;)V"); +static_assert(QtJniTypes::methodSignature() == "(ILjava/lang/Class;)J"); +static_assert(QtJniTypes::methodSignature() == "(ILjava/lang/String;)Ljava/lang/Object;"); + +static_assert(QtJniTypes::isPrimitiveType()); +static_assert(QtJniTypes::isPrimitiveType()); +static_assert(!QtJniTypes::isPrimitiveType()); +static_assert(!QtJniTypes::isPrimitiveType()); + +static_assert(!QtJniTypes::isObjectType()); +static_assert(!QtJniTypes::isObjectType()); +static_assert(QtJniTypes::isObjectType()); +static_assert(QtJniTypes::isObjectType()); + +static_assert(QtJniTypes::String("ABCDE").startsWith("ABC")); +static_assert(QtJniTypes::String("ABCDE").startsWith("A")); +static_assert(QtJniTypes::String("ABCDE").startsWith("ABCDE")); +static_assert(!QtJniTypes::String("ABCDE").startsWith("ABCDEF")); +static_assert(!QtJniTypes::String("ABCDE").startsWith("9AB")); +static_assert(QtJniTypes::String("ABCDE").startsWith('A')); +static_assert(!QtJniTypes::String("ABCDE").startsWith('B')); + +static_assert(QtJniTypes::String("ABCDE").endsWith("CDE")); +static_assert(QtJniTypes::String("ABCDE").endsWith("E")); +static_assert(QtJniTypes::String("ABCDE").endsWith("ABCDE")); +static_assert(!QtJniTypes::String("ABCDE").endsWith("DEF")); +static_assert(!QtJniTypes::String("ABCDE").endsWith("ABCDEF")); +static_assert(QtJniTypes::String("ABCDE").endsWith('E')); +static_assert(!QtJniTypes::String("ABCDE").endsWith('F')); + +void tst_QJniTypes::initTestCase() +{ +} + +QTEST_MAIN(tst_QJniTypes) + +#include "tst_qjnitypes.moc"