diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 6b2d327f889..af7f1a9bba5 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -16,6 +16,47 @@ class QJniObjectPrivate; class Q_CORE_EXPORT QJniObject { + template + struct LocalFrame { + QJniEnvironment env; + bool hasFrame = false; + ~LocalFrame() { + if (hasFrame) + env->PopLocalFrame(nullptr); + } + template + auto newLocalRef(QJniObject &&object) { + if (!hasFrame) { + if (env->PushLocalFrame(sizeof...(Args)) < 0) + return T{}; // JVM is out of memory, avoid making matters worse + hasFrame = true; + } + return static_cast(env->NewLocalRef(object.template object())); + } + JNIEnv *jniEnv() const { return env.jniEnv(); } + bool checkAndClearExceptions() { return env.checkAndClearExceptions(); } + + template + auto convertToJni(T &&value) + { + using Type = q20::remove_cvref_t; + if constexpr (std::is_same_v) { + return newLocalRef(QJniObject::fromString(value)); + } else { + return static_cast(value); + } + } + template + auto convertFromJni(QJniObject &&object) + { + using Type = q20::remove_cvref_t; + if constexpr (std::is_same_v) { + return object.toString(); + } else { + return object; + } + } + }; public: QJniObject(); explicit QJniObject(const char *className); @@ -46,9 +87,10 @@ public: template static inline QJniObject construct(Args &&...args) { + LocalFrame frame; return QJniObject(QtJniTypes::Traits::className().data(), QtJniTypes::constructorSignature().data(), - std::forward(args)...); + frame.convertToJni(std::forward(args))...); } jobject object() const; @@ -68,19 +110,22 @@ public: > auto callMethod(const char *methodName, const char *signature, Args &&...args) const { + LocalFrame frame; if constexpr (QtJniTypes::isObjectType()) { - return callObjectMethod(methodName, signature, std::forward(args)...); + return frame.template convertFromJni(callObjectMethod(methodName, signature, + frame.convertToJni(std::forward(args))...)); } else { - QJniEnvironment env; - jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature); + jmethodID id = getCachedMethodID(frame.jniEnv(), methodName, signature); if (id) { if constexpr (std::is_same_v) { - callVoidMethodV(env.jniEnv(), id, std::forward(args)...); - env.checkAndClearExceptions(); + callVoidMethodV(frame.jniEnv(), id, + frame.convertToJni(std::forward(args))...); + frame.checkAndClearExceptions(); } else { Ret res{}; - callMethodForType(env.jniEnv(), res, object(), id, std::forward(args)...); - if (env.checkAndClearExceptions()) + callMethodForType(frame.jniEnv(), res, object(), id, + frame.convertToJni(std::forward(args))...); + if (frame.checkAndClearExceptions()) res = {}; return res; } @@ -139,18 +184,21 @@ public: > static auto callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args) { + LocalFrame frame; if constexpr (QtJniTypes::isObjectType()) { - return callStaticObjectMethod(clazz, methodId, std::forward(args)...); + return frame.template convertFromJni(callStaticObjectMethod(clazz, methodId, + frame.convertToJni(std::forward(args))...)); } else { - QJniEnvironment env; if (clazz && methodId) { if constexpr (std::is_same_v) { - callStaticMethodForVoid(env.jniEnv(), clazz, methodId, std::forward(args)...); - env.checkAndClearExceptions(); + callStaticMethodForVoid(frame.jniEnv(), clazz, methodId, + frame.convertToJni(std::forward(args))...); + frame.checkAndClearExceptions(); } else { Ret res{}; - callStaticMethodForType(env.jniEnv(), res, clazz, methodId, std::forward(args)...); - if (env.checkAndClearExceptions()) + callStaticMethodForType(frame.jniEnv(), res, clazz, methodId, + frame.convertToJni(std::forward(args))...); + if (frame.checkAndClearExceptions()) res = {}; return res; } @@ -216,7 +264,9 @@ public: { QtJniTypes::assertObjectType(); constexpr auto signature = QtJniTypes::methodSignature(); - return callStaticObjectMethod(className, methodName, signature.data(), std::forward(args)...); + LocalFrame frame; + return frame.template convertFromJni(callStaticObjectMethod(className, methodName, signature.data(), + frame.convertToJni(std::forward(args))...)); } template (); constexpr auto signature = QtJniTypes::methodSignature(); - return callStaticObjectMethod(clazz, methodName, signature.data(), std::forward(args)...); + LocalFrame frame; + return frame.template convertFromJni(callStaticObjectMethod(clazz, methodName, signature.data(), + frame.convertToJni(std::forward(args))...)); } template auto getField(const char *fieldName) const { + LocalFrame frame; if constexpr (QtJniTypes::isObjectType()) { - return getObjectField(fieldName); + return frame.template convertFromJni(getObjectField(fieldName)); } else { - QJniEnvironment env; T res{}; constexpr auto signature = QtJniTypes::fieldSignature(); - jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature); + jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature); if (id) { - getFieldForType(env.jniEnv(), res, object(), id); - if (env.checkAndClearExceptions()) + getFieldForType(frame.jniEnv(), res, object(), id); + if (frame.checkAndClearExceptions()) res = {}; } return res; @@ -261,27 +313,14 @@ public: > static auto getStaticField(const char *className, const char *fieldName) { + LocalFrame frame; if constexpr (QtJniTypes::isObjectType()) { - return getStaticObjectField(className, fieldName); + return frame.template convertFromJni(getStaticObjectField(className, fieldName)); } else { - QJniEnvironment env; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); - T res{}; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); if (!clazz) - return res; - - constexpr auto signature = QtJniTypes::fieldSignature(); - jfieldID id = getCachedFieldID(env.jniEnv(), clazz, - QJniObject::toBinaryEncClassName(className), - fieldName, - signature, true); - if (!id) - return res; - - getStaticFieldForType(env.jniEnv(), res, clazz, id); - if (env.checkAndClearExceptions()) - res = {}; - return res; + return T{}; + return getStaticField(clazz, fieldName); } } @@ -292,16 +331,16 @@ public: > static auto getStaticField(jclass clazz, const char *fieldName) { + LocalFrame frame; if constexpr (QtJniTypes::isObjectType()) { - return getStaticObjectField(clazz, fieldName); + return frame.template convertFromJni(getStaticObjectField(clazz, fieldName)); } else { - QJniEnvironment env; T res{}; constexpr auto signature = QtJniTypes::fieldSignature(); - jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); + jfieldID id = getFieldID(frame.jniEnv(), clazz, fieldName, signature, true); if (id) { - getStaticFieldForType(env.jniEnv(), res, clazz, id); - if (env.checkAndClearExceptions()) + getStaticFieldForType(frame.jniEnv(), res, clazz, id); + if (frame.checkAndClearExceptions()) res = {}; } return res; @@ -398,19 +437,19 @@ public: > static void setStaticField(const char *className, const char *fieldName, T value) { - QJniEnvironment env; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); + LocalFrame frame; + jclass clazz = QJniObject::loadClass(className, frame.jniEnv()); if (!clazz) return; constexpr auto signature = QtJniTypes::fieldSignature(); - jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, + jfieldID id = getCachedFieldID(frame.jniEnv(), clazz, className, fieldName, signature, true); if (!id) return; - setStaticFieldForType(env.jniEnv(), clazz, id, value); - env.checkAndClearExceptions(); + setStaticFieldForType(frame.jniEnv(), clazz, id, value); + frame.checkAndClearExceptions(); } template static void setStaticField(jclass clazz, const char *fieldName, T value) { - QJniEnvironment env; - constexpr auto signature = QtJniTypes::fieldSignature(); - jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true); - if (id) { - setStaticFieldForType(env.jniEnv(), clazz, id, value); - env.checkAndClearExceptions(); - } + setStaticField(clazz, fieldName, QtJniTypes::fieldSignature(), value); } template frame; if constexpr (std::is_same_v) env->SetBooleanField(obj, id, value); else if constexpr (likeIntegerType) @@ -678,8 +712,8 @@ private: env->SetFloatField(obj, id, value); else if constexpr (std::is_same_v) env->SetDoubleField(obj, id, value); - else if constexpr (std::is_convertible_v) - env->SetObjectField(obj, id, value); + else if constexpr (QtJniTypes::isObjectType()) + env->SetObjectField(obj, id, static_cast(frame.convertToJni(value))); else QtJniTypes::staticAssertTypeMismatch(); } @@ -688,6 +722,7 @@ private: static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value) { + LocalFrame frame; if constexpr (std::is_same_v) env->SetStaticBooleanField(clazz, id, value); else if constexpr (likeIntegerType) @@ -704,8 +739,8 @@ private: env->SetStaticFloatField(clazz, id, value); else if constexpr (std::is_same_v) env->SetStaticDoubleField(clazz, id, value); - else if constexpr (std::is_convertible_v) - env->SetStaticObjectField(clazz, id, value); + else if constexpr (QtJniTypes::isObjectType()) + env->SetStaticObjectField(clazz, id, static_cast(frame.convertToJni(value))); else QtJniTypes::staticAssertTypeMismatch(); } diff --git a/src/corelib/kernel/qjnitypes_impl.h b/src/corelib/kernel/qjnitypes_impl.h index 511b59de4f8..66f9fee6fa6 100644 --- a/src/corelib/kernel/qjnitypes_impl.h +++ b/src/corelib/kernel/qjnitypes_impl.h @@ -235,6 +235,8 @@ struct Traits { return CTString("V"); } else if constexpr (std::is_enum_v) { return Traits>::signature(); + } else if constexpr (std::is_same_v) { + return CTString("Ljava/lang/String;"); } // else: return void -> not implemented } diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index cc3ebc658eb..3d8c15d0b0f 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -187,8 +187,10 @@ void tst_QJniObject::ctor() void tst_QJniObject::callMethodTest() { { - QJniObject jString1 = QJniObject::fromString(QLatin1String("Hello, Java")); - QJniObject jString2 = QJniObject::fromString(QLatin1String("hELLO, jAVA")); + const QString qString1 = u"Hello, Java"_s; + const QString qString2 = u"hELLO, jAVA"_s; + QJniObject jString1 = QJniObject::fromString(qString1); + QJniObject jString2 = QJniObject::fromString(qString2); QVERIFY(jString1 != jString2); const jboolean isEmpty = jString1.callMethod("isEmpty"); @@ -201,6 +203,10 @@ void tst_QJniObject::callMethodTest() ret = jString1.callMethod("compareToIgnoreCase", jString2.object()); QVERIFY(0 == ret); + + // as of Qt 6.7, we can pass QString directly + ret = jString1.callMethod("compareToIgnoreCase", qString2); + QVERIFY(0 == ret); } { @@ -219,6 +225,13 @@ void tst_QJniObject::callMethodTest() QJniObject subString = jString.callMethod("substring", 0, 4); QCOMPARE(subString.toString(), qString.mid(0, 4)); + + // and as of Qt 6.7, we can return and take QString directly + QCOMPARE(jString.callMethod("substring", 0, 4), qString.mid(0, 4)); + + QCOMPARE(jString.callMethod("substring", 0, 7) + .callMethod("toUpperCase") + .callMethod("concat", u"C++"_s), u"HELLO, C++"_s); } } @@ -346,6 +359,11 @@ void tst_QJniObject::callStaticObjectMethod() QVERIFY(returnValue.isValid()); QCOMPARE(returnValue.toString(), string); + // from 6.7 we can pass QString directly, both as parameters and return type + QString result = QJniObject::callStaticMethod("format", + string, + jobjectArray(0)); + QCOMPARE(result, string); } void tst_QJniObject::callStaticObjectMethodById() @@ -1053,11 +1071,16 @@ void tst_QJniObject::setObjectField() QJniObject obj(testClassName); QVERIFY(obj.isValid()); - QJniObject testValue = QJniObject::fromString(QStringLiteral("Hello")); + const QString qString = u"Hello"_s; + QJniObject testValue = QJniObject::fromString(qString); obj.setField("STRING_OBJECT_VAR", testValue.object()); QJniObject res = obj.getObjectField("STRING_OBJECT_VAR"); QCOMPARE(res.toString(), testValue.toString()); + + // as of Qt 6.7, we can set and get strings directly + obj.setField("STRING_OBJECT_VAR", qString); + QCOMPARE(obj.getField("STRING_OBJECT_VAR"), qString); } template @@ -1127,11 +1150,17 @@ void tst_QJniObject::setStaticBooleanField() void tst_QJniObject::setStaticObjectField() { - QJniObject testValue = QJniObject::fromString(QStringLiteral("Hello")); + const QString qString = u"Hello"_s; + QJniObject testValue = QJniObject::fromString(qString); QJniObject::setStaticField(testClassName, "S_STRING_OBJECT_VAR", testValue.object()); QJniObject res = QJniObject::getStaticObjectField(testClassName, "S_STRING_OBJECT_VAR"); QCOMPARE(res.toString(), testValue.toString()); + + // as of Qt 6.7, we can set and get strings directly + using namespace QtJniTypes; + QJniObject::setStaticField("S_STRING_OBJECT_VAR", qString); + QCOMPARE((QJniObject::getStaticField("S_STRING_OBJECT_VAR")), qString); } void tst_QJniObject::templateApiCheck()