File 0001-QtQml-Invalidate-fallback-lookups-after-each-call-fr.patch of Package qt6-declarative
From 9c6b2b78e9076f1c2676aa0c41573db9ca480654 Mon Sep 17 00:00:00 2001
From: Ulf Hermann <ulf.hermann@qt.io>
Date: Tue, 2 Dec 2025 17:42:30 +0100
Subject: [PATCH] QtQml: Invalidate fallback lookups after each call from AOT
code
Fallback property lookups are created for completely dynamic
metaobjects. Anything about them may change between any two calls.
Pick-to: 6.8 6.5
Fixes: QTBUG-142331
Change-Id: Ib732c37a6f27ab8105bea0eeae000af7eb9c36d7
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
(cherry picked from commit 9af6d2d6d0046b3c8369e15eb4791957cdc7ab7b)
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
---
src/qml/jsruntime/qv4lookup_p.h | 4 ++
src/qml/qml/qqml.cpp | 13 ++++--
.../qml/qmlcppcodegen/data/CMakeLists.txt | 2 +
.../qml/qmlcppcodegen/data/propertyMap.qml | 6 +++
.../auto/qml/qmlcppcodegen/data/propertymap.h | 40 +++++++++++++++++
.../qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 43 +++++++++++++++++++
6 files changed, 104 insertions(+), 4 deletions(-)
create mode 100644 tests/auto/qml/qmlcppcodegen/data/propertyMap.qml
create mode 100644 tests/auto/qml/qmlcppcodegen/data/propertymap.h
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index 083c3ec2df8f8..ef36bf67c509c 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -159,6 +159,10 @@ struct Q_QML_EXPORT Lookup {
const QQmlPropertyData *propertyData;
} qobjectMethodLookup;
struct {
+ // NB: None of this is actually cache-able. The metaobject may change at any time.
+ // We invalidate this data every time the lookup is invoked and thereby force a
+ // re-initialization next time.
+
quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc
quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
int coreIndex;
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 4e3b4fcf1ee11..3f0d9e332b1cd 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -1386,16 +1386,16 @@ struct FallbackPropertyQmlData
static FallbackPropertyQmlData findFallbackPropertyQmlData(QV4::Lookup *lookup, QObject *object)
{
+ // We've just initialized the lookup. So everything must be fine here.
+
QQmlData *qmlData = QQmlData::get(object);
- if (qmlData && qmlData->isQueuedForDeletion)
- return {qmlData, nullptr, PropertyResult::Deleted};
+ Q_ASSERT(!qmlData || !qmlData->isQueuedForDeletion);
Q_ASSERT(!QQmlData::wasDeleted(object));
const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(lookup->qobjectFallbackLookup.metaObject - 1);
- if (!metaObject || metaObject != object->metaObject())
- return {qmlData, nullptr, PropertyResult::NeedsInit};
+ Q_ASSERT(metaObject == object->metaObject());
return {qmlData, metaObject, PropertyResult::OK};
}
@@ -2585,6 +2585,7 @@ bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target)
break;
case QV4::Lookup::Call::ContextGetterScopeObjectPropertyFallback:
result = loadFallbackProperty(lookup, qmlScopeObject, target, this);
+ lookup->call = QV4::Lookup::Call::ContextGetterGeneric;
break;
default:
return false;
@@ -2616,6 +2617,7 @@ bool AOTCompiledContext::writeBackScopeObjectPropertyLookup(uint index, void *so
break;
case QV4::Lookup::Call::ContextGetterScopeObjectPropertyFallback:
result = writeBackFallbackProperty(lookup, qmlScopeObject, source);
+ lookup->call = QV4::Lookup::Call::ContextGetterGeneric;
break;
default:
return false;
@@ -2816,6 +2818,7 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
result = lookup->asVariant
? loadFallbackAsVariant(lookup, object, target, this)
: loadFallbackProperty(lookup, object, target, this);
+ lookup->call = QV4::Lookup::Call::GetterGeneric;
break;
default:
return false;
@@ -2850,6 +2853,7 @@ bool AOTCompiledContext::writeBackObjectLookup(uint index, QObject *object, void
result = lookup->asVariant
? writeBackFallbackAsVariant(lookup, object, source)
: writeBackFallbackProperty(lookup, object, source);
+ lookup->call = QV4::Lookup::Call::GetterGeneric;
break;
default:
return false;
@@ -3010,6 +3014,7 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu
result = lookup->asVariant
? storeFallbackAsVariant(engine->handle(), lookup, object, value)
: storeFallbackProperty(lookup, object, value);
+ lookup->call = QV4::Lookup::Call::SetterGeneric;
break;
default:
return false;
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 79e908c9674f3..67cdefa30d938 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -26,6 +26,7 @@ set(cpp_sources
multiforeign.h
objectwithmethod.h
person.cpp person.h
+ propertymap.h
qmlusing.h
recursiveObject.h
refuseWrite.h
@@ -282,6 +283,7 @@ set(qml_files
popContextAfterRet.qml
prefixedMetaType.qml
pressAndHoldButton.qml
+ propertyMap.qml
qmlUsing.qml
qtbug113150.qml
qtfont.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/propertyMap.qml b/tests/auto/qml/qmlcppcodegen/data/propertyMap.qml
new file mode 100644
index 0000000000000..c00f3972e8ad6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/propertyMap.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import TestTypes
+
+WithPropertyMap {
+ objectName: map.foo
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/propertymap.h b/tests/auto/qml/qmlcppcodegen/data/propertymap.h
new file mode 100644
index 0000000000000..64d84c5c09721
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/propertymap.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef PROPERTYMAP_H
+#define PROPERTYMAP_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlpropertymap.h>
+
+class WithPropertyMap : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QQmlPropertyMap *map READ map NOTIFY mapChanged)
+public:
+ WithPropertyMap(QObject *parent = nullptr)
+ : QObject(parent)
+ , m_map(new QQmlPropertyMap(this))
+ {
+ }
+
+ QQmlPropertyMap *map() const { return m_map; }
+
+ void setProperties(const QVariantHash &properties)
+ {
+ delete m_map;
+ m_map = new QQmlPropertyMap(this);
+ m_map->insert(properties);
+ emit mapChanged();
+ }
+
+signals:
+ void mapChanged();
+
+private:
+ QQmlPropertyMap *m_map = nullptr;
+};
+
+#endif // PROPERTYMAP_H
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 70c50b457a958..a90e2a605057c 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -10,6 +10,7 @@
#include <data/getOptionalLookup.h>
#include <data/listprovider.h>
#include <data/objectwithmethod.h>
+#include <data/propertymap.h>
#include <data/qmlusing.h>
#include <data/refuseWrite.h>
#include <data/resettable.h>
@@ -237,6 +238,7 @@ private slots:
void parentProperty();
void popContextAfterRet();
void prefixedType();
+ void propertyMap();
void propertyOfParent();
void qmlUsing();
void qtfont();
@@ -4908,6 +4910,47 @@ void tst_QmlCppCodegen::prefixedType()
QCOMPARE(o->property("countH").toInt(), 11);
}
+void tst_QmlCppCodegen::propertyMap()
+{
+ QQmlEngine engine;
+
+ const QUrl document(u"qrc:/qt/qml/TestTypes/propertyMap.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(
+ document.toString()
+ + u":5:5: QML WithPropertyMap: Unable to assign [undefined] to \"objectName\""));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ WithPropertyMap *w = qobject_cast<WithPropertyMap *>(o.data());
+ QVERIFY(w);
+
+ QVERIFY(w->objectName().isEmpty());
+
+ w->setProperties({
+ { u"foo"_s, u"aaa"_s },
+ { u"bar"_s, u"bbb"_s },
+ });
+
+ QCOMPARE(w->objectName(), u"aaa"_s);
+
+ w->setProperties({
+ { u"foo"_s, u"ccc"_s },
+ });
+
+ QCOMPARE(w->objectName(), u"ccc"_s);
+
+ w->setProperties({
+ { u"foo"_s, 24.25 },
+ });
+
+ QCOMPARE(w->objectName(), u"24.25"_s);
+}
+
void tst_QmlCppCodegen::propertyOfParent()
{
QQmlEngine engine;
--
2.51.1