summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel')
-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/kernel/qpermissions.cpp6
6 files changed, 437 insertions, 240 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/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp
index bbcea8338ca..6767917e176 100644
--- a/src/corelib/kernel/qpermissions.cpp
+++ b/src/corelib/kernel/qpermissions.cpp
@@ -124,7 +124,11 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
The relevant permission names are described in the documentation
for each permission type.
- \sa {Qt Creator: Editing Manifest Files}
+ \note When using this API, the \c{<!-- %%INSERT_PERMISSIONS -->} tag must be present in
+ the AndroidManifest.xml. For further information on the use of this tag,
+ see \l {Qt Permissions and Features}
+
+ \sa {Qt Creator: Editing Manifest Files}.
\section1 Available Permissions