diff options
| -rw-r--r-- | src/corelib/kernel/qjniarray.h | 4 | ||||
| -rw-r--r-- | src/corelib/kernel/qjnienvironment.cpp | 8 | ||||
| -rw-r--r-- | src/corelib/kernel/qjnienvironment.h | 1 | ||||
| -rw-r--r-- | src/corelib/kernel/qjniobject.cpp | 135 | ||||
| -rw-r--r-- | src/corelib/kernel/qjniobject.h | 523 | ||||
| -rw-r--r-- | src/corelib/tools/qflatmap_p.h | 33 | ||||
| -rw-r--r-- | src/gui/text/qfont.cpp | 2 | ||||
| -rw-r--r-- | src/plugins/platforms/windows/qwindowswindowclassdescription.cpp | 74 | ||||
| -rw-r--r-- | src/plugins/platforms/windows/qwindowswindowclassdescription.h | 12 | ||||
| -rw-r--r-- | tests/auto/corelib/kernel/qjniobject/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java | 4 | ||||
| -rw-r--r-- | tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp | 567 |
12 files changed, 1078 insertions, 293 deletions
diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h index 13349688d20..97d0cd37682 100644 --- a/src/corelib/kernel/qjniarray.h +++ b/src/corelib/kernel/qjniarray.h @@ -872,7 +872,7 @@ auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion) const size_type length = size_type(std::size(list)); JNIEnv *env = QJniEnvironment::getJniEnv(); auto localArray = (env->*newArray)(length); - if (QJniEnvironment::checkAndClearExceptions(env)) { + if (env->ExceptionCheck()) { if (localArray) env->DeleteLocalRef(localArray); return QJniArray<ElementType>(); @@ -916,7 +916,7 @@ auto QJniArrayBase::makeObjectArray(List &&list) elementClass = env->GetObjectClass(*std::begin(list)); } auto localArray = env->NewObjectArray(length, elementClass, nullptr); - if (QJniEnvironment::checkAndClearExceptions(env)) { + if (env->ExceptionCheck()) { if (localArray) env->DeleteLocalRef(localArray); return ResultType(); diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp index b4f8497ddda..1ee658fd18d 100644 --- a/src/corelib/kernel/qjnienvironment.cpp +++ b/src/corelib/kernel/qjnienvironment.cpp @@ -559,4 +559,12 @@ bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::Outp return false; } +/*! + Returns the stack trace that resulted in \a exception being thrown. +*/ +QStringList QJniEnvironment::stackTrace(jthrowable exception) +{ + return exceptionMessage(getJniEnv(), exception).split(u'\n'); +} + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h index b473f75bed1..a5f3700b1f0 100644 --- a/src/corelib/kernel/qjnienvironment.h +++ b/src/corelib/kernel/qjnienvironment.h @@ -89,6 +89,7 @@ public: static bool checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose); static JNIEnv *getJniEnv(); + static QStringList stackTrace(jthrowable exception); private: Q_DISABLE_COPY_MOVE(QJniEnvironment) diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp index 8f3da9a8595..59117bd01d4 100644 --- a/src/corelib/kernel/qjniobject.cpp +++ b/src/corelib/kernel/qjniobject.cpp @@ -348,7 +348,10 @@ static inline QByteArray cacheKey(Args &&...args) return (QByteArrayView(":") + ... + QByteArrayView(args)); } -typedef QHash<QByteArray, jclass> JClassHash; +struct JClassHash : QHash<QByteArray, jclass> +{ + jmethodID loadClassMethod = 0; +}; Q_GLOBAL_STATIC(JClassHash, cachedClasses) Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock) @@ -391,29 +394,22 @@ bool QJniObjectPrivate::isJString(JNIEnv *env) const */ static QJniObject getCleanJniObject(jobject object, JNIEnv *env) { - if (QJniEnvironment::checkAndClearExceptions(env) || !object) { - if (object) - env->DeleteLocalRef(object); + if (!object || env->ExceptionCheck()) return QJniObject(); - } - QJniObject res(object); - env->DeleteLocalRef(object); - return res; + return QJniObject::fromLocalRef(object); } -/*! - \internal - \a className must be slash-encoded -*/ -jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env) +namespace { + +jclass loadClassHelper(const QByteArray &className, JNIEnv *env) { Q_ASSERT(env); QByteArray classNameArray(className); #ifdef QT_DEBUG if (classNameArray.contains('.')) { qWarning("QtAndroidPrivate::findClass: className '%s' should use slash separators!", - className); + className.constData()); } #endif classNameArray.replace('.', '/'); @@ -442,21 +438,40 @@ jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env) if (!clazz) { // Wrong class loader, try our own - QJniObject classLoader(QtAndroidPrivate::classLoader()); - if (!classLoader.isValid()) + jobject classLoader = QtAndroidPrivate::classLoader(); + if (!classLoader) return nullptr; + if (!cachedClasses->loadClassMethod) { + jclass classLoaderClass = env->GetObjectClass(classLoader); + if (!classLoaderClass) + return nullptr; + cachedClasses->loadClassMethod = + env->GetMethodID(classLoaderClass, + "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + env->DeleteLocalRef(classLoaderClass); + if (!cachedClasses->loadClassMethod) { + qCritical("Couldn't find the 'loadClass' method in the Qt class loader"); + return nullptr; + } + } + // ClassLoader::loadClass on the other hand wants the binary name of the class, // e.g. dot-separated. In testing it works also with /, but better to stick to // the specification. const QString binaryClassName = QString::fromLatin1(className).replace(u'/', u'.'); - jstring classNameObject = env->NewString(reinterpret_cast<const jchar*>(binaryClassName.constData()), - binaryClassName.length()); - QJniObject classObject = classLoader.callMethod<jclass>("loadClass", classNameObject); + jstring classNameObject = env->NewString(binaryClassName.utf16(), binaryClassName.length()); + jobject classObject = env->CallObjectMethod(classLoader, + cachedClasses->loadClassMethod, + classNameObject); env->DeleteLocalRef(classNameObject); - if (!QJniEnvironment::checkAndClearExceptions(env) && classObject.isValid()) - clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object())); + if (classObject && !env->ExceptionCheck()) { + clazz = static_cast<jclass>(env->NewGlobalRef(classObject)); + env->DeleteLocalRef(classObject); + } + // Clearing the exception is the caller's responsibility (see + // QtAndroidPrivate::findClass()) and QJniObject::loadClass{KeepExceptions} } if (clazz) @@ -465,11 +480,32 @@ jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env) return clazz; } +} // unnamed namespace + +/*! + \internal + \a className must be slash-encoded +*/ +jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env) +{ + jclass clazz = loadClassHelper(className, env); + if (!clazz) + QJniEnvironment::checkAndClearExceptions(env); + return clazz; +} + jclass QJniObject::loadClass(const QByteArray &className, JNIEnv *env) { return QtAndroidPrivate::findClass(className, env); } +jclass QJniObject::loadClassKeepExceptions(const QByteArray &className, JNIEnv *env) +{ + return loadClassHelper(className, env); +} + + + typedef QHash<QByteArray, jmethodID> JMethodIDHash; Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID) Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock) @@ -483,9 +519,6 @@ jmethodID QJniObject::getMethodID(JNIEnv *env, jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, signature) : env->GetMethodID(clazz, name, signature); - if (QJniEnvironment::checkAndClearExceptions(env)) - return nullptr; - return id; } @@ -525,7 +558,8 @@ jmethodID QJniObject::getCachedMethodID(JNIEnv *env, jmethodID id = getMethodID(env, clazz, name, signature, isStatic); - cachedMethodID->insert(key, id); + if (id) + cachedMethodID->insert(key, id); return id; } } @@ -549,9 +583,6 @@ jfieldID QJniObject::getFieldID(JNIEnv *env, jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, signature) : env->GetFieldID(clazz, name, signature); - if (QJniEnvironment::checkAndClearExceptions(env)) - return nullptr; - return id; } @@ -583,7 +614,8 @@ jfieldID QJniObject::getCachedFieldID(JNIEnv *env, jfieldID id = getFieldID(env, clazz, name, signature, isStatic); - cachedFieldID->insert(key, id); + if (id) + cachedFieldID->insert(key, id); return id; } } @@ -1008,15 +1040,15 @@ QJniObject QJniObject::callObjectMethod(const char *methodName, const char *sign { JNIEnv *env = jniEnv(); jmethodID id = getCachedMethodID(env, methodName, signature); - if (id) { - va_list args; - va_start(args, signature); - QJniObject res = getCleanJniObject(env->CallObjectMethodV(d->m_jobject, id, args), env); - va_end(args); - return res; - } + va_list args; + va_start(args, signature); + // can't go back from variadic arguments to variadic templates + jobject object = id ? jniEnv()->CallObjectMethodV(javaObject(), id, args) : nullptr; + QJniObject res = getCleanJniObject(object, env); + va_end(args); - return QJniObject(); + QJniEnvironment::checkAndClearExceptions(env); + return res; } /*! @@ -1150,7 +1182,7 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, */ /*! - \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, T value); + \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, Type value); Sets the static field \a fieldName on the class \a className to \a value using the setter with \a signature. @@ -1158,7 +1190,7 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, */ /*! - \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, T value); + \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, Type value); Sets the static field \a fieldName on the class \a clazz to \a value using the setter with \a signature. @@ -1196,19 +1228,19 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, */ /*! - \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, T value) + \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(const char *className, const char *fieldName, Type value) Sets the static field \a fieldName of the class \a className to \a value. */ /*! - \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, T value) + \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(jclass clazz, const char *fieldName, Type value) Sets the static field \a fieldName of the class \a clazz to \a value. */ /*! - \fn template <typename Klass, typename T> auto QJniObject::setStaticField(const char *fieldName, T value) + \fn template <typename Klass, typename Ret, typename Type> auto QJniObject::setStaticField(const char *fieldName, Type value) Sets the static field \a fieldName of the class \c Klass to \a value. @@ -1263,11 +1295,16 @@ QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName, { JNIEnv *env = QJniEnvironment::getJniEnv(); jfieldID id = getFieldID(env, clazz, fieldName, signature, true); - return getCleanJniObject(env->GetStaticObjectField(clazz, id), env); + + const auto clearExceptions = qScopeGuard([env]{ + QJniEnvironment::checkAndClearExceptions(env); + }); + + return getCleanJniObject(getStaticObjectFieldImpl(env, clazz, id), env); } /*! - \fn template <typename T> void QJniObject::setField(const char *fieldName, const char *signature, T value) + \fn template <typename Ret, typename Type> void QJniObject::setField(const char *fieldName, const char *signature, Type value) Sets the value of \a fieldName with \a signature to \a value. @@ -1304,14 +1341,16 @@ QJniObject QJniObject::getObjectField(const char *fieldName, const char *signatu { JNIEnv *env = jniEnv(); jfieldID id = getCachedFieldID(env, fieldName, signature); - if (!id) - return QJniObject(); - return getCleanJniObject(env->GetObjectField(d->m_jobject, id), env); + const auto clearExceptions = qScopeGuard([env]{ + QJniEnvironment::checkAndClearExceptions(env); + }); + + return getCleanJniObject(getObjectFieldImpl(env, id), env); } /*! - \fn template <typename T> void QJniObject::setField(const char *fieldName, T value) + \fn template <typename Ret, typename Type> void QJniObject::setField(const char *fieldName, Type value) Sets the value of \a fieldName to \a value. diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 236a49544be..06dfc328b4b 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -27,10 +27,29 @@ struct StoresGlobalRefTest<T, std::void_t<decltype(std::declval<T>().object())>> : std::is_same<decltype(std::declval<T>().object()), jobject> {}; -template <typename ...Args> +// detect if a type is std::expected-like +template <typename R, typename = void> +struct CallerHandlesException : std::false_type { + using value_type = R; +}; +template <typename R> +struct CallerHandlesException<R, std::void_t<typename R::unexpected_type, + typename R::value_type, + typename R::error_type>> : std::true_type +{ + using value_type = typename R::value_type; +}; + +template <typename ReturnType> +static constexpr bool callerHandlesException = CallerHandlesException<ReturnType>::value; + +template <typename Ret, typename ...Args> struct LocalFrame { + using ReturnType = Ret; + mutable JNIEnv *env; bool hasFrame = false; + explicit LocalFrame(JNIEnv *env = nullptr) noexcept : env(env) { @@ -52,9 +71,12 @@ struct LocalFrame { env = QJniEnvironment::getJniEnv(); return env; } - bool checkAndClearExceptions() + bool checkAndClearExceptions() const { - return env ? QJniEnvironment::checkAndClearExceptions(env) : false; + if constexpr (callerHandlesException<ReturnType>) + return false; + else + return QJniEnvironment::checkAndClearExceptions(jniEnv()); } template <typename T> auto convertToJni(T &&value) @@ -79,13 +101,46 @@ struct LocalFrame { using Type = q20::remove_cvref_t<T>; return QtJniTypes::Traits<Type>::convertFromJni(std::move(object)); } + + template <typename T> + auto convertFromJni(jobject object); + + auto makeResult() + { + if constexpr (callerHandlesException<ReturnType>) { + JNIEnv *env = jniEnv(); + if (env->ExceptionCheck()) { + jthrowable exception = env->ExceptionOccurred(); + env->ExceptionClear(); + return ReturnType(typename ReturnType::unexpected_type(exception)); + } + return ReturnType(); + } else { + checkAndClearExceptions(); + } + } + + template <typename Value> + auto makeResult(Value &&value) + { + if constexpr (callerHandlesException<ReturnType>) { + auto maybeValue = makeResult(); + if (maybeValue) + return ReturnType(std::forward<Value>(value)); + return std::move(maybeValue); + } else { + checkAndClearExceptions(); + return std::forward<Value>(value); + } + } }; } } class Q_CORE_EXPORT QJniObject { - template <typename ...Args> using LocalFrame = QtJniTypes::Detail::LocalFrame<Args...>; + template <typename Ret, typename ...Args> using LocalFrame + = QtJniTypes::Detail::LocalFrame<Ret, Args...>; public: QJniObject(); @@ -97,12 +152,12 @@ public: #endif > explicit QJniObject(const char *className, Args &&...args) - : QJniObject(LocalFrame<Args...>{}, className, std::forward<Args>(args)...) + : QJniObject(LocalFrame<QJniObject, Args...>{}, className, std::forward<Args>(args)...) { } private: template<typename ...Args> - explicit QJniObject(LocalFrame<Args...> localFrame, const char *className, Args &&...args) + explicit QJniObject(LocalFrame<QJniObject, Args...> localFrame, const char *className, Args &&...args) : QJniObject(className, QtJniTypes::constructorSignature<Args...>().data(), localFrame.convertToJni(std::forward<Args>(args))...) { @@ -130,13 +185,23 @@ public: void swap(QJniObject &other) noexcept { d.swap(other.d); } - template<typename Class, typename ...Args> - static inline QJniObject construct(Args &&...args) + template<typename Class, typename ...Args +#ifndef Q_QDOC + , QtJniTypes::IfValidSignatureTypes<Class, Args...> = true +#endif + > + static inline auto construct(Args &&...args) { - LocalFrame<Args...> frame; - return QJniObject(QtJniTypes::Traits<Class>::className().data(), - QtJniTypes::constructorSignature<Args...>().data(), - frame.convertToJni(std::forward<Args>(args))...); + LocalFrame<Class, Args...> frame; + jclass clazz = QJniObject::loadClassKeepExceptions(QtJniTypes::Traits<Class>::className().data(), + frame.jniEnv()); + auto res = clazz + ? QJniObject(clazz, QtJniTypes::constructorSignature<Args...>().data(), + frame.convertToJni(std::forward<Args>(args))...) + : QtJniTypes::Detail::callerHandlesException<Class> + ? QJniObject(Qt::Initialization::Uninitialized) + : QJniObject(); + return frame.makeResult(std::move(res)); } jobject object() const; @@ -149,47 +214,49 @@ public: jclass objectClass() const; QByteArray className() const; - template <typename Ret = void, typename ...Args + template <typename ReturnType = void, typename ...Args #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<Ret> = true + , QtJniTypes::IfValidFieldType<ReturnType> = true #endif > auto callMethod(const char *methodName, const char *signature, Args &&...args) const { - LocalFrame<Args...> frame(jniEnv()); + using Ret = typename QtJniTypes::Detail::CallerHandlesException<ReturnType>::value_type; + LocalFrame<ReturnType, Args...> frame(jniEnv()); + jmethodID id = getCachedMethodID(frame.jniEnv(), methodName, signature); + if constexpr (QtJniTypes::isObjectType<Ret>()) { - return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature, - frame.convertToJni(std::forward<Args>(args))...)); + return frame.makeResult(frame.template convertFromJni<Ret>(callObjectMethodImpl( + id, 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; + return frame.makeResult(res); } } if constexpr (!std::is_same_v<Ret, void>) - return Ret{}; + return frame.makeResult(Ret{}); + else + return frame.makeResult(); } } - template <typename Ret = void, typename ...Args + template <typename ReturnType = void, typename ...Args #ifndef Q_QDOC - , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true + , QtJniTypes::IfValidSignatureTypes<ReturnType, 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)...); + constexpr auto signature = QtJniTypes::methodSignature<ReturnType, Args...>(); + return callMethod<ReturnType>(methodName, signature.data(), std::forward<Args>(args)...); } template <typename Ret, typename ...Args @@ -201,9 +268,11 @@ public: { QtJniTypes::assertObjectType<Ret>(); constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); - LocalFrame<Args...> frame(jniEnv()); - return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature, + LocalFrame<Ret, Args...> frame(jniEnv()); + auto object = frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature, frame.convertToJni(std::forward<Args>(args))...)); + frame.checkAndClearExceptions(); + return object; } QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const; @@ -211,90 +280,93 @@ public: template <typename Ret = void, 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); + LocalFrame<Ret, Args...> frame; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); return callStaticMethod<Ret>(clazz, methodName, signature, std::forward<Args>(args)...); } template <typename Ret = void, 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) + LocalFrame<Ret, Args...> frame; + jmethodID id = clazz ? getMethodID(frame.jniEnv(), clazz, methodName, signature, true) : 0; return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...); } - template <typename Ret = void, typename ...Args + template <typename ReturnType = void, typename ...Args #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<Ret> = true + , QtJniTypes::IfValidFieldType<ReturnType> = true #endif > static auto callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args) { - LocalFrame<Args...> frame; + using Ret = typename QtJniTypes::Detail::CallerHandlesException<ReturnType>::value_type; + LocalFrame<ReturnType, Args...> frame; if constexpr (QtJniTypes::isObjectType<Ret>()) { - return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodId, - frame.convertToJni(std::forward<Args>(args))...)); + return frame.makeResult(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; + return frame.makeResult(res); } } if constexpr (!std::is_same_v<Ret, void>) - return Ret{}; + return frame.makeResult(Ret{}); + else + return frame.makeResult(); } } - template <typename Ret = void, typename ...Args + template <typename ReturnType = void, typename ...Args #ifndef Q_QDOC - , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true + , QtJniTypes::IfValidSignatureTypes<ReturnType, 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, + using Ret = typename QtJniTypes::Detail::CallerHandlesException<ReturnType>::value_type; + LocalFrame<Ret, Args...> frame; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); + const jmethodID id = clazz ? getMethodID(frame.jniEnv(), clazz, methodName, QtJniTypes::methodSignature<Ret, Args...>().data(), true) : 0; - return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...); + return callStaticMethod<ReturnType>(clazz, id, std::forward<Args>(args)...); } - template <typename Ret = void, typename ...Args + template <typename ReturnType = void, typename ...Args #ifndef Q_QDOC - , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true + , QtJniTypes::IfValidSignatureTypes<ReturnType, 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)...); + constexpr auto signature = QtJniTypes::methodSignature<ReturnType, Args...>(); + return callStaticMethod<ReturnType>(clazz, methodName, signature.data(), std::forward<Args>(args)...); } - template <typename Klass, typename Ret = void, typename ...Args + template <typename Klass, typename ReturnType = void, typename ...Args #ifndef Q_QDOC - , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true + , QtJniTypes::IfValidSignatureTypes<ReturnType, Args...> = true #endif > static auto callStaticMethod(const char *methodName, Args &&...args) { - JNIEnv *env = QJniEnvironment::getJniEnv(); + LocalFrame<ReturnType, Args...> frame; 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) + frame.jniEnv()); + const jmethodID id = clazz ? getMethodID(frame.jniEnv(), clazz, methodName, + QtJniTypes::methodSignature<ReturnType, Args...>().data(), true) : 0; - return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...); + return callStaticMethod<ReturnType>(clazz, id, std::forward<Args>(args)...); } static QJniObject callStaticObjectMethod(const char *className, const char *methodName, @@ -315,7 +387,7 @@ public: { QtJniTypes::assertObjectType<Ret>(); constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); - LocalFrame<Args...> frame; + LocalFrame<QJniObject, Args...> frame; return frame.template convertFromJni<Ret>(callStaticObjectMethod(className, methodName, signature.data(), frame.convertToJni(std::forward<Args>(args))...)); } @@ -329,98 +401,95 @@ public: { QtJniTypes::assertObjectType<Ret>(); constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>(); - LocalFrame<Args...> frame; + LocalFrame<QJniObject, Args...> frame; return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodName, signature.data(), - frame.convertToJni(std::forward<Args>(args))...)); + frame.convertToJni(std::forward<Args>(args))...)); } - template <typename T + template <typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > auto getField(const char *fieldName) const { - LocalFrame<T> frame(jniEnv()); + using T = typename QtJniTypes::Detail::CallerHandlesException<Type>::value_type; + LocalFrame<Type, T> frame(jniEnv()); + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature); + if constexpr (QtJniTypes::isObjectType<T>()) { - return frame.template convertFromJni<T>(getObjectField<T>(fieldName)); + return frame.makeResult(frame.template convertFromJni<T>(getObjectFieldImpl( + frame.jniEnv(), id)) + ); } else { T res{}; - constexpr auto signature = QtJniTypes::fieldSignature<T>(); - jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature); - if (id) { + if (id) getFieldForType<T>(frame.jniEnv(), res, object(), id); - if (frame.checkAndClearExceptions()) - res = {}; - } - return res; + return frame.makeResult(res); } } - template <typename T + template <typename Klass, typename T #ifndef Q_QDOC , QtJniTypes::IfValidFieldType<T> = true #endif > - static auto getStaticField(const char *className, const char *fieldName) + static auto getStaticField(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); - } + return getStaticField<T>(QtJniTypes::Traits<Klass>::className(), fieldName); } template <typename T #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true #endif > - static auto getStaticField(jclass clazz, const char *fieldName) + QJniObject getObjectField(const char *fieldName) const { - 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; - } + constexpr auto signature = QtJniTypes::fieldSignature<T>(); + return getObjectField(fieldName, signature); } - template <typename Klass, typename T + QJniObject getObjectField(const char *fieldName, const char *signature) const; + + template <typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - static auto getStaticField(const char *fieldName) + static auto getStaticField(const char *className, const char *fieldName) { - return getStaticField<T>(QtJniTypes::Traits<Klass>::className(), fieldName); + using T = typename QtJniTypes::Detail::CallerHandlesException<Type>::value_type; + LocalFrame<Type, T> frame; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); + return getStaticField<Type>(clazz, fieldName); } - template <typename T + template <typename Type #ifndef Q_QDOC - , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - QJniObject getObjectField(const char *fieldName) const + static auto getStaticField(jclass clazz, const char *fieldName) { + using T = typename QtJniTypes::Detail::CallerHandlesException<Type>::value_type; + LocalFrame<Type, T> frame; constexpr auto signature = QtJniTypes::fieldSignature<T>(); - return getObjectField(fieldName, signature); + jfieldID id = clazz ? getFieldID(frame.jniEnv(), clazz, fieldName, signature, true) + : nullptr; + if constexpr (QtJniTypes::isObjectType<T>()) { + return frame.makeResult(frame.template convertFromJni<T>(getStaticObjectFieldImpl( + frame.jniEnv(), clazz, id)) + ); + } else { + T res{}; + if (id) + getStaticFieldForType<T>(frame.jniEnv(), res, clazz, id); + return frame.makeResult(res); + } } - QJniObject getObjectField(const char *fieldName, const char *signature) const; - template <typename T #ifndef Q_QDOC , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true @@ -450,114 +519,122 @@ public: static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *signature); - template <typename T + template <typename Ret = void, typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - void setField(const char *fieldName, T value) + auto setField(const char *fieldName, Type value) { + // handle old code explicitly specifying a non-return type for Ret + using T = std::conditional_t<!std::is_void_v<Ret> && !QtJniTypes::Detail::callerHandlesException<Ret>, + Ret, Type>; + LocalFrame<Ret, T> frame(jniEnv()); constexpr auto signature = QtJniTypes::fieldSignature<T>(); jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature); - if (id) { + if (id) setFieldForType<T>(jniEnv(), object(), id, value); - QJniEnvironment::checkAndClearExceptions(jniEnv()); - } + return frame.makeResult(); } - template <typename T + template <typename Ret = void, typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - void setField(const char *fieldName, const char *signature, T value) - { - jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature); - if (id) { + auto setField(const char *fieldName, const char *signature, Type value) + { + // handle old code explicitly specifying a non-return type for Ret + using T = std::conditional_t<!std::is_void_v<Ret> && !QtJniTypes::Detail::callerHandlesException<Ret>, + Ret, Type>; + LocalFrame<Ret, T> frame(jniEnv()); + jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature); + if (id) setFieldForType<T>(jniEnv(), object(), id, value); - QJniEnvironment::checkAndClearExceptions(jniEnv()); - } + return frame.makeResult(); } - template <typename T + template <typename Ret = void, typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = 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(); + static auto setStaticField(const char *className, const char *fieldName, Type value) + { + // handle old code explicitly specifying a non-return type for Ret + using T = std::conditional_t<!std::is_void_v<Ret> && !QtJniTypes::Detail::callerHandlesException<Ret>, + Ret, Type>; + LocalFrame<Ret, T> frame; + if (jclass clazz = QJniObject::loadClass(className, frame.jniEnv())) { + constexpr auto signature = QtJniTypes::fieldSignature<q20::remove_cvref_t<T>>(); + jfieldID id = getCachedFieldID(frame.jniEnv(), clazz, className, fieldName, + signature, true); + if (id) + setStaticFieldForType<T>(frame.jniEnv(), clazz, id, value); + } + return frame.makeResult(); } - template <typename T + template <typename Ret = void, typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - static void setStaticField(const char *className, const char *fieldName, - 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); + static auto setStaticField(const char *className, const char *fieldName, + const char *signature, Type value) + { + // handle old code explicitly specifying a non-return type for Ret + using T = std::conditional_t<!std::is_void_v<Ret> && !QtJniTypes::Detail::callerHandlesException<Ret>, + Ret, Type>; + LocalFrame<Ret, T> frame; + if (jclass clazz = QJniObject::loadClass(className, frame.jniEnv())) { + jfieldID id = getCachedFieldID(frame.jniEnv(), clazz, className, fieldName, + signature, true); + if (id) + setStaticFieldForType<T>(frame.jniEnv(), clazz, id, value); } + return frame.makeResult(); } - template <typename T + template <typename Ret = void, typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - static void setStaticField(jclass clazz, const char *fieldName, - const char *signature, T value) + static auto setStaticField(jclass clazz, const char *fieldName, + const char *signature, Type value) { - JNIEnv *env = QJniEnvironment::getJniEnv(); - jfieldID id = getFieldID(env, clazz, fieldName, signature, true); + // handle old code explicitly specifying a non-return type for Ret + using T = std::conditional_t<!std::is_void_v<Ret> && !QtJniTypes::Detail::callerHandlesException<Ret>, + Ret, Type>; + LocalFrame<Ret, T> frame; + jfieldID id = getFieldID(frame.jniEnv(), clazz, fieldName, signature, true); - if (id) { - setStaticFieldForType<T>(env, clazz, id, value); - QJniEnvironment::checkAndClearExceptions(env); - } + if (id) + setStaticFieldForType<T>(frame.jniEnv(), clazz, id, value); + return frame.makeResult(); } - template <typename T + template <typename Ret = void, typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - static void setStaticField(jclass clazz, const char *fieldName, T value) + static auto setStaticField(jclass clazz, const char *fieldName, Type value) { - setStaticField(clazz, fieldName, QtJniTypes::fieldSignature<T>(), value); + return setStaticField<Ret, Type>(clazz, fieldName, + QtJniTypes::fieldSignature<q20::remove_cvref_t<Type>>(), + value); } - template <typename Klass, typename T + template <typename Klass, typename Ret = void, typename Type #ifndef Q_QDOC - , QtJniTypes::IfValidFieldType<T> = true + , QtJniTypes::IfValidFieldType<Type> = true #endif > - static void setStaticField(const char *fieldName, T value) + static auto setStaticField(const char *fieldName, Type value) { - setStaticField(QtJniTypes::Traits<Klass>::className(), fieldName, value); + return setStaticField<Ret, Type>(QtJniTypes::Traits<Klass>::className(), fieldName, value); } static QJniObject fromString(const QString &string); @@ -583,6 +660,7 @@ protected: private: static jclass loadClass(const QByteArray &className, JNIEnv *env); + static jclass loadClassKeepExceptions(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 @@ -620,12 +698,25 @@ private: template<typename T> static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, ...) { + if (!id) + return; + va_list args = {}; va_start(args, id); QtJniTypes::Caller<T>::callMethodForType(env, res, obj, id, args); va_end(args); } + jobject callObjectMethodImpl(jmethodID method, ...) const + { + va_list args; + va_start(args, method); + jobject res = method ? jniEnv()->CallObjectMethodV(javaObject(), method, args) + : nullptr; + va_end(args); + return res; + } + template<typename T> static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, jmethodID id, ...) @@ -652,6 +743,9 @@ private: template<typename T> static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id) { + if (!id) + return; + QtJniTypes::Caller<T>::getFieldForType(env, res, obj, id); } @@ -661,22 +755,42 @@ private: QtJniTypes::Caller<T>::getStaticFieldForType(env, res, clazz, id); } - template<typename T> - static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value) + template<typename Type> + static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, Type value) { + if (!id) + return; + + using T = q20::remove_cvref_t<Type>; if constexpr (QtJniTypes::isObjectType<T>()) { - LocalFrame<T> frame(env); + LocalFrame<T, T> frame(env); env->SetObjectField(obj, id, static_cast<jobject>(frame.convertToJni(value))); } else { - QtJniTypes::Caller<T>::setFieldForType(env, obj, id, value); + using ValueType = typename QtJniTypes::Detail::CallerHandlesException<T>::value_type; + QtJniTypes::Caller<ValueType>::setFieldForType(env, obj, id, value); } } - template<typename T> - static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value) + jobject getObjectFieldImpl(JNIEnv *env, jfieldID field) const + { + return field ? env->GetObjectField(javaObject(), field) : nullptr; + } + + static jobject getStaticObjectFieldImpl(JNIEnv *env, jclass clazz, jfieldID field) + { + return clazz && field ? env->GetStaticObjectField(clazz, field) + : nullptr; + } + + template<typename Type> + static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, Type value) { + if (!clazz || !id) + return; + + using T = q20::remove_cvref_t<Type>; if constexpr (QtJniTypes::isObjectType<T>()) { - LocalFrame<T> frame(env); + LocalFrame<T, T> frame(env); env->SetStaticObjectField(clazz, id, static_cast<jobject>(frame.convertToJni(value))); } else { QtJniTypes::Caller<T>::setStaticFieldForType(env, clazz, id, value); @@ -797,14 +911,14 @@ public: { return QJniObject::getStaticField<Class, T>(field); } - template <typename T + template <typename Ret = void, typename T #ifndef Q_QDOC , QtJniTypes::IfValidFieldType<T> = true #endif > - static void setStaticField(const char *field, T &&value) + static auto setStaticField(const char *field, T &&value) { - QJniObject::setStaticField<Class, T>(field, std::forward<T>(value)); + return QJniObject::setStaticField<Class, Ret, T>(field, std::forward<T>(value)); } // keep only these overloads, the rest is made private @@ -827,14 +941,14 @@ public: return m_object.getField<T>(fieldName); } - template <typename T + template <typename Ret = void, typename T #ifndef Q_QDOC , QtJniTypes::IfValidFieldType<T> = true #endif > - void setField(const char *fieldName, T &&value) + auto setField(const char *fieldName, T &&value) { - m_object.setField(fieldName, std::forward<T>(value)); + return m_object.setField<Ret>(fieldName, std::forward<T>(value)); } QByteArray className() const { @@ -911,6 +1025,37 @@ struct Traits<QString> } }; +template <typename T> +struct Traits<T, std::enable_if_t<QtJniTypes::Detail::callerHandlesException<T>>> +{ + static constexpr auto className() + { + return Traits<typename T::value_type>::className(); + } + + static constexpr auto signature() + { + return Traits<typename T::value_type>::signature(); + } +}; + +} + +template <typename ReturnType, typename ...Args> +template <typename T> +auto QtJniTypes::Detail::LocalFrame<ReturnType, Args...>::convertFromJni(jobject object) +{ + // If the caller wants to handle exceptions through a std::expected-like + // type, then we cannot turn the jobject into a QJniObject, as a + // std::expected<jobject, jthrowable> cannot be constructed from a QJniObject. + // The caller will have to take care of this themselves, by asking for a + // std::expected<QJniObject, ...>, or (typically) using a declared JNI class + // or implicitly supported Qt type (QString or array type). + if constexpr (callerHandlesException<ReturnType> && + std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>) + return static_cast<T>(object); + else + return convertFromJni<T>(object ? QJniObject::fromLocalRef(object) : QJniObject()); } QT_END_NAMESPACE diff --git a/src/corelib/tools/qflatmap_p.h b/src/corelib/tools/qflatmap_p.h index 585eab33ae8..5a827fb4148 100644 --- a/src/corelib/tools/qflatmap_p.h +++ b/src/corelib/tools/qflatmap_p.h @@ -15,7 +15,9 @@ // We mean it. // +#include <QtCore/qcontainertools_impl.h> #include "qlist.h" +#include <QtCore/qtclasshelpermacros.h> #include "private/qglobal_p.h" #include <algorithm> @@ -44,8 +46,7 @@ QT_BEGIN_NAMESPACE namespace Qt { -struct OrderedUniqueRange_t {}; -constexpr OrderedUniqueRange_t OrderedUniqueRange = {}; +QT_DEFINE_TAG(OrderedUniqueRange); } // namespace Qt @@ -70,35 +71,11 @@ public: } }; -namespace qflatmap { -namespace detail { -template <class T> -class QFlatMapMockPointer -{ - T ref; -public: - QFlatMapMockPointer(T r) - : ref(r) - { - } - - T *operator->() - { - return &ref; - } -}; -} // namespace detail -} // namespace qflatmap - template<class Key, class T, class Compare = std::less<Key>, class KeyContainer = QList<Key>, class MappedContainer = QList<T>> class QFlatMap : private QFlatMapValueCompare<Key, T, Compare> { static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers."); - - template<class U> - using mock_pointer = qflatmap::detail::QFlatMapMockPointer<U>; - public: using key_type = Key; using mapped_type = T; @@ -121,7 +98,7 @@ public: using difference_type = ptrdiff_t; using value_type = std::pair<const Key, T>; using reference = std::pair<const Key &, T &>; - using pointer = mock_pointer<reference>; + using pointer = QtPrivate::ArrowProxy<reference>; using iterator_category = std::random_access_iterator_tag; iterator() = default; @@ -253,7 +230,7 @@ public: using difference_type = ptrdiff_t; using value_type = std::pair<const Key, const T>; using reference = std::pair<const Key &, const T &>; - using pointer = mock_pointer<reference>; + using pointer = QtPrivate::ArrowProxy<reference>; using iterator_category = std::random_access_iterator_tag; const_iterator() = default; diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index b60cad34a8d..2b2f2a27fcd 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -2201,7 +2201,7 @@ QString QFont::toString() const fontDescription += comma + QString::number(sortedFeatures.size()); for (const auto &[tag, value] : std::as_const(sortedFeatures).asKeyValueRange()) - fontDescription += comma + tag.toString() + u'=' + QString::number(value); + fontDescription += comma + QLatin1StringView{tag.toString()} + u'=' + QString::number(value); return fontDescription; } diff --git a/src/plugins/platforms/windows/qwindowswindowclassdescription.cpp b/src/plugins/platforms/windows/qwindowswindowclassdescription.cpp index abd6f02fb0b..6962d28e7d0 100644 --- a/src/plugins/platforms/windows/qwindowswindowclassdescription.cpp +++ b/src/plugins/platforms/windows/qwindowswindowclassdescription.cpp @@ -41,43 +41,69 @@ QString QWindowsWindowClassDescription::classNameSuffix(Qt::WindowFlags type, un return suffix; } -QWindowsWindowClassDescription QWindowsWindowClassDescription::fromName(QString name, WNDPROC procedure) +bool QWindowsWindowClassDescription::computeHasIcon(Qt::WindowFlags flags, Qt::WindowFlags type) { - return { std::move(name), procedure }; + bool hasIcon = true; + + switch (type) { + case Qt::Tool: + case Qt::ToolTip: + case Qt::Popup: + hasIcon = false; + break; + case Qt::Dialog: + if (!(flags & Qt::WindowSystemMenuHint)) + hasIcon = false; // QTBUG-2027, dialogs without system menu. + break; + } + + return hasIcon; } -QWindowsWindowClassDescription QWindowsWindowClassDescription::fromWindow(const QWindow *window, WNDPROC procedure) +unsigned int QWindowsWindowClassDescription::computeWindowStyles(Qt::WindowFlags flags, Qt::WindowFlags type, WindowStyleOptions options) { - Q_ASSERT(window); + unsigned int style = CS_DBLCLKS; - QWindowsWindowClassDescription description; - description.procedure = procedure; - - const Qt::WindowFlags flags = window->flags(); - const Qt::WindowFlags type = flags & Qt::WindowType_Mask; - // Determine style and icon. - description.style = CS_DBLCLKS; - description.hasIcon = true; // The following will not set CS_OWNDC for any widget window, even if it contains a // QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage. - if (window->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC)) - description.style |= CS_OWNDC; - if (!(flags & Qt::NoDropShadowWindowHint) - && (type == Qt::Popup || window->property("_q_windowsDropShadow").toBool())) { - description.style |= CS_DROPSHADOW; - } + if (options.testFlag(WindowStyleOption::GLSurface) || (flags & Qt::MSWindowsOwnDC)) + style |= CS_OWNDC; + if (!(flags & Qt::NoDropShadowWindowHint) && (type == Qt::Popup || options.testFlag(WindowStyleOption::DropShadow))) + style |= CS_DROPSHADOW; + switch (type) { case Qt::Tool: case Qt::ToolTip: case Qt::Popup: - description.style |= CS_SAVEBITS; // Save/restore background - description.hasIcon = false; - break; - case Qt::Dialog: - if (!(flags & Qt::WindowSystemMenuHint)) - description.hasIcon = false; // QTBUG-2027, dialogs without system menu. + style |= CS_SAVEBITS; // Save/restore background break; } + + return style; +} + +QWindowsWindowClassDescription QWindowsWindowClassDescription::fromName(QString name, WNDPROC procedure) +{ + return { std::move(name), procedure }; +} + +QWindowsWindowClassDescription QWindowsWindowClassDescription::fromWindow(const QWindow *window, WNDPROC procedure) +{ + Q_ASSERT(window); + + const Qt::WindowFlags flags = window->flags(); + const Qt::WindowFlags type = flags & Qt::WindowType_Mask; + + WindowStyleOptions options = WindowStyleOption::None; + if (window->surfaceType() == QSurface::OpenGLSurface) + options |= WindowStyleOption::GLSurface; + if (window->property("_q_windowsDropShadow").toBool()) + options |= WindowStyleOption::DropShadow; + + QWindowsWindowClassDescription description; + description.procedure = procedure; + description.style = computeWindowStyles(flags, type, options); + description.hasIcon = computeHasIcon(flags, type); description.name = "QWindow"_L1 + classNameSuffix(type, description.style, description.hasIcon); return description; diff --git a/src/plugins/platforms/windows/qwindowswindowclassdescription.h b/src/plugins/platforms/windows/qwindowswindowclassdescription.h index 692bf18e618..f0019f8f3c2 100644 --- a/src/plugins/platforms/windows/qwindowswindowclassdescription.h +++ b/src/plugins/platforms/windows/qwindowswindowclassdescription.h @@ -14,6 +14,14 @@ class QWindow; struct QWindowsWindowClassDescription { + enum class WindowStyleOption + { + None = 0x00, + GLSurface = 0x01, + DropShadow = 0x02 + }; + Q_DECLARE_FLAGS(WindowStyleOptions, WindowStyleOption) + static QWindowsWindowClassDescription fromName(QString name, WNDPROC procedure); static QWindowsWindowClassDescription fromWindow(const QWindow *window, WNDPROC procedure); @@ -26,10 +34,14 @@ struct QWindowsWindowClassDescription private: static QString classNameSuffix(Qt::WindowFlags type, unsigned int style, bool hasIcon); + static bool computeHasIcon(Qt::WindowFlags flags, Qt::WindowFlags type); + static unsigned int computeWindowStyles(Qt::WindowFlags flags, Qt::WindowFlags type, WindowStyleOptions options); friend QDebug operator<<(QDebug dbg, const QWindowsWindowClassDescription &description); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QWindowsWindowClassDescription::WindowStyleOptions) + QT_END_NAMESPACE #endif // QWINDOWSWINDOWCLASSDESCRIPTION_H diff --git a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt index 98509dc0e5f..1d320b49c86 100644 --- a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt @@ -14,8 +14,16 @@ endif() qt_internal_add_test(tst_qjniobject SOURCES tst_qjniobject.cpp + LIBRARIES + Qt::CorePrivate ) +# if we can, enable C++23 so that we can use std::expected in the test +# otherwise the test will use our own tl::expected copy from qexpected_p.h +if ("${CMAKE_CXX_COMPILE_FEATURES}" MATCHES "cxx_std_23") + set_property(TARGET tst_qjniobject PROPERTY CXX_STANDARD 23) +endif() + if(ANDROID) set_property(TARGET tst_qjniobject APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/testdata diff --git a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java index e0765fcb7e0..bdec173ca97 100644 --- a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java +++ b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java @@ -137,6 +137,10 @@ public class QtJniObjectTestClass public Throwable throwableMethod() { return staticThrowableMethod(); } // -------------------------------------------------------------------------------------------- + public static void staticThrowingMethod() throws Throwable { throw new Throwable(A_STRING_OBJECT); } + public void throwingMethod() throws Throwable { throw new Throwable(A_STRING_OBJECT); } + + // -------------------------------------------------------------------------------------------- public static Object[] staticObjectArrayMethod() { Object[] array = { new Object(), new Object(), new Object() }; return array; } public Object[] objectArrayMethod() { return staticObjectArrayMethod(); } diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index c06b500778b..215b3bf3b78 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -8,12 +8,20 @@ #include <QtCore/QJniObject> #include <QTest> +#if defined(__cpp_lib_expected) +# include <expected> +#else +# include <QtCore/private/qexpected_p.h> +#endif + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; static constexpr const char testClassName[] = "org/qtproject/qt/android/testdatapackage/QtJniObjectTestClass"; Q_DECLARE_JNI_CLASS(QtJniObjectTestClass, testClassName) +Q_DECLARE_JNI_CLASS(NoSuchClass, "no/such/Class") + using TestClass = QtJniTypes::QtJniObjectTestClass; static const jbyte A_BYTE_VALUE = 127; @@ -125,6 +133,24 @@ private slots: void callback(); void callStaticOverloadResolution(); + void implicitExceptionHandling_construct(); + void implicitExceptionHandling_callMethod(); + void implicitExceptionHandling_callStaticMethod(); + void implicitExceptionHandling_getField(); + void implicitExceptionHandling_setField(); + void implicitExceptionHandling_getStaticField(); + void implicitExceptionHandling_setStaticField(); + + void constructWithException(); + void callMethodWithException(); + void callMethodWithMonadic(); + void callMethodWithTryCatch(); + void callStaticMethodWithException(); + void getFieldWithException(); + void setFieldWithException(); + void getStaticFieldWithException(); + void setStaticFieldWithException(); + void cleanupTestCase(); }; @@ -2303,8 +2329,547 @@ void tst_QJniObject::callStaticOverloadResolution() QCOMPARE(result, value); } -QT_END_NAMESPACE +void tst_QJniObject::implicitExceptionHandling_construct() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + + // Constructor, including named constructor + { + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject testObject("NoSuchClass"); + QVERIFY(!env.checkAndClearExceptions()); + } + { + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + QJniObject testObject = QJniObject::construct<TestClass>(u"NoSuchConstructor"_s); + QVERIFY(!env.checkAndClearExceptions()); + } +} + +void tst_QJniObject::implicitExceptionHandling_callMethod() +{ + QJniEnvironment env; + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + const QRegularExpression throwingMethod("java.lang.Throwable: .*"); + + QJniObject testObject = QJniObject::construct<TestClass>(); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + testObject.callMethod<void>("noSuchMethod1"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + testObject.callMethod<void>("noSuchMethod2", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, throwingMethod); + testObject.callMethod<void>("throwingMethod"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + (void)testObject.callObjectMethod<jstring>("noSuchMethod"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + testObject.callObjectMethod("noSuchMethod", "()V"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_callStaticMethod() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + const QRegularExpression throwingMethod("java.lang.Throwable: .*"); + + const jclass classId = env.findClass<TestClass>(); + QVERIFY(classId != nullptr); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + TestClass::callStaticMethod<void>("noSuchStaticMethod"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + QJniObject::callStaticMethod<void>(QtJniTypes::Traits<TestClass>::className(), + "noSuchStaticMethod2", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject::callStaticMethod<void>("noSuchClass", "noSuchStaticMethod2", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, throwingMethod); + jmethodID methodId = env.findStaticMethod<void>(classId, "staticThrowingMethod"); + QVERIFY(methodId != nullptr); + QJniObject::callStaticMethod<void>(classId, methodId); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject::callStaticObjectMethod("noSuchClass", "noSuchStaticMethod", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + QJniObject::callStaticObjectMethod(classId, "noSuchStaticMethod", "()V"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_getField() +{ + QJniEnvironment env; + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + QJniObject testObject = QJniObject::construct<TestClass>(); + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)testObject.getField<int>("noSuchField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)testObject.getObjectField<jobject>("noSuchObjectField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)testObject.getObjectField("noSuchObjectField2", "Ljava/lang/Object;"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_setField() +{ + QJniEnvironment env; + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + QJniObject testObject = QJniObject::construct<TestClass>(); + QTest::ignoreMessage(QtWarningMsg, invalidField); + testObject.setField("noSuchField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + testObject.setField("BOOLEAN_VAR", "I", 123); + QVERIFY(!env.checkAndClearExceptions()); + + // make sure that code specifying deducible type explicitly still works + static_assert(std::is_same_v<decltype(testObject.setField<jboolean>("BOOLEAN_VAR", true)), + void>); +} + +void tst_QJniObject::implicitExceptionHandling_getStaticField() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + const jclass classId = env.findClass<TestClass>(); + QVERIFY(classId != nullptr); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)TestClass::getStaticField<int>("noSuchStaticField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)QJniObject::getStaticField<int>(classId, "noSuchStaticField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + (void)QJniObject::getStaticObjectField("noSuchClass", "noSuchStaticField", "I"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)QJniObject::getStaticObjectField(classId, "S_BOOLEAN_VAR", "I"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_setStaticField() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + const jclass classId = env.findClass<TestClass>(); + QVERIFY(classId != nullptr); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + TestClass::setStaticField("noSuchStaticField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + QJniObject::setStaticField(classId, "noSuchStaticField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject::setStaticField("noSuchClass", "noSuchStaticField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + QJniObject::setStaticField(QtJniTypes::Traits<TestClass>::className(), + "S_BOOLEAN_VAR", "I", 123); + QVERIFY(!env.checkAndClearExceptions()); + + // make sure that code specifying deducible type explicitly still works + static_assert(std::is_same_v<decltype(TestClass::setStaticField<jboolean>("S_BOOLEAN_VAR", true)), + void>); +} + +#if __cpp_lib_expected +template <typename T> +using QJniReturnValue = std::expected<T, jthrowable>; +using BadAccessException = std::bad_expected_access<jthrowable>; +// even with __cpp_lib_expected >= 202211L, monadic functions seem to be rather +// broken or not reliably available +#define EXPECTED_HAS_MONADIC false +#elif TL_EXPECTED_VERSION_MAJOR +#define EXPECTED_HAS_MONADIC true +template <typename T> +using QJniReturnValue = tl::expected<T, jthrowable>; +using BadAccessException = tl::bad_expected_access<jthrowable>; +#endif + +static_assert(QtJniTypes::Traits<QJniReturnValue<int>>::signature() == + QtJniTypes::Traits<int>::signature()); +static_assert(QtJniTypes::Traits<QJniReturnValue<QString>>::signature() == + QtJniTypes::Traits<QString>::signature()); + + +void tst_QJniObject::constructWithException() +{ +#if __cpp_lib_expected + qInfo() << "Testing explicit exception handling with std::expected" << __cpp_lib_expected; +#elif defined(TL_EXPECTED_VERSION_MAJOR) + qInfo() << "Testing explicit exception handling with tl::expected"; +#else + qInfo() << "Testing explicit exception handling with QJniReturnValue"; +#endif + + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + { + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject invalid = QJniObject::construct<QtJniTypes::NoSuchClass>(); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); + + { + // can only handle exceptions when using the named constructor + auto result = QJniObject::construct<QJniReturnValue<TestClass>>(); + QVERIFY(result); + result = QJniObject::construct<QJniReturnValue<TestClass>>(u"123"_s); + QVERIFY(!result); + QVERIFY(result.error()); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); + + { + const auto result = QJniObject::construct<QJniReturnValue<QtJniTypes::NoSuchClass>>(); + QVERIFY(!result); + QVERIFY(result.error()); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); + + { + // no way to prevent implicit exception here + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QtJniTypes::NoSuchClass noSuchClass; + QVERIFY(!noSuchClass.isValid()); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); +} + +void tst_QJniObject::callMethodWithException() +{ + TestClass testObject; + { + auto result = testObject.callMethod<QJniReturnValue<void>>("voidMethod"); + QVERIFY(result); + result = testObject.callMethod<QJniReturnValue<void>>("voidMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = testObject.callMethod<QJniReturnValue<jint>>("intMethod"); + QVERIFY(result); + QCOMPARE(result.value(), A_INT_VALUE); + result = testObject.callMethod<QJniReturnValue<jint>>("intMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + QCOMPARE(result.value_or(456), 456); + } + + { + auto result = testObject.callMethod<QJniReturnValue<jstring>>("stringMethod"); + QVERIFY(result); + QVERIFY(result.value()); + result = testObject.callMethod<QJniReturnValue<jstring>>("stringMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = testObject.callMethod<QJniReturnValue<QString>>("stringMethod"); + QVERIFY(result); + QVERIFY(!result.value().isEmpty()); + result = testObject.callMethod<QJniReturnValue<QString>>("stringMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + QCOMPARE(result.value_or(u"Default"_s), u"Default"_s); + } + + { + QJniArray<jboolean> newArray(QList<jboolean>{true, false, false}); + auto result = testObject.callMethod<QJniReturnValue<QJniArray<jboolean>>>("reverseBooleanArray", newArray); + // this shorthand cannot work with e.g. std::expected + // result = testObject.callMethod<QJniReturnValue<jboolean[]>>("reverseBooleanArray", newArray); + QVERIFY(result); + QCOMPARE(result.value().toContainer(), (QList<jboolean>{false, false, true})); + result = testObject.callMethod<QJniReturnValue<QJniArray<jboolean>>>("reverseBooleanArray", 123); + QVERIFY(!result); + QVERIFY(result.error()); + } + + // throwing method - QJniObject cleans the exception and prints qWarning + { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"java.lang.Throwable: "_s + A_STRING_OBJECT() + u".*"_s)); + testObject.callMethod<void>("throwingMethod"); + + auto result = testObject.callMethod<QJniReturnValue<void>>("throwingMethod"); + QVERIFY(!result); + const QStringList stackTrace = QJniEnvironment::stackTrace(result.error()); + QCOMPARE_GE(stackTrace.size(), 1); + QCOMPARE(stackTrace.at(0), u"java.lang.Throwable: "_s + A_STRING_OBJECT()); + } +} + +void tst_QJniObject::callMethodWithMonadic() +{ +#if !EXPECTED_HAS_MONADIC + QSKIP("Used version of std::expected does not have monadic functions"); +#else + enum Monadic { + AndThen, + OrElse, + Transform, + }; + + TestClass testObject; + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<void>>("voidMethod") + .and_then([&flow]{ + flow << AndThen; + return QJniReturnValue<void>(); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<void>(); + }) + .transform([&flow](){ + flow << Transform; + return 42; + }); + QCOMPARE(flow, (QList<Monadic>{AndThen, Transform})); + QVERIFY(result); + QCOMPARE(*result, 42); + } + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<void>>("voidMethod", 123) + .and_then([&flow]{ + flow << AndThen; + return QJniReturnValue<void>(); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<void>(typename QJniReturnValue<void>::unexpected_type(error)); + }) + .transform([&flow](){ + flow << Transform; + return 42; + }); + QCOMPARE(flow, (QList<Monadic>{OrElse})); + QVERIFY(!result); + } + + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<jobject>>("objectMethod") + .and_then([&flow](auto &&obj){ + flow << AndThen; + return QJniReturnValue<jobject>(obj); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<jobject>(typename QJniReturnValue<void>::unexpected_type(error)); + }) + .transform([&flow](auto &&obj){ + flow << Transform; + return QJniObject(obj).template getField<int>("INT_FIELD"); + }) + .and_then([&flow](auto value){ + flow << AndThen; + return QJniReturnValue<int>(value * 2); + }); + QCOMPARE(flow, (QList<Monadic>{AndThen, Transform, AndThen})); + QVERIFY(result); + QCOMPARE(*result, 246); + } + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<jobject>>("objectMethod", 123) + .and_then([&flow](const QJniObject &obj){ + flow << AndThen; + return QJniReturnValue<jobject>(obj.object()); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<jobject>(typename QJniReturnValue<jobject>::unexpected_type(error)); + }) + .transform([&flow](const QJniObject &obj){ + flow << Transform; + return obj.getField<int>("INT_FIELD"); + }); + QCOMPARE(flow, (QList<Monadic>{OrElse})); + QVERIFY(!result); + } +#endif +} + +void tst_QJniObject::callMethodWithTryCatch() +{ + TestClass testObject; + + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + + try { + const auto result = testObject.callMethod<QJniReturnValue<QJniObject>>("objectMethod", 123); + result.value().getField<int>("INT_FIELD"); + } + catch (BadAccessException &e) { + qWarning().noquote() << QJniEnvironment::stackTrace(e.error()).join('\n'); + } +} + +void tst_QJniObject::callStaticMethodWithException() +{ + { + auto result = TestClass::callStaticMethod<QJniReturnValue<int>>("staticIntMethod"); + QVERIFY(result); + QCOMPARE(*result, A_INT_VALUE); + result = TestClass::callStaticMethod<QJniReturnValue<int>>("staticIntMethod", 123); + QVERIFY(!result && result.error()); + } + + { + auto result = TestClass::callStaticMethod<QJniReturnValue<QString>>("staticStringMethod"); + QVERIFY(result); + QCOMPARE(*result, A_STRING_OBJECT()); + result = TestClass::callStaticMethod<QJniReturnValue<QString>>("staticStringMethod", 123); + QVERIFY(!result && result.error()); + } + + // throwing method + { + const auto result = TestClass::callStaticMethod<QJniReturnValue<void>>("staticThrowingMethod"); + QVERIFY(!result); + QStringList stackTrace = QJniEnvironment::stackTrace(result.error()); + QCOMPARE_GE(stackTrace.size(), 1); + QCOMPARE(stackTrace.at(0), u"java.lang.Throwable: "_s + A_STRING_OBJECT()); + } +} + +void tst_QJniObject::getFieldWithException() +{ + TestClass testObject; + { + auto result = testObject.getField<QJniReturnValue<jboolean>>("BOOL_FIELD"); + QVERIFY(result); + result = testObject.getField<QJniReturnValue<jboolean>>("INVALID_BOOL"); + QVERIFY(!result && result.error()); + } + + { + auto result = testObject.getField<QJniReturnValue<QString>>("STRING_OBJECT_VAR"); + QVERIFY(result); + result = testObject.getField<QJniReturnValue<QString>>("INVALID_STRING"); + QVERIFY(!result && result.error()); + } +} + +void tst_QJniObject::setFieldWithException() +{ + TestClass testObject; + { + auto result = testObject.setField<QJniReturnValue<jboolean>>("BOOL_FIELD", true); + QVERIFY(result); + result = testObject.setField<QJniReturnValue<jboolean>>("SET_INVALID_BOOL", true); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = testObject.setField<QJniReturnValue<QString>>("STRING_OBJECT_VAR", u"test"_s); + QVERIFY(result); + result = testObject.setField<QJniReturnValue<QString>>("SET_INVALID_STRING", u"test"_s); + QVERIFY(!result); + QVERIFY(result.error()); + } +} + +void tst_QJniObject::getStaticFieldWithException() +{ + { + auto result = TestClass::getStaticField<QJniReturnValue<jshort>>("S_SHORT_VAR"); + QVERIFY(result); + result = TestClass::getStaticField<QJniReturnValue<jshort>>("S_INVALID_SHORT"); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = TestClass::getStaticField<QJniReturnValue<QString>>("S_STRING_OBJECT_VAR"); + QVERIFY(result); + result = TestClass::getStaticField<QJniReturnValue<QString>>("S_INVALID_STRING"); + QVERIFY(!result); + QVERIFY(result.error()); + } +} + +void tst_QJniObject::setStaticFieldWithException() +{ + { + auto result = TestClass::setStaticField<QJniReturnValue<jboolean>>("S_BOOLEAN_VAR", true); + QVERIFY(result); + result = TestClass::setStaticField<QJniReturnValue<jboolean>>("SET_S_INVALID_BOOL", true); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = TestClass::setStaticField<QJniReturnValue<QString>>("S_STRING_OBJECT_VAR", u"test"_s); + QVERIFY(result); + result = TestClass::setStaticField<QJniReturnValue<QString>>("SET_S_INVALID_STRING", u"test"_s); + QVERIFY(!result); + QVERIFY(result.error()); + } +} QTEST_MAIN(tst_QJniObject) +QT_END_NAMESPACE + #include "tst_qjniobject.moc" |
