Source code
Revision control
Copy as Markdown
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifndef QJNIOBJECT_H
#define QJNIOBJECT_H
#include <QtCore/qsharedpointer.h>
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qxptype_traits.h>
QT_BEGIN_NAMESPACE
class QJniObjectPrivate;
class QJniObject;
namespace QtJniTypes
{
namespace Detail
{
// any type with an "jobject object()" member function stores a global reference
template <typename T, typename = void> struct StoresGlobalRefTest : std::false_type {};
template <typename T>
struct StoresGlobalRefTest<T, std::void_t<decltype(std::declval<T>().object())>>
: std::is_same<decltype(std::declval<T>().object()), jobject>
{};
// 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>
constexpr bool callerHandlesException = CallerHandlesException<ReturnType>::value;
template <typename ...Args>
struct LocalFrame
{
mutable JNIEnv *env;
bool hasFrame = false;
explicit LocalFrame(JNIEnv *env = nullptr) noexcept
: env(env)
{
}
~LocalFrame()
{
if (hasFrame)
env->PopLocalFrame(nullptr);
}
bool ensureFrame()
{
if (!hasFrame)
hasFrame = jniEnv()->PushLocalFrame(sizeof...(Args)) == 0;
return hasFrame;
}
JNIEnv *jniEnv() const
{
if (!env)
env = QJniEnvironment::getJniEnv();
return env;
}
template <typename T>
auto convertToJni(T &&value)
{
using Type = q20::remove_cvref_t<T>;
using ResultType = decltype(QtJniTypes::Traits<Type>::convertToJni(jniEnv(),
std::declval<T&&>()));
if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>,
std::remove_pointer_t<ResultType>>) {
// Make sure the local frame is engaged if we create a jobject, unless
// we know that the value stores a global reference that it returns.
if constexpr (!qxp::is_detected_v<StoresGlobalRefTest, Type>) {
if (!ensureFrame())
return ResultType{};
}
}
return QtJniTypes::Traits<Type>::convertToJni(jniEnv(), std::forward<T>(value));
}
template <typename T>
auto convertFromJni(QJniObject &&object)
{
using Type = q20::remove_cvref_t<T>;
return QtJniTypes::Traits<Type>::convertFromJni(std::move(object));
}
};
template <typename Ret, typename ...Args>
struct LocalFrameWithReturn : LocalFrame<Args...>
{
using ReturnType = Ret;
using LocalFrame<Args...>::LocalFrame;
template <typename T>
auto convertFromJni(QJniObject &&object)
{
return LocalFrame<Args...>::template convertFromJni<T>(std::move(object));
}
template <typename T>
auto convertFromJni(jobject object);
bool checkAndClearExceptions() const
{
if constexpr (callerHandlesException<ReturnType>)
return false;
else
return QJniEnvironment::checkAndClearExceptions(this->jniEnv());
}
auto makeResult()
{
if constexpr (callerHandlesException<ReturnType>) {
JNIEnv *env = this->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 Ret, typename ...Args> using LocalFrame
= QtJniTypes::Detail::LocalFrameWithReturn<Ret, Args...>;
public:
QJniObject();
explicit QJniObject(const char *className);
explicit QJniObject(const char *className, const char *signature, ...);
template<typename ...Args
#ifndef Q_QDOC
, std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr
#endif
>
explicit QJniObject(const char *className, Args &&...args)
: QJniObject(QtJniTypes::Detail::LocalFrame<Args...>{}, className, std::forward<Args>(args)...)
{
}
private:
template<typename ...Args>
explicit QJniObject(QtJniTypes::Detail::LocalFrame<Args...> localFrame, const char *className, Args &&...args)
: QJniObject(className, QtJniTypes::constructorSignature<Args...>().data(),
localFrame.convertToJni(std::forward<Args>(args))...)
{
}
public:
explicit QJniObject(jclass clazz);
explicit QJniObject(jclass clazz, const char *signature, ...);
template<typename ...Args
#ifndef Q_QDOC
, std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr
#endif
>
explicit QJniObject(jclass clazz, Args &&...args)
: QJniObject(clazz, QtJniTypes::constructorSignature<Args...>().data(),
std::forward<Args>(args)...)
{}
QJniObject(jobject globalRef);
QJniObject(const QJniObject &other) noexcept = default;
QJniObject(QJniObject &&other) noexcept = default;
QJniObject &operator=(const QJniObject &other) noexcept = default;
QJniObject &operator=(QJniObject &&other) noexcept = default;
~QJniObject();
void swap(QJniObject &other) noexcept { d.swap(other.d); }
template<typename Class, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<Class, Args...> = true
#endif
>
static inline auto construct(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;
template <typename T> T object() const
{
QtJniTypes::assertObjectType<T>();
return static_cast<T>(javaObject());
}
jclass objectClass() const;
QByteArray className() const;
template <typename ReturnType = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<ReturnType> = true
#endif
>
auto callMethod(const char *methodName, const char *signature, Args &&...args) const
{
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.makeResult(frame.template convertFromJni<Ret>(callObjectMethodImpl(
id, frame.convertToJni(std::forward<Args>(args))...))
);
} else {
if (id) {
if constexpr (std::is_same_v<Ret, void>) {
callVoidMethodV(frame.jniEnv(), id,
frame.convertToJni(std::forward<Args>(args))...);
} else {
Ret res{};
callMethodForType<Ret>(frame.jniEnv(), res, object(), id,
frame.convertToJni(std::forward<Args>(args))...);
return frame.makeResult(res);
}
}
if constexpr (!std::is_same_v<Ret, void>)
return frame.makeResult(Ret{});
else
return frame.makeResult();
}
}
template <typename ReturnType = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<ReturnType, Args...> = true
#endif
>
auto callMethod(const char *methodName, Args &&...args) const
{
constexpr auto signature = QtJniTypes::methodSignature<ReturnType, Args...>();
return callMethod<ReturnType>(methodName, signature.data(), std::forward<Args>(args)...);
}
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
#endif
>
QJniObject callObjectMethod(const char *methodName, Args &&...args) const
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
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;
template <typename Ret = void, typename ...Args>
static auto callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
{
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)
{
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 ReturnType = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<ReturnType> = true
#endif
>
static auto callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
{
using Ret = typename QtJniTypes::Detail::CallerHandlesException<ReturnType>::value_type;
LocalFrame<ReturnType, Args...> frame;
if constexpr (QtJniTypes::isObjectType<Ret>()) {
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))...);
} else {
Ret res{};
callStaticMethodForType<Ret>(frame.jniEnv(), res, clazz, methodId,
frame.convertToJni(std::forward<Args>(args))...);
return frame.makeResult(res);
}
}
if constexpr (!std::is_same_v<Ret, void>)
return frame.makeResult(Ret{});
else
return frame.makeResult();
}
}
template <typename ReturnType = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<ReturnType, Args...> = true
#endif
>
static auto callStaticMethod(const char *className, const char *methodName, Args &&...args)
{
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<ReturnType>(clazz, id, std::forward<Args>(args)...);
}
template <typename ReturnType = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<ReturnType, Args...> = true
#endif
>
static auto callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
{
constexpr auto signature = QtJniTypes::methodSignature<ReturnType, Args...>();
return callStaticMethod<ReturnType>(clazz, methodName, signature.data(), std::forward<Args>(args)...);
}
template <typename Klass, typename ReturnType = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<ReturnType, Args...> = true
#endif
>
static auto callStaticMethod(const char *methodName, Args &&...args)
{
LocalFrame<ReturnType, Args...> frame;
const jclass clazz = QJniObject::loadClass(QtJniTypes::Traits<Klass>::className().data(),
frame.jniEnv());
const jmethodID id = clazz ? getMethodID(frame.jniEnv(), clazz, methodName,
QtJniTypes::methodSignature<ReturnType, Args...>().data(), true)
: 0;
return callStaticMethod<ReturnType>(clazz, id, std::forward<Args>(args)...);
}
static QJniObject callStaticObjectMethod(const char *className, const char *methodName,
const char *signature, ...);
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName,
const char *signature, ...);
static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...);
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
#endif
>
static QJniObject callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
LocalFrame<QJniObject, Args...> frame;
return frame.template convertFromJni<Ret>(callStaticObjectMethod(className, methodName, signature.data(),
frame.convertToJni(std::forward<Args>(args))...));
}
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
#endif
>
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
LocalFrame<QJniObject, Args...> frame;
return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodName, signature.data(),
frame.convertToJni(std::forward<Args>(args))...));
}
template <typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
auto getField(const char *fieldName) const
{
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.makeResult(frame.template convertFromJni<T>(getObjectFieldImpl(
frame.jniEnv(), id))
);
} else {
T res{};
if (id)
getFieldForType<T>(frame.jniEnv(), res, object(), id);
return frame.makeResult(res);
}
}
template <typename Klass, typename T
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<T> = true
#endif
>
static auto getStaticField(const char *fieldName)
{
return getStaticField<T>(QtJniTypes::Traits<Klass>::className(), fieldName);
}
template <typename T
#ifndef Q_QDOC
, std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
#endif
>
QJniObject getObjectField(const char *fieldName) const
{
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getObjectField(fieldName, signature);
}
QJniObject getObjectField(const char *fieldName, const char *signature) const;
template <typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
static auto getStaticField(const char *className, const char *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 Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
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>();
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);
}
}
template <typename T
#ifndef Q_QDOC
, std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
#endif
>
static QJniObject getStaticObjectField(const char *className, const char *fieldName)
{
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(className, fieldName, signature);
}
static QJniObject getStaticObjectField(const char *className,
const char *fieldName,
const char *signature);
template <typename T
#ifndef Q_QDOC
, std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
#endif
>
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName)
{
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(clazz, fieldName, signature);
}
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName,
const char *signature);
template <typename Ret = void, typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
auto setField(const char *fieldName, Type value)
{
// handle old code that explicitly specifies the field type (i.e. Ret != void)
using T = std::conditional_t<std::is_void_v<Ret> || QtJniTypes::Detail::callerHandlesException<Ret>,
Type, Ret>;
LocalFrame<Ret, T> frame(jniEnv());
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature);
if (id)
setFieldForType<T>(jniEnv(), object(), id, value);
return frame.makeResult();
}
template <typename Ret = void, typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
auto setField(const char *fieldName, const char *signature, Type value)
{
// handle old code that explicitly specifies the field type (i.e. Ret != void)
using T = std::conditional_t<std::is_void_v<Ret> || QtJniTypes::Detail::callerHandlesException<Ret>,
Type, Ret>;
LocalFrame<Ret, T> frame(jniEnv());
jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature);
if (id)
setFieldForType<T>(jniEnv(), object(), id, value);
return frame.makeResult();
}
template <typename Ret = void, typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
static auto setStaticField(const char *className, const char *fieldName, Type value)
{
// handle old code that explicitly specifies the field type (i.e. Ret != void)
using T = std::conditional_t<std::is_void_v<Ret> || QtJniTypes::Detail::callerHandlesException<Ret>,
Type, Ret>;
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 Ret = void, typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
static auto setStaticField(const char *className, const char *fieldName,
const char *signature, Type value)
{
// handle old code that explicitly specifies the field type (i.e. Ret != void)
using T = std::conditional_t<std::is_void_v<Ret> || QtJniTypes::Detail::callerHandlesException<Ret>,
Type, Ret>;
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 Ret = void, typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
static auto setStaticField(jclass clazz, const char *fieldName,
const char *signature, Type value)
{
// handle old code that explicitly specifies the field type (i.e. Ret != void)
using T = std::conditional_t<std::is_void_v<Ret> || QtJniTypes::Detail::callerHandlesException<Ret>,
Type, Ret>;
LocalFrame<Ret, T> frame;
jfieldID id = getFieldID(frame.jniEnv(), clazz, fieldName, signature, true);
if (id)
setStaticFieldForType<T>(frame.jniEnv(), clazz, id, value);
return frame.makeResult();
}
template <typename Ret = void, typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
static auto setStaticField(jclass clazz, const char *fieldName, Type value)
{
return setStaticField<Ret, Type>(clazz, fieldName,
QtJniTypes::fieldSignature<q20::remove_cvref_t<Type>>(),
value);
}
template <typename Klass, typename Ret = void, typename Type
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<Type> = true
#endif
>
static auto setStaticField(const char *fieldName, Type value)
{
return setStaticField<Ret, Type>(QtJniTypes::Traits<Klass>::className(), fieldName, value);
}
static QJniObject fromString(const QString &string);
QString toString() const;
static bool isClassAvailable(const char *className);
bool isValid() const;
// This function takes ownership of the jobject and releases the local ref. before returning.
static QJniObject fromLocalRef(jobject lref);
template <typename T,
std::enable_if_t<std::is_convertible_v<T, jobject>, bool> = true>
QJniObject &operator=(T obj)
{
assign(static_cast<T>(obj));
return *this;
}
protected:
QJniObject(Qt::Initialization) {}
JNIEnv *jniEnv() const noexcept;
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
static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded);
static QByteArray toBinaryEncClassName(const QByteArray &className);
void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const;
#endif
static jfieldID getCachedFieldID(JNIEnv *env, jclass clazz, const QByteArray &className,
const char *name, const char *signature,
bool isStatic = false);
jfieldID getCachedFieldID(JNIEnv *env, const char *name, const char *signature,
bool isStatic = false) const;
static jmethodID getCachedMethodID(JNIEnv *env, jclass clazz, const QByteArray &className,
const char *name, const char *signature,
bool isStatic = false);
jmethodID getCachedMethodID(JNIEnv *env, const char *name, const char *signature,
bool isStatic = false) const;
static jfieldID getFieldID(JNIEnv *env, jclass clazz, const char *name,
const char *signature, bool isStatic = false);
static jmethodID getMethodID(JNIEnv *env, jclass clazz, const char *name,
const char *signature, bool isStatic = false);
void callVoidMethodV(JNIEnv *env, jmethodID id, ...) const;
bool isSameObject(jobject obj) const;
bool isSameObject(const QJniObject &other) const;
void assign(jobject obj);
jobject javaObject() const;
friend bool operator==(const QJniObject &, const QJniObject &);
friend bool operator!=(const QJniObject&, const QJniObject&);
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, ...)
{
if (!clazz || !id)
return;
va_list args = {};
va_start(args, id);
QtJniTypes::Caller<T>::callStaticMethodForType(env, res, clazz, id, args);
va_end(args);
}
static void callStaticMethodForVoid(JNIEnv *env, jclass clazz, jmethodID id, ...)
{
if (!clazz || !id)
return;
va_list args;
va_start(args, id);
env->CallStaticVoidMethodV(clazz, id, args);
va_end(args);
}
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);
}
template<typename T>
static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id)
{
QtJniTypes::Caller<T>::getStaticFieldForType(env, res, clazz, id);
}
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, T> frame(env);
env->SetObjectField(obj, id, static_cast<jobject>(frame.convertToJni(value)));
} else {
using ValueType = typename QtJniTypes::Detail::CallerHandlesException<T>::value_type;
QtJniTypes::Caller<ValueType>::setFieldForType(env, obj, id, 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, T> frame(env);
env->SetStaticObjectField(clazz, id, static_cast<jobject>(frame.convertToJni(value)));
} else {
QtJniTypes::Caller<T>::setStaticFieldForType(env, clazz, id, value);
}
}
friend QJniObjectPrivate;
QSharedPointer<QJniObjectPrivate> d;
};
inline bool operator==(const QJniObject &obj1, const QJniObject &obj2)
{
return obj1.isSameObject(obj2);
}
inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
{
return !obj1.isSameObject(obj2);
}
namespace QtJniTypes {
struct JObjectBase
{
operator QJniObject() const { return m_object; }
bool isValid() const { return m_object.isValid(); }
jclass objectClass() const { return m_object.objectClass(); }
QString toString() const { return m_object.toString(); }
template <typename T = jobject>
T object() const {
return m_object.object<T>();
}
protected:
JObjectBase() = default;
JObjectBase(const JObjectBase &) = default;
JObjectBase(JObjectBase &&) = default;
JObjectBase &operator=(const JObjectBase &) = default;
JObjectBase &operator=(JObjectBase &&) = default;
~JObjectBase() = default;
Q_IMPLICIT JObjectBase(jobject object) : m_object(object) {}
Q_IMPLICIT JObjectBase(const QJniObject &object) : m_object(object) {}
Q_IMPLICIT JObjectBase(QJniObject &&object) noexcept : m_object(std::move(object)) {}
QJniObject m_object;
};
template<typename Type>
class JObject : public JObjectBase
{
public:
using Class = Type;
JObject()
: JObjectBase{QJniObject(QtJniTypes::Traits<Class>::className())}
{}
Q_IMPLICIT JObject(jobject object) : JObjectBase(object) {}
Q_IMPLICIT JObject(const QJniObject &object) : JObjectBase(object) {}
Q_IMPLICIT JObject(QJniObject &&object) noexcept : JObjectBase(std::move(object)) {}
// base class SMFs are protected, make them public:
JObject(const JObject &other) = default;
JObject(JObject &&other) noexcept = default;
JObject &operator=(const JObject &other) = default;
JObject &operator=(JObject &&other) noexcept = default;
~JObject() = default;
template<typename Arg, typename ...Args
, std::enable_if_t<!std::is_same_v<q20::remove_cvref_t<Arg>, JObject>, bool> = true
, IfValidSignatureTypes<Arg, Args...> = true
>
explicit JObject(Arg && arg, Args &&...args)
: JObjectBase{QJniObject(QtJniTypes::Traits<Class>::className(),
std::forward<Arg>(arg), std::forward<Args>(args)...)}
{}
// named constructors avoid ambiguities
static JObject fromJObject(jobject object)
{
return JObject(object);
}
template <typename ...Args>
static JObject construct(Args &&...args)
{
return JObject(std::forward<Args>(args)...);
}
static JObject fromLocalRef(jobject lref)
{
return JObject(QJniObject::fromLocalRef(lref));
}
#ifdef Q_QDOC // from JObjectBase, which we don't document
bool isValid() const;
jclass objectClass() const;
QString toString() const;
template <typename T = jobject> object() const;
#endif
static bool registerNativeMethods(std::initializer_list<JNINativeMethod> methods)
{
QJniEnvironment env;
return env.registerNativeMethods<Class>(methods);
}
// public API forwarding to QJniObject, with the implicit Class template parameter
template <typename Ret = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
#endif
>
static auto callStaticMethod(const char *name, Args &&...args)
{
return QJniObject::callStaticMethod<Class, Ret, Args...>(name,
std::forward<Args>(args)...);
}
template <typename T
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<T> = true
#endif
>
static auto getStaticField(const char *field)
{
return QJniObject::getStaticField<Class, T>(field);
}
template <typename Ret = void, typename T
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<T> = true
#endif
>
static auto setStaticField(const char *field, T &&value)
{
return QJniObject::setStaticField<Class, Ret, T>(field, std::forward<T>(value));
}
// keep only these overloads, the rest is made private
template <typename Ret = void, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
#endif
>
auto callMethod(const char *method, Args &&...args) const
{
return m_object.callMethod<Ret>(method, std::forward<Args>(args)...);
}
template <typename T
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<T> = true
#endif
>
auto getField(const char *fieldName) const
{
return m_object.getField<T>(fieldName);
}
template <typename Ret = void, typename T
#ifndef Q_QDOC
, QtJniTypes::IfValidFieldType<T> = true
#endif
>
auto setField(const char *fieldName, T &&value)
{
return m_object.setField<Ret>(fieldName, std::forward<T>(value));
}
QByteArray className() const {
return QtJniTypes::Traits<Class>::className().data();
}
static bool isClassAvailable()
{
return QJniObject::isClassAvailable(QtJniTypes::Traits<Class>::className().data());
}
private:
friend bool comparesEqual(const JObject &lhs, const JObject &rhs)
{ return lhs.m_object == rhs.m_object; }
Q_DECLARE_EQUALITY_COMPARABLE_NON_NOEXCEPT(JObject);
};
template <typename T> struct Traits<JObject<T>> {
static constexpr auto signature() { return Traits<T>::signature(); }
static constexpr auto className() { return Traits<T>::className(); }
static auto convertToJni(JNIEnv *, const JObject<T> &value)
{
return value.object();
}
static auto convertFromJni(QJniObject &&object)
{
return JObject<T>(std::move(object));
}
};
template<>
struct Traits<QJniObject>
{
static constexpr auto className()
{
return CTString("java/lang/Object");
}
static constexpr auto signature()
{
return CTString("Ljava/lang/Object;");
}
static auto convertToJni(JNIEnv *, const QJniObject &value)
{
return value.object();
}
static auto convertFromJni(QJniObject &&object)
{
return std::move(object);
}
};
template<>
struct Traits<QString>
{
static constexpr auto className()
{
return CTString("java/lang/String");
}
static constexpr auto signature()
{
return CTString("Ljava/lang/String;");
}
static auto convertToJni(JNIEnv *env, const QString &value)
{
return QtJniTypes::Detail::fromQString(value, env);
}
static auto convertFromJni(QJniObject &&object)
{
return object.toString();
}
};
template<>
struct Traits<QStringView>
{
static constexpr auto className()
{
return CTString("java/lang/String");
}
static constexpr auto signature()
{
return CTString("Ljava/lang/String;");
}
static auto convertToJni(JNIEnv *env, QStringView value)
{
return QtJniTypes::Detail::fromQString(value, env);
}
};
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::LocalFrameWithReturn<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
#endif
#endif // QJNIOBJECT_H