aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2025-05-23 12:31:46 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2025-06-09 07:00:17 +0000
commit05bf1bcb5067287644e7a41067bd4d779c56f4b9 (patch)
treebfe5d568d5276c6c1ff665c653d29540f101aee2
parent5cecb800c184e35583620bccd7c83c284dd8642a (diff)
QQmlBind: Store all values as QV4::PersistentValue
We can't store them as QObject because that may result in dangling pointers. We need the heap wrappers to track deletions. Pick-to: 6.9 6.8 6.5 Fixes: QTBUG-135295 Change-Id: Id53932aec834ad59104bd867c0056fb8da94d827 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit db7a74d7dc3097c4464c22cbfc189bc180b80fd6) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/qmlmeta/types/qqmlbind.cpp75
-rw-r--r--tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml51
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp19
3 files changed, 119 insertions, 26 deletions
diff --git a/src/qmlmeta/types/qqmlbind.cpp b/src/qmlmeta/types/qqmlbind.cpp
index 8ca11064ec..a66ace6ddd 100644
--- a/src/qmlmeta/types/qqmlbind.cpp
+++ b/src/qmlmeta/types/qqmlbind.cpp
@@ -59,10 +59,8 @@ public:
silentDestroy(oldKind);
switch (newKind) {
case QQmlBindEntryKind::V4Value:
- new (&v4Value) QV4::PersistentValue(std::move(other.v4Value));
- break;
case QQmlBindEntryKind::Variant:
- new (&variant) QVariant(std::move(other.variant));
+ new (&v4Value) QV4::PersistentValue(std::move(other.v4Value));
break;
case QQmlBindEntryKind::Binding:
new (&binding) QQmlAnyBinding(std::move(other.binding));
@@ -79,10 +77,8 @@ public:
silentDestroy(oldKind);
switch (newKind) {
case QQmlBindEntryKind::V4Value:
- new (&v4Value) QV4::PersistentValue(other.v4Value);
- break;
case QQmlBindEntryKind::Variant:
- new (&variant) QVariant(other.variant);
+ new (&v4Value) QV4::PersistentValue(other.v4Value);
break;
case QQmlBindEntryKind::Binding:
new (&binding) QQmlAnyBinding(other.binding);
@@ -97,10 +93,8 @@ public:
{
switch (kind) {
case QQmlBindEntryKind::V4Value:
- v4Value.~PersistentValue();
- break;
case QQmlBindEntryKind::Variant:
- variant.~QVariant();
+ v4Value.~PersistentValue();
break;
case QQmlBindEntryKind::Binding:
binding.~QQmlAnyBinding();
@@ -111,10 +105,11 @@ public:
return QQmlBindEntryKind::None;
}
- [[nodiscard]] QQmlBindEntryKind set(QVariant v, QQmlBindEntryKind oldKind)
+ [[nodiscard]] QQmlBindEntryKind set(
+ QV4::ExecutionEngine *engine, const QVariant &v, QQmlBindEntryKind oldKind)
{
silentDestroy(oldKind);
- new (&variant) QVariant(std::move(v));
+ new (&v4Value) QV4::PersistentValue(engine, engine->fromVariant(v));
return QQmlBindEntryKind::Variant;
}
@@ -125,6 +120,13 @@ public:
return QQmlBindEntryKind::V4Value;
}
+ [[nodiscard]] QQmlBindEntryKind setVariant(QV4::PersistentValue v, QQmlBindEntryKind oldKind)
+ {
+ silentDestroy(oldKind);
+ new (&v4Value) QV4::PersistentValue(std::move(v));
+ return QQmlBindEntryKind::Variant;
+ }
+
[[nodiscard]] QQmlBindEntryKind set(QQmlAnyBinding v, QQmlBindEntryKind oldKind)
{
silentDestroy(oldKind);
@@ -133,7 +135,6 @@ public:
}
QV4::PersistentValue v4Value;
- QVariant variant;
QQmlAnyBinding binding;
private:
@@ -730,7 +731,10 @@ QVariant QQmlBind::value() const
Q_D(const QQmlBind);
if (d->mode == QQmlBindPrivate::ObjectPropertyValue) {
Q_ASSERT(d->objectPropertyValueData.entry.currentKind == QQmlBindEntryKind::Variant);
- return d->objectPropertyValueData.entry.current.variant;
+ QV4::ExecutionEngine *engine = d->objectPropertyValueData.entry.current.v4Value.engine();
+ return engine->toVariant(
+ *d->objectPropertyValueData.entry.current.v4Value.valueRef(), QMetaType());
+
}
return QVariant();
}
@@ -751,7 +755,13 @@ void QQmlBind::setValue(const QVariant &v)
Q_FALLTHROUGH();
case QQmlBindPrivate::ObjectPropertyValue: {
QQmlBindEntry *targetEntry = &d->objectPropertyValueData.entry;
- targetEntry->currentKind = targetEntry->current.set(v, targetEntry->currentKind);
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine) {
+ qWarning() << "QQmlBind must be created in a QML context";
+ return;
+ }
+ targetEntry->currentKind
+ = targetEntry->current.set(engine->handle(), v, targetEntry->currentKind);
prepareEval();
break;
}
@@ -1054,8 +1064,8 @@ void QQmlBindPrivate::decodeBinding(
break;
}
- const auto setVariant = [&entry](QVariant var) {
- entry.currentKind = entry.current.set(std::move(var), entry.currentKind);
+ const auto setVariant = [&entry](QV4::PersistentValue value) {
+ entry.currentKind = entry.current.setVariant(value, entry.currentKind);
};
const auto setBinding = [&entry](QQmlAnyBinding binding) {
@@ -1086,16 +1096,20 @@ void QQmlBindPrivate::decodeBinding(
}
break;
case QV4::CompiledData::Binding::Type_String:
- setVariant(compilationUnit->bindingValueAsString(binding));
+ setVariant(QV4::PersistentValue(
+ compilationUnit->engine,
+ compilationUnit->runtimeStrings[binding->stringIndex]->asReturnedValue()));
break;
case QV4::CompiledData::Binding::Type_Number:
- setVariant(compilationUnit->bindingValueAsNumber(binding));
+ setVariant(QV4::PersistentValue(
+ compilationUnit->engine,
+ compilationUnit->constants[binding->value.constantValueIndex].asReturnedValue()));
break;
case QV4::CompiledData::Binding::Type_Boolean:
- setVariant(binding->valueAsBoolean());
+ setVariant(QV4::PersistentValue(compilationUnit->engine, QV4::Encode(binding->value.b)));
break;
case QV4::CompiledData::Binding::Type_Null:
- setVariant(QVariant::fromValue(nullptr));
+ setVariant(QV4::PersistentValue(compilationUnit->engine, QV4::Encode::null()));
break;
case QV4::CompiledData::Binding::Type_Object:
case QV4::CompiledData::Binding::Type_Invalid:
@@ -1236,8 +1250,11 @@ bool QQmlBindPrivate::isCurrent(QQmlBindEntry *entry) const
QV4::Value::fromReturnedValue(vmemo->vmeProperty(propPriv->core.coreIndex())),
*entry->current.v4Value.valueRef());
}
- case QQmlBindEntryKind::Variant:
- return entry->current.variant == entry->prop.read();
+ case QQmlBindEntryKind::Variant: {
+ const QV4::PersistentValue &v4Value = entry->current.v4Value;
+ return v4Value.engine()->toVariant(*v4Value.valueRef(), entry->prop.propertyMetaType())
+ == entry->prop.read();
+ }
case QQmlBindEntryKind::Binding:
return entry->current.binding == QQmlAnyBinding::ofProperty(entry->prop);
case QQmlBindEntryKind::None:
@@ -1283,7 +1300,9 @@ void QQmlBindPrivate::preEvalEntry(QQmlBindEntry *entry)
case QQmlBindEntryKind::Variant:
if (restoreValue) {
QQmlAnyBinding::takeFrom(entry->prop); // we don't want to have a binding active
- entry->prop.write(entry->previous.variant);
+ const QV4::PersistentValue &v4Value = entry->previous.v4Value;
+ entry->prop.write(v4Value.engine()->toVariant(
+ *v4Value.valueRef(), entry->prop.propertyMetaType()));
entry->clearPrev();
}
break;
@@ -1311,7 +1330,8 @@ void QQmlBindPrivate::preEvalEntry(QQmlBindEntry *entry)
QV4::PersistentValue(vmemo->engine, retVal), entry->previousKind);
} else {
// nope, use the meta object to get a QVariant
- entry->previousKind = entry->previous.set(entry->prop.read(), entry->previousKind);
+ entry->previousKind = entry->previous.set(
+ propPriv->engine->handle(), entry->prop.read(), entry->previousKind);
}
}
}
@@ -1326,9 +1346,12 @@ void QQmlBindPrivate::postEvalEntry(QQmlBindEntry *entry)
if (!entry->prop.isValid())
return;
switch (entry->currentKind) {
- case QQmlBindEntryKind::Variant:
- entry->prop.write(entry->current.variant);
+ case QQmlBindEntryKind::Variant: {
+ const QV4::PersistentValue &v4Value = entry->current.v4Value;
+ entry->prop.write(
+ v4Value.engine()->toVariant(*v4Value.valueRef(), entry->prop.propertyMetaType()));
break;
+ }
case QQmlBindEntryKind::Binding:
Q_ASSERT(!delayed);
entry->current.binding.installOn(entry->prop);
diff --git a/tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml b/tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml
new file mode 100644
index 0000000000..fb95305791
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml
@@ -0,0 +1,51 @@
+pragma ComponentBehavior: Bound
+import QtQml
+
+QtObject {
+ id: root
+
+ property Component c: Component {
+ QtObject {
+ Component.onDestruction: {
+ console.log("dead")
+ timer.start()
+ }
+ }
+ }
+
+ property QtObject stashed: c.createObject()
+ property QtObject replacement: QtObject {}
+
+ onStashedChanged: {
+ if (stashed) {
+ page = stashed
+ console.log("alive")
+ binding.when = true
+ }
+ }
+
+ property QtObject page
+
+ property Binding binding: Binding {
+ id: binding
+ target: root
+ property: "page"
+ when: false
+ value: root.replacement
+ }
+
+ property Timer timer: Timer {
+ id: timer
+ interval: 10
+ onTriggered: {
+ console.log("before")
+ binding.when = false
+ console.log("after")
+ }
+ }
+
+ Component.onCompleted: {
+ console.log("destroy")
+ stashed.destroy();
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index 42f5cfc1dc..48f437387d 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -53,6 +53,7 @@ private slots:
void qQmlPropertyToPropertyBinding();
void qQmlPropertyToPropertyBindingReverse();
void delayedBindingDestruction();
+ void deleteStashedObject();
private:
QQmlEngine engine;
@@ -856,6 +857,24 @@ void tst_qqmlbinding::delayedBindingDestruction()
verifyDelegate(QLatin1String("foo"));
}
+void tst_qqmlbinding::deleteStashedObject()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("deleteStashedObject.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "alive");
+ QTest::ignoreMessage(QtDebugMsg, "destroy");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ QVERIFY(object->property("page").value<QObject *>() != nullptr);
+
+ QTest::ignoreMessage(QtDebugMsg, "dead");
+ QTest::ignoreMessage(QtDebugMsg, "before");
+ QTest::ignoreMessage(QtDebugMsg, "after");
+ QTRY_VERIFY(object->property("page").value<QObject *>() == nullptr);
+}
+
QTEST_MAIN(tst_qqmlbinding)
#include "tst_qqmlbinding.moc"