diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index c638266256..8578afc61d 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -999,7 +999,7 @@ qt_internal_extend_target(Core CONDITION ANDROID SOURCES io/qstandardpaths_android.cpp io/qstorageinfo_linux.cpp - kernel/qjnitypes.h + kernel/qjnitypes.h kernel/qjnitypes_impl.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/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h index f7ffa836c2..2121ceb6fe 100644 --- a/src/corelib/kernel/qjnienvironment.h +++ b/src/corelib/kernel/qjnienvironment.h @@ -8,7 +8,7 @@ #if defined(Q_QDOC) || defined(Q_OS_ANDROID) #include -#include +#include QT_BEGIN_NAMESPACE diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 75c896a728..de73676630 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 #include -#include QT_BEGIN_NAMESPACE @@ -42,7 +41,6 @@ public: std::forward(args)...) {} QJniObject(jobject globalRef); - inline QJniObject(QtJniTypes::Object wrapper) noexcept : QJniObject(jobject(wrapper)) {} ~QJniObject(); template diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h index 067504c41d..72d6f2783c 100644 --- a/src/corelib/kernel/qjnitypes.h +++ b/src/corelib/kernel/qjnitypes.h @@ -4,330 +4,25 @@ #ifndef QJNITYPES_H #define QJNITYPES_H -#include -#include - #if defined(Q_QDOC) || defined(Q_OS_ANDROID) -#include + +#include +#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_array_v) { - using UnderlyingType = typename std::remove_extent_t; - static_assert(!std::is_array_v, - "typeSignature() does not handle multi-dimensional arrays"); - return String("[") + typeSignature(); - } else if constexpr (std::is_same_v) { - return String("Ljava/lang/Object;"); - } else if constexpr (std::is_same_v) { - return String("Ljava/lang/Class;"); - } else if constexpr (std::is_same_v) { - return String("Ljava/lang/String;"); - } else if constexpr (std::is_same_v) { - return String("[Ljava/lang/Object;"); - } else if constexpr (std::is_same_v) { - return String("Ljava/lang/Throwable;"); - } else if constexpr (std::is_same_v) { - return String("[Z"); - } else if constexpr (std::is_same_v) { - return String("[B"); - } else if constexpr (std::is_same_v) { - return String("[S"); - } else if constexpr (std::is_same_v) { - return String("[I"); - } else if constexpr (std::is_same_v) { - return String("[J"); - } else if constexpr (std::is_same_v) { - return String("[F"); - } else if constexpr (std::is_same_v) { - return String("[D"); - } else if constexpr (std::is_same_v) { - return String("[C"); - } else if constexpr (std::is_same_v) { - return String("Z"); - } else if constexpr (std::is_same_v) { - return String("Z"); - } else if constexpr (std::is_same_v) { - return String("B"); - } else if constexpr (std::is_same_v) { - return String("C"); - } else if constexpr (std::is_same_v) { - return String("C"); - } else if constexpr (std::is_same_v) { - return String("S"); - } else if constexpr (std::is_same_v) { - return String("S"); - } else if constexpr (std::is_same_v) { - return String("I"); - } else if constexpr (std::is_same_v) { - return String("I"); - } else if constexpr (std::is_same_v) { - return String("I"); - } else if constexpr (std::is_same_v) { - return String("J"); - } else if constexpr (std::is_same_v) { - return String("J"); - } else if constexpr (std::is_same_v) { - return String("F"); - } else if constexpr (std::is_same_v) { - return String("F"); - } else if constexpr (std::is_same_v) { - return String("D"); - } else if constexpr (std::is_same_v) { - return String("D"); - } else if constexpr (std::is_same_v) { - return String("V"); - } - - // else: The return type becomes void, indicating that the typeSignature - // template is not implemented for the respective type. We use this to - // detect invalid types in the ValidSignatureTypes and ValidFieldType - // predicates below. -} - -template -static void staticAssertClassNotRegistered() -{ - static_assert(flag, "Class not registered, use Q_DECLARE_JNI_CLASS"); -} - -template -constexpr auto className() -{ - if constexpr (std::is_same_v) - return String("java/lang/String"); - else - staticAssertClassNotRegistered(); -} - -template -static constexpr bool isPrimitiveType() -{ - return typeSignature().size() == 2; -} - -template -static constexpr bool isObjectType() -{ - if constexpr (std::is_convertible_v) { - return true; - } else { - constexpr auto signature = typeSignature(); - return (signature.startsWith('L') || signature.startsWith('[')) - && signature.endsWith(';'); - } -} - -template -static constexpr bool isArrayType() -{ - constexpr auto signature = typeSignature(); - return signature.startsWith('['); -} - -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)!"); -} - -// A set of types is valid if typeSignature is implemented for all of them -template -constexpr bool ValidSignatureTypesDetail = !std::disjunction()), - void>..., - IsStringType...>::value; -template -using ValidSignatureTypes = std::enable_if_t< - ValidSignatureTypesDetail...>, bool>; - -template -constexpr bool ValidFieldTypeDetail = isObjectType() || isPrimitiveType(); -template -using ValidFieldType = std::enable_if_t< - ValidFieldTypeDetail>, bool>; - - -template = true> -static constexpr auto methodSignature() -{ - return (String("(") + - ... + typeSignature>()) - + String(")") - + typeSignature(); -} - -template = true> -static constexpr auto fieldSignature() -{ - return QtJniTypes::typeSignature(); -} - -template = true> -static constexpr auto constructorSignature() -{ - return methodSignature(); -} - -template = true> -static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...)) -{ - return methodSignature(); -} - -template = true> -static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...)) -{ - return methodSignature(); -} - // A generic thin wrapper around jobject, convertible to jobject. // We need this as a baseclass so that QJniObject can be implicitly -// constructed from the various subclasses - we can't provide an -// operator QJniObject() here as the class is not declared. +// constructed from the various subclasses. We can also pass instances +// of this type (or of any of the generated subclasses) as if it was +// a jobject. struct Object { jobject _object; constexpr operator jobject() const { return _object; } + operator QJniObject() const { return QJniObject(_object); } }; } // namespace QtJniTypes @@ -402,6 +97,6 @@ static const JNINativeMethod Method##_method = { \ QT_END_NAMESPACE -#endif +#endif // defined(Q_QDOC) || defined(Q_OS_ANDROID) #endif // QJNITYPES_H diff --git a/src/corelib/kernel/qjnitypes_impl.h b/src/corelib/kernel/qjnitypes_impl.h new file mode 100644 index 0000000000..a7a3a30b2a --- /dev/null +++ b/src/corelib/kernel/qjnitypes_impl.h @@ -0,0 +1,329 @@ +// 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 QJNITYPES_IMPL_H +#define QJNITYPES_IMPL_H + +#include +#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_array_v) { + using UnderlyingType = typename std::remove_extent_t; + static_assert(!std::is_array_v, + "typeSignature() does not handle multi-dimensional arrays"); + return String("[") + typeSignature(); + } else if constexpr (std::is_same_v) { + return String("Ljava/lang/Object;"); + } else if constexpr (std::is_same_v) { + return String("Ljava/lang/Class;"); + } else if constexpr (std::is_same_v) { + return String("Ljava/lang/String;"); + } else if constexpr (std::is_same_v) { + return String("[Ljava/lang/Object;"); + } else if constexpr (std::is_same_v) { + return String("Ljava/lang/Throwable;"); + } else if constexpr (std::is_same_v) { + return String("[Z"); + } else if constexpr (std::is_same_v) { + return String("[B"); + } else if constexpr (std::is_same_v) { + return String("[S"); + } else if constexpr (std::is_same_v) { + return String("[I"); + } else if constexpr (std::is_same_v) { + return String("[J"); + } else if constexpr (std::is_same_v) { + return String("[F"); + } else if constexpr (std::is_same_v) { + return String("[D"); + } else if constexpr (std::is_same_v) { + return String("[C"); + } else if constexpr (std::is_same_v) { + return String("Z"); + } else if constexpr (std::is_same_v) { + return String("Z"); + } else if constexpr (std::is_same_v) { + return String("B"); + } else if constexpr (std::is_same_v) { + return String("C"); + } else if constexpr (std::is_same_v) { + return String("C"); + } else if constexpr (std::is_same_v) { + return String("S"); + } else if constexpr (std::is_same_v) { + return String("S"); + } else if constexpr (std::is_same_v) { + return String("I"); + } else if constexpr (std::is_same_v) { + return String("I"); + } else if constexpr (std::is_same_v) { + return String("I"); + } else if constexpr (std::is_same_v) { + return String("J"); + } else if constexpr (std::is_same_v) { + return String("J"); + } else if constexpr (std::is_same_v) { + return String("F"); + } else if constexpr (std::is_same_v) { + return String("F"); + } else if constexpr (std::is_same_v) { + return String("D"); + } else if constexpr (std::is_same_v) { + return String("D"); + } else if constexpr (std::is_same_v) { + return String("V"); + } + + // else: The return type becomes void, indicating that the typeSignature + // template is not implemented for the respective type. We use this to + // detect invalid types in the ValidSignatureTypes and ValidFieldType + // predicates below. +} + +template +static void staticAssertClassNotRegistered() +{ + static_assert(flag, "Class not registered, use Q_DECLARE_JNI_CLASS"); +} + +template +constexpr auto className() +{ + if constexpr (std::is_same_v) + return String("java/lang/String"); + else + staticAssertClassNotRegistered(); +} + +template +static constexpr bool isPrimitiveType() +{ + return typeSignature().size() == 2; +} + +template +static constexpr bool isObjectType() +{ + if constexpr (std::is_convertible_v) { + return true; + } else { + constexpr auto signature = typeSignature(); + return (signature.startsWith('L') || signature.startsWith('[')) + && signature.endsWith(';'); + } +} + +template +static constexpr bool isArrayType() +{ + constexpr auto signature = typeSignature(); + return signature.startsWith('['); +} + +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)!"); +} + +// A set of types is valid if typeSignature is implemented for all of them +template +constexpr bool ValidSignatureTypesDetail = !std::disjunction()), + void>..., + IsStringType...>::value; +template +using ValidSignatureTypes = std::enable_if_t< + ValidSignatureTypesDetail...>, bool>; + +template +constexpr bool ValidFieldTypeDetail = isObjectType() || isPrimitiveType(); +template +using ValidFieldType = std::enable_if_t< + ValidFieldTypeDetail>, bool>; + + +template = true> +static constexpr auto methodSignature() +{ + return (String("(") + + ... + typeSignature>()) + + String(")") + + typeSignature(); +} + +template = true> +static constexpr auto fieldSignature() +{ + return QtJniTypes::typeSignature(); +} + +template = true> +static constexpr auto constructorSignature() +{ + return methodSignature(); +} + +template = true> +static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...)) +{ + return methodSignature(); +} + +template = true> +static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...)) +{ + return methodSignature(); +} + +} // namespace QtJniTypes + +QT_END_NAMESPACE + +#endif + +#endif // QJNITYPES_IMPL_H