aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2024-01-19 16:19:06 +0100
committerUlf Hermann <ulf.hermann@qt.io>2024-02-06 11:12:49 +0100
commitbba76cd15f718c79f9c5e6f5e297ed211887f5f4 (patch)
treee7f576e85f5156c82839accdeb1c47a8f61a90c8
parent684656d53a3064c61744921269d4dca25c08dc5b (diff)
QtQml: Clear context objects more thoroughly on destruction
The same object can be the context object of a hierarchy of contexts. So far we would only clear one of them, leaving dangling pointers in the others. Clear all the contexts. Fixes: QTBUG-119326 Change-Id: I509f257672813866e3736b51f430f1243a8577f0 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 27ba69af2f64a8b194655c9fbb276ce981075f75) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit 833f4f6913835a18c37b02bd4784d62cbb4d0701) (cherry picked from commit 477c3c3c25d478caa5b833a9c64f3945b3d3a83e) (cherry picked from commit 7318fadc42e462babeca473864d12cfd033ffa42) (cherry picked from commit e1ded8e46f71c12855811d0ee1cb2c36dcf67517)
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2
-rw-r--r--src/qml/qml/qqmlcontext_p.h21
-rw-r--r--src/qml/qml/qqmlengine.cpp20
-rw-r--r--tests/auto/qml/qqmlcontext/data/A.qml6
-rw-r--r--tests/auto/qml/qqmlcontext/data/B.qml6
-rw-r--r--tests/auto/qml/qqmlcontext/data/C.qml6
-rw-r--r--tests/auto/qml/qqmlcontext/data/destroyContextObject.qml5
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp25
8 files changed, 76 insertions, 15 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index b9cb74c3ca..e261b4a6d3 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1153,7 +1153,7 @@ void QObjectWrapper::destroyObject(bool lastCall)
if (!h->object()->parent() && !ddata->indestructible) {
if (ddata && ddata->ownContext) {
Q_ASSERT(ddata->ownContext == ddata->context);
- ddata->ownContext->emitDestruction();
+ ddata->ownContext->deepClearContextObject(h->object());
ddata->ownContext = nullptr;
ddata->context = nullptr;
}
diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h
index 4d09abd927..92fd9f3a9f 100644
--- a/src/qml/qml/qqmlcontext_p.h
+++ b/src/qml/qml/qqmlcontext_p.h
@@ -139,6 +139,27 @@ public:
// If internal is false publicContext owns this.
QQmlContext *asQQmlContext();
QQmlContextPrivate *asQQmlContextPrivate();
+
+ template<typename HandleSelf, typename HandleLinked>
+ void deepClearContextObject(
+ QObject *expectedContextObject, HandleSelf &&handleSelf, HandleLinked &&handleLinked) {
+ for (QQmlContextData *lc = linkedContext; lc; lc = lc->linkedContext) {
+ handleLinked(lc);
+ if (lc->contextObject == expectedContextObject)
+ lc->contextObject = nullptr;
+ }
+ handleSelf(this);
+ if (contextObject == expectedContextObject)
+ contextObject = nullptr;
+ }
+ void deepClearContextObject(QObject *contextObject)
+ {
+ deepClearContextObject(
+ contextObject,
+ [](QQmlContextData *self) { self->emitDestruction(); },
+ [](QQmlContextData *){});
+ }
+
quint32 refCount = 0;
quint32 isInternal:1;
quint32 isJSContext:1;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index e564a3d0cf..a0ecdfa61a 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -695,22 +695,16 @@ QQmlEnginePrivate::~QQmlEnginePrivate()
void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
{
if (QQmlData *d = QQmlData::get(o)) {
+ const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
if (d->ownContext) {
- for (QQmlContextData *lc = d->ownContext->linkedContext; lc; lc = lc->linkedContext) {
- lc->invalidate();
- if (lc->contextObject == o)
- lc->contextObject = nullptr;
- }
- d->ownContext->invalidate();
- if (d->ownContext->contextObject == o)
- d->ownContext->contextObject = nullptr;
+ d->ownContext->deepClearContextObject(o, invalidate, invalidate);
d->ownContext = nullptr;
d->context = nullptr;
+ Q_ASSERT(!d->outerContext || d->outerContext->contextObject != o);
+ } else if (d->outerContext && d->outerContext->contextObject == o) {
+ d->outerContext->deepClearContextObject(o, invalidate, invalidate);
}
- if (d->outerContext && d->outerContext->contextObject == o)
- d->outerContext->contextObject = nullptr;
-
// Mark this object as in the process of deletion to
// prevent it resolving in bindings
QQmlData::markAsDeleted(o);
@@ -884,9 +878,7 @@ void QQmlData::setQueuedForDeletion(QObject *object)
if (QQmlData *ddata = QQmlData::get(object)) {
if (ddata->ownContext) {
Q_ASSERT(ddata->ownContext == ddata->context);
- ddata->context->emitDestruction();
- if (ddata->ownContext->contextObject == object)
- ddata->ownContext->contextObject = nullptr;
+ ddata->ownContext->deepClearContextObject(object);
ddata->ownContext = nullptr;
ddata->context = nullptr;
}
diff --git a/tests/auto/qml/qqmlcontext/data/A.qml b/tests/auto/qml/qqmlcontext/data/A.qml
new file mode 100644
index 0000000000..569a54422f
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/A.qml
@@ -0,0 +1,6 @@
+import QtQml 2.15
+
+B {
+ id: b
+ property int y: 2
+}
diff --git a/tests/auto/qml/qqmlcontext/data/B.qml b/tests/auto/qml/qqmlcontext/data/B.qml
new file mode 100644
index 0000000000..43e3db04cb
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/B.qml
@@ -0,0 +1,6 @@
+import QtQml 2.15
+
+C {
+ id: z
+ property int z: 3
+}
diff --git a/tests/auto/qml/qqmlcontext/data/C.qml b/tests/auto/qml/qqmlcontext/data/C.qml
new file mode 100644
index 0000000000..07ad9c4f5a
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/C.qml
@@ -0,0 +1,6 @@
+import QtQml 2.15
+
+QtObject {
+ id: outer
+ objectName: "the" + "C"
+}
diff --git a/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml
new file mode 100644
index 0000000000..bfd090ab38
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml
@@ -0,0 +1,5 @@
+import QtQml 2.15
+
+QtObject {
+ property A a: A {}
+}
diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
index 2866988a1a..1106f7b529 100644
--- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
+++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
@@ -72,6 +72,7 @@ private slots:
void outerContextObject();
void contextObjectHierarchy();
void destroyContextProperty();
+ void destroyContextObject();
void numericContextProperty();
@@ -920,6 +921,30 @@ void tst_qqmlcontext::destroyContextProperty()
// TODO: Or are we?
}
+void tst_qqmlcontext::destroyContextObject()
+{
+ QQmlEngine engine;
+ QList<QQmlContextDataRef> contexts;
+ QQmlComponent component(&engine, testFileUrl("destroyContextObject.qml"));
+ QScopedPointer<QObject> root(component.create());
+
+ QPointer<QObject> a = root->property("a").value<QObject *>();
+ QVERIFY(a);
+
+ for (QQmlContextDataRef context = QQmlData::get(a)->ownContext;
+ context; context = context->parent) {
+ contexts.append(context);
+ }
+
+ QObject *deleted = a.data();
+ root.reset();
+
+ QVERIFY(a.isNull());
+
+ for (const auto &context : contexts)
+ QVERIFY(context->contextObject != deleted);
+}
+
void tst_qqmlcontext::numericContextProperty()
{
QQmlEngine engine;