diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h index 6fb557288d5..a0bb25cf0f1 100644 --- a/src/corelib/kernel/qjniarray.h +++ b/src/corelib/kernel/qjniarray.h @@ -325,7 +325,6 @@ private: template // need to specialize traits for it, so can't be nested struct QJniArrayMutableValueRef { - using refwrapper = T; T value; QJniArrayMutableIterator back = {-1, nullptr}; @@ -466,11 +465,17 @@ public: // forward-iterable container, so explicitly remove that from the overload // set so that the copy constructors get used instead. // Used also in the deduction guide, so must be public + template + using IsSequentialOrContiguous = std::bool_constant< + IsSequentialContainerHelper::isForwardIterable + || (isContiguousContainer && ElementTypeHelper::isPrimitive) + >; template > - static constexpr bool isCompatibleSourceContainer = - (IsSequentialContainerHelper::isForwardIterable - || (isContiguousContainer && ElementTypeHelper::isPrimitive)) - && !std::is_base_of_v; + static constexpr bool isCompatibleSourceContainer = std::conjunction_v< + std::negation>, + IsSequentialOrContiguous, + std::negation> + >; template using if_compatible_source_container = std::enable_if_t, bool>; @@ -963,29 +968,70 @@ auto QJniArrayBase::makeObjectArray(List &&list) namespace QtJniTypes { -template struct IsJniArray: std::false_type {}; -template struct IsJniArray> : std::true_type {}; -template struct Traits> { +template struct Traits> +{ template = true> static constexpr auto signature() { return CTString("[") + Traits::signature(); } + static auto convertToJni(JNIEnv *, const QJniArray &value) + { + return value.arrayObject(); + } + static auto convertFromJni(QJniObject &&object) + { + return QJniArray(std::move(object)); + } }; template struct Traits> : public Traits {}; -template struct Traits> { - template = true> +template struct Traits>> +{ + // QByteArray::value_type is char, which maps to 'C'; we need 'B', i.e. jbyte + using ElementType = std::conditional_t, + jbyte, typename T::value_type>; + + template = true> static constexpr auto signature() { - return CTString("[") + Traits::signature(); + return CTString("[") + Traits::signature(); + } + + static auto convertToJni(JNIEnv *env, const T &value) + { + using QJniArrayType = decltype(QJniArrayBase::fromContainer(value)); + using ArrayType = decltype(std::declval().arrayObject()); + return static_cast(env->NewLocalRef(QJniArray(value).arrayObject())); + } + + static auto convertFromJni(QJniObject &&object) + { + // if we were to create a QJniArray from Type... + using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval())); + // then that QJniArray would have elements of type + using ArrayType = typename QJniArrayType::Type; + // construct a QJniArray from a jobject pointer of that type + return QJniArray(object.template object()).toContainer(); } }; -template <> struct Traits { + +template struct Traits>> +{ + using ElementType = std::remove_extent_t; + + template = true> static constexpr auto signature() { - return CTString("[B"); + static_assert(!std::is_array_v, + "Traits::signature() does not handle multi-dimensional arrays"); + return CTString("[") + Traits::signature(); + } + + static constexpr auto convertFromJni(QJniObject &&object) + { + return QJniArray(std::move(object)); } }; } diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 7ff30fc1d99..c28149bdc59 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -20,6 +20,13 @@ namespace QtJniTypes { namespace Detail { +// any type with an "jobject object()" member function stores a global reference +template struct StoresGlobalRefTest : std::false_type {}; +template +struct StoresGlobalRefTest().object())>> + : std::is_same().object()), jobject> +{}; + template struct LocalFrame { mutable JNIEnv *env; @@ -39,15 +46,6 @@ struct LocalFrame { hasFrame = jniEnv()->PushLocalFrame(sizeof...(Args)) == 0; return hasFrame; } - template - auto newLocalRef(jobject object) - { - if (!ensureFrame()) { - // if the JVM is out of memory, avoid making matters worse - return T{}; - } - return static_cast(jniEnv()->NewLocalRef(object)); - } JNIEnv *jniEnv() const { if (!env) @@ -59,9 +57,28 @@ struct LocalFrame { return env ? QJniEnvironment::checkAndClearExceptions(env) : false; } template - auto convertToJni(T &&value); + auto convertToJni(T &&value) + { + using Type = q20::remove_cvref_t; + using ResultType = decltype(QtJniTypes::Traits::convertToJni(jniEnv(), + std::declval())); + if constexpr (std::is_base_of_v, + std::remove_pointer_t>) { + // Make sure the local frame is engaged if we create a jobject, unless + // we know that the value stores a global reference that it returns. + if constexpr (!qxp::is_detected_v) { + if (!ensureFrame()) + return ResultType{}; + } + } + return QtJniTypes::Traits::convertToJni(jniEnv(), std::forward(value)); + } template - auto convertFromJni(QJniObject &&object); + auto convertFromJni(QJniObject &&object) + { + using Type = q20::remove_cvref_t; + return QtJniTypes::Traits::convertFromJni(std::move(object)); + } }; } } @@ -833,6 +850,14 @@ private: template struct Traits> { static constexpr auto signature() { return Traits::signature(); } static constexpr auto className() { return Traits::className(); } + static auto convertToJni(JNIEnv *, const JObject &value) + { + return value.object(); + } + static auto convertFromJni(QJniObject &&object) + { + return JObject(std::move(object)); + } }; template<> @@ -847,87 +872,42 @@ struct Traits { return CTString("Ljava/lang/Object;"); } + + static auto convertToJni(JNIEnv *, const QJniObject &value) + { + return value.object(); + } + static auto convertFromJni(QJniObject &&object) + { + return std::move(object); + } +}; + +template<> +struct Traits +{ + static constexpr auto className() + { + return CTString("java/lang/String"); + } + static constexpr auto signature() + { + return CTString("Ljava/lang/String;"); + } + + static auto convertToJni(JNIEnv *env, const QString &value) + { + return QtJniTypes::Detail::fromQString(value, env); + } + + static auto convertFromJni(QJniObject &&object) + { + return object.toString(); + } }; } -// 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 -QT_BEGIN_NAMESPACE - -namespace QtJniTypes { -namespace detail { -template -using FromContainerTest = decltype(QJniArrayBase::fromContainer(std::declval())); - -template -static constexpr bool isCompatibleSourceContainer = qxp::is_detected_v; - -template -using IsReferenceWrapperTest = typename It::refwrapper; - -template -static constexpr bool isReferenceWrapper = qxp::is_detected_v; -} -} - -template -template -auto QtJniTypes::Detail::LocalFrame::convertToJni(T &&value) -{ - using Type = q20::remove_cvref_t; - if constexpr (std::is_same_v) { - if (ensureFrame()) // fromQString already returns a local reference - return QtJniTypes::Detail::fromQString(value, jniEnv()); - return jstring{}; - } else if constexpr (QtJniTypes::IsJniArray::value) { - return value.arrayObject(); - } else if constexpr (QtJniTypes::detail::isCompatibleSourceContainer) { - using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::forward(value))); - using ArrayType = decltype(std::declval().arrayObject()); - return newLocalRef(QJniArrayBase::fromContainer(std::forward(value)).template object()); - } else if constexpr (QtJniTypes::detail::isReferenceWrapper) { - return convertToJni(*value); - } else if constexpr (std::is_base_of_v - || std::is_base_of_v) { - return value.object(); - } else { - return std::forward(value); - } -} - -template -template -auto QtJniTypes::Detail::LocalFrame::convertFromJni(QJniObject &&object) -{ - using Type = q20::remove_cvref_t; - if constexpr (std::is_same_v) { - return object.toString(); - } else if constexpr (QtJniTypes::IsJniArray::value) { - return T(std::move(object)); - } else if constexpr (QtJniTypes::detail::isCompatibleSourceContainer) { - // if we were to create a QJniArray from Type... - using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval())); - // 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(object.template object()).toContainer(); - } else if constexpr (std::is_array_v) { - using ElementType = std::remove_extent_t; - return QJniArray(std::move(object)); - } else if constexpr (std::is_base_of_v - && !std::is_same_v) { - return T{std::move(object)}; - } else if constexpr (std::is_base_of_v) { - return T{std::move(object)}; - } else { - return std::move(object); - } -} - - QT_END_NAMESPACE #endif diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h index 78ab7ad8597..c7bec5c1946 100644 --- a/src/corelib/kernel/qjnitypes.h +++ b/src/corelib/kernel/qjnitypes.h @@ -8,6 +8,7 @@ #include #include +#include #if 0 // This is needed for generating the QtJniTypes forward header @@ -140,8 +141,7 @@ namespace Detail { template struct JNITypeForArgImpl { - using LocalFrame = QtJniTypes::Detail::LocalFrame; - using JNIType = decltype(std::declval().convertToJni(std::declval())); + using JNIType = decltype(QtJniTypes::Traits::convertToJni(nullptr, {})); static Arg fromVarArg(JNIType t) // JNIType is always POD { // Special case: if convertToJni doesn't do anything, don't do anything @@ -150,8 +150,7 @@ struct JNITypeForArgImpl if constexpr (std::is_same_v) { return t; } else { - LocalFrame frame; - return frame.template convertFromJni(t); + return QtJniTypes::Traits::convertFromJni(t); } } }; diff --git a/src/corelib/kernel/qjnitypes_impl.h b/src/corelib/kernel/qjnitypes_impl.h index 6e1ba45db83..95e7aa59661 100644 --- a/src/corelib/kernel/qjnitypes_impl.h +++ b/src/corelib/kernel/qjnitypes_impl.h @@ -15,6 +15,8 @@ QT_BEGIN_NAMESPACE +class QJniObject; + namespace QtJniTypes { @@ -164,7 +166,7 @@ template struct IsStringType : std::true_type {}; template struct IsStringType : std::true_type {}; template struct IsStringType : std::true_type {}; -template +template struct Traits { // The return type of className/signature becomes void for any type // not handled here. This indicates that the Traits type is not specialized @@ -189,11 +191,6 @@ struct Traits { if constexpr (!std::is_same_v) { // the type signature of any object class is L; return CTString("L") + className() + CTString(";"); - } else if constexpr (std::is_array_v) { - using UnderlyingType = typename std::remove_extent_t; - static_assert(!std::is_array_v, - "Traits::signature() does not handle multi-dimensional arrays"); - return CTString("[") + Traits::signature(); } else if constexpr (std::is_same_v) { return CTString("[Ljava/lang/Object;"); } else if constexpr (std::is_same_v) { @@ -248,11 +245,19 @@ struct Traits { return CTString("V"); } else if constexpr (std::is_enum_v) { return Traits>::signature(); - } else if constexpr (std::is_same_v) { - return CTString("Ljava/lang/String;"); } // else: return void -> not implemented } + + template + static auto convertToJni(JNIEnv *, U &&value) + { + return std::forward(value); + } + static auto convertFromJni(QJniObject &&object) + { + return std::move(object); + } }; template