JNI: don't access null jobject

When Java calls a native function that expects a QString with a null
String object, then we get a nullptr jstring. We must not try to convert
that to a QString.

Add the necessary checks at the call-site of the toQString helper, and
an assert to the helper itself. Add test case.

Change-Id: If5c55c702bdb3f89ac45fdf3912af2ac295f5e5c
Reviewed-by: Volker Krause <vkrause@kde.org>
(cherry picked from commit db6a9834d2b109e81cc73ef3c64d0c420237e9dc)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Volker Hilsheimer 2025-01-03 16:18:09 +01:00 committed by Qt Cherry-pick Bot
parent 6dc19b55fc
commit 11b9657b08
5 changed files with 33 additions and 6 deletions

View File

@ -819,12 +819,14 @@ public:
container.reserve(sz);
if constexpr (std::is_same_v<typename ContainerType::value_type, QString>) {
for (auto element : *this) {
if constexpr (std::is_same_v<decltype(element), QString>)
if constexpr (std::is_same_v<decltype(element), QString>) {
container.emplace_back(element);
else if constexpr (std::is_same_v<decltype(element), jstring>)
container.emplace_back(QtJniTypes::Detail::toQString(element, env));
else
} else if constexpr (std::is_same_v<decltype(element), jstring>) {
container.emplace_back(element ? QtJniTypes::Detail::toQString(element, env)
: QString{});
} else {
container.emplace_back(QJniObject(element).toString());
}
}
} else if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>) {
for (auto element : *this)

View File

@ -163,7 +163,7 @@ struct JNITypeForArgImpl<QString>
static QString fromVarArg(Type t)
{
return QtJniTypes::Detail::toQString(t, QJniEnvironment::getJniEnv());
return t ? QtJniTypes::Detail::toQString(t, QJniEnvironment::getJniEnv()) : QString();
}
};
@ -190,7 +190,7 @@ public:
static QList<T> fromVarArg(Type t)
{
return QJniArray<ElementType>(t).toContainer();
return t ? QJniArray<ElementType>(t).toContainer() : QList<T>{};
}
};

View File

@ -24,6 +24,7 @@ static inline jstring fromQString(const QString &string, JNIEnv *env)
static inline QString toQString(jstring string, JNIEnv *env)
{
Q_ASSERT(string);
const jsize length = env->GetStringLength(string);
QString res(length, Qt::Uninitialized);
env->GetStringRegion(string, 0, length, reinterpret_cast<jchar *>(res.data()));

View File

@ -307,6 +307,7 @@ public class QtJniObjectTestClass
native public int callbackWithRawArray(Object[] value);
native public int callbackWithQList(double[] value);
native public int callbackWithStringList(String[] value);
native public int callbackWithNull(String str);
public int callMeBackWithObject(QtJniObjectTestClass that)
{
@ -358,6 +359,10 @@ public class QtJniObjectTestClass
{
return callbackWithStringList(value);
}
public int callMeBackWithNull()
{
return callbackWithNull(null);
}
public Object callMethodThrowsException() throws Exception {
throw new Exception();

View File

@ -1959,6 +1959,7 @@ enum class CallbackParameterType
RawArray,
QList,
QStringList,
Null,
};
static std::optional<TestClass> calledWithObject;
@ -2047,6 +2048,14 @@ static int callbackWithStringList(JNIEnv *, jobject, const QStringList &value)
}
Q_DECLARE_JNI_NATIVE_METHOD(callbackWithStringList)
static std::optional<QString> calledWithNull;
static int callbackWithNull(JNIEnv *, jobject, const QString &str)
{
calledWithNull.emplace(str);
return int(CallbackParameterType::Null);
}
Q_DECLARE_JNI_NATIVE_METHOD(callbackWithNull)
void tst_QJniObject::callback_data()
{
QTest::addColumn<CallbackParameterType>("parameterType");
@ -2062,6 +2071,7 @@ void tst_QJniObject::callback_data()
QTest::addRow("RawArray") << CallbackParameterType::RawArray;
QTest::addRow("QList") << CallbackParameterType::QList;
QTest::addRow("QStringList") << CallbackParameterType::QStringList;
QTest::addRow("Null") << CallbackParameterType::Null;
}
void tst_QJniObject::callback()
@ -2171,6 +2181,15 @@ void tst_QJniObject::callback()
QCOMPARE(calledWithStringList.value(), strings);
break;
}
case CallbackParameterType::Null: {
QVERIFY(TestClass::registerNativeMethods({
Q_JNI_NATIVE_METHOD(callbackWithNull)
}));
result = testObject.callMethod<int>("callMeBackWithNull");
QVERIFY(calledWithNull);
QCOMPARE(calledWithNull.value(), QString());
break;
}
}
QCOMPARE(result, int(parameterType));
}