aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2025-10-27 13:48:49 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2025-10-28 15:17:35 +0000
commit6bec88d1e2da15d24c5af7f80e9a62fe7feeca69 (patch)
tree0ededc6d1c126b7b2355f0dd2ffe2f7627a6a24b
parentd97e7ebdbce7083b71a68ef6f8f3b7c1a29dcef1 (diff)
QQmlValueTypeWrapper: Mark as dirty if gadgetPtr goes away
In QQmlValueTypeWrapper::write, we clear the gadgetPtr if it didn't exist before, and it was only created for write. That however leads to an inconsistent isDirty state: The wrapper assumes it still has data which is up-to-date, but that data is actually gone. Avoid that issue by also marking the wrapper as dirty when the gadgetPtr is reset in write. Fixes: QTBUG-140414 Change-Id: Ie73b47ae49d4a26a43e2dbcc47365ae71a343109 Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> (cherry picked from commit c920059cbc0098b6218e814d445eac7da345b0e0) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/valueTypeReadAfterWrite.qml25
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp17
3 files changed, 43 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 10de6f85af..9349db0614 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -545,6 +545,7 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
bool destructGadgetOnExit = false;
auto cleanup = qScopeGuard([&]() {
if (destructGadgetOnExit) {
+ d()->setDirty(true);
d()->metaType().destruct(d()->gadgetPtr());
d()->setGadgetPtr(nullptr);
}
diff --git a/tests/auto/qml/qqmlecmascript/data/valueTypeReadAfterWrite.qml b/tests/auto/qml/qqmlecmascript/data/valueTypeReadAfterWrite.qml
new file mode 100644
index 0000000000..a8c05b6585
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/valueTypeReadAfterWrite.qml
@@ -0,0 +1,25 @@
+import QtQuick
+
+Item {
+ id: rootRectangle
+ width: 500
+ height: 500
+
+ readonly property color myBlue: "#1010FF"
+ readonly property color myRed: "#ff1010"
+ property string result: ""
+
+ property var themes: {
+ "blueTheme": {
+ bgColor: rootRectangle.myBlue
+ },
+ "redTheme": {
+ bgColor: rootRectangle.myRed
+ }
+ }
+
+ Component.onCompleted: {
+ result = JSON.stringify(rootRectangle.themes)
+ }
+}
+
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 807b040f79..7995f0df58 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -103,6 +103,7 @@ private slots:
void attachedProperties();
void enums();
void valueTypeFunctions();
+ void valueTypeReadAfterWrite();
void constantsOverrideBindings();
void outerBindingOverridesInnerBinding();
void groupPropertyBindingOrder();
@@ -1646,6 +1647,22 @@ void tst_qqmlecmascript::valueTypeFunctions()
QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
}
+void tst_qqmlecmascript::valueTypeReadAfterWrite()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent testComponent(&engine);
+ testComponent.loadFromModule("QtQuick", "Item");
+ if (!testComponent.isReady())
+ QSKIP("Test requires QtQuick");
+ }
+ QQmlComponent component(&engine, testFileUrl("valueTypeReadAfterWrite.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY2(object, qPrintable(component.errorString()));
+ QString result = object->property("result").toString();
+ QVERIFY2(result.contains("\"b\":1"), result.toUtf8().constData());
+}
+
/*
Tests that writing a constant to a property with a binding on it disables the
binding.