summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qjniarray.h4
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp8
-rw-r--r--src/corelib/kernel/qjnienvironment.h1
-rw-r--r--src/corelib/kernel/qjniobject.cpp135
-rw-r--r--src/corelib/kernel/qjniobject.h523
-rw-r--r--src/corelib/tools/qflatmap_p.h33
-rw-r--r--src/gui/text/qfont.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowswindowclassdescription.cpp74
-rw-r--r--src/plugins/platforms/windows/qwindowswindowclassdescription.h12
-rw-r--r--tests/auto/corelib/kernel/qjniobject/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java4
-rw-r--r--tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp567
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"