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. Pick-to: 6.5 6.2 5.15 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 commit27ba69af2f
) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit833f4f6913
)
This commit is contained in:
parent
61fa31707a
commit
477c3c3c25
|
@ -1402,9 +1402,7 @@ void QObjectWrapper::destroyObject(bool lastCall)
|
||||||
if (!o->parent() && !ddata->indestructible) {
|
if (!o->parent() && !ddata->indestructible) {
|
||||||
if (ddata && ddata->ownContext) {
|
if (ddata && ddata->ownContext) {
|
||||||
Q_ASSERT(ddata->ownContext.data() == ddata->context);
|
Q_ASSERT(ddata->ownContext.data() == ddata->context);
|
||||||
ddata->ownContext->emitDestruction();
|
ddata->ownContext->deepClearContextObject(o);
|
||||||
if (ddata->ownContext->contextObject() == o)
|
|
||||||
ddata->ownContext->setContextObject(nullptr);
|
|
||||||
ddata->ownContext.reset();
|
ddata->ownContext.reset();
|
||||||
ddata->context = nullptr;
|
ddata->context = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,28 @@ public:
|
||||||
QObject *contextObject() const { return m_contextObject; }
|
QObject *contextObject() const { return m_contextObject; }
|
||||||
void setContextObject(QObject *contextObject) { m_contextObject = contextObject; }
|
void setContextObject(QObject *contextObject) { m_contextObject = contextObject; }
|
||||||
|
|
||||||
|
template<typename HandleSelf, typename HandleLinked>
|
||||||
|
void deepClearContextObject(
|
||||||
|
QObject *contextObject, HandleSelf &&handleSelf, HandleLinked &&handleLinked) {
|
||||||
|
for (QQmlContextData *lc = m_linkedContext.data(); lc; lc = lc->m_linkedContext.data()) {
|
||||||
|
handleLinked(lc);
|
||||||
|
if (lc->m_contextObject == contextObject)
|
||||||
|
lc->m_contextObject = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelf(this);
|
||||||
|
if (m_contextObject == contextObject)
|
||||||
|
m_contextObject = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deepClearContextObject(QObject *contextObject)
|
||||||
|
{
|
||||||
|
deepClearContextObject(
|
||||||
|
contextObject,
|
||||||
|
[](QQmlContextData *self) { self->emitDestruction(); },
|
||||||
|
[](QQmlContextData *){});
|
||||||
|
}
|
||||||
|
|
||||||
QQmlEngine *engine() const { return m_engine; }
|
QQmlEngine *engine() const { return m_engine; }
|
||||||
void setEngine(QQmlEngine *engine) { m_engine = engine; }
|
void setEngine(QQmlEngine *engine) { m_engine = engine; }
|
||||||
|
|
||||||
|
|
|
@ -215,23 +215,16 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
|
||||||
{
|
{
|
||||||
QObjectPrivate *p = QObjectPrivate::get(o);
|
QObjectPrivate *p = QObjectPrivate::get(o);
|
||||||
if (QQmlData *d = QQmlData::get(p)) {
|
if (QQmlData *d = QQmlData::get(p)) {
|
||||||
|
const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
|
||||||
if (d->ownContext) {
|
if (d->ownContext) {
|
||||||
for (QQmlRefPointer<QQmlContextData> lc = d->ownContext->linkedContext(); lc;
|
d->ownContext->deepClearContextObject(o, invalidate, invalidate);
|
||||||
lc = lc->linkedContext()) {
|
|
||||||
lc->invalidate();
|
|
||||||
if (lc->contextObject() == o)
|
|
||||||
lc->setContextObject(nullptr);
|
|
||||||
}
|
|
||||||
d->ownContext->invalidate();
|
|
||||||
if (d->ownContext->contextObject() == o)
|
|
||||||
d->ownContext->setContextObject(nullptr);
|
|
||||||
d->ownContext.reset();
|
d->ownContext.reset();
|
||||||
d->context = 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->setContextObject(nullptr);
|
|
||||||
|
|
||||||
if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
|
if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
|
||||||
// This is somewhat dangerous because another thread might concurrently
|
// This is somewhat dangerous because another thread might concurrently
|
||||||
// try to resolve the dynamic metaobject. In practice this will then
|
// try to resolve the dynamic metaobject. In practice this will then
|
||||||
|
@ -407,9 +400,7 @@ void QQmlData::setQueuedForDeletion(QObject *object)
|
||||||
if (QQmlData *ddata = QQmlData::get(object)) {
|
if (QQmlData *ddata = QQmlData::get(object)) {
|
||||||
if (ddata->ownContext) {
|
if (ddata->ownContext) {
|
||||||
Q_ASSERT(ddata->ownContext.data() == ddata->context);
|
Q_ASSERT(ddata->ownContext.data() == ddata->context);
|
||||||
ddata->context->emitDestruction();
|
ddata->ownContext->deepClearContextObject(object);
|
||||||
if (ddata->ownContext->contextObject() == object)
|
|
||||||
ddata->ownContext->setContextObject(nullptr);
|
|
||||||
ddata->ownContext.reset();
|
ddata->ownContext.reset();
|
||||||
ddata->context = nullptr;
|
ddata->context = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import QtQml
|
||||||
|
|
||||||
|
B {
|
||||||
|
id: b
|
||||||
|
property int y: 2
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import QtQml
|
||||||
|
|
||||||
|
C {
|
||||||
|
id: z
|
||||||
|
property int z: 3
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import QtQml
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: outer
|
||||||
|
objectName: "the" + "C"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import QtQml
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
property A a: A {}
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ private slots:
|
||||||
void outerContextObject();
|
void outerContextObject();
|
||||||
void contextObjectHierarchy();
|
void contextObjectHierarchy();
|
||||||
void destroyContextProperty();
|
void destroyContextProperty();
|
||||||
|
void destroyContextObject();
|
||||||
|
|
||||||
void numericContextProperty();
|
void numericContextProperty();
|
||||||
void gcDeletesContextObject();
|
void gcDeletesContextObject();
|
||||||
|
@ -982,6 +983,30 @@ void tst_qqmlcontext::destroyContextProperty()
|
||||||
// TODO: Or are we?
|
// TODO: Or are we?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_qqmlcontext::destroyContextObject()
|
||||||
|
{
|
||||||
|
QQmlEngine engine;
|
||||||
|
QList<QQmlRefPointer<QQmlContextData>> contexts;
|
||||||
|
QQmlComponent component(&engine, testFileUrl("destroyContextObject.qml"));
|
||||||
|
QScopedPointer<QObject> root(component.create());
|
||||||
|
|
||||||
|
QPointer<QObject> a = root->property("a").value<QObject *>();
|
||||||
|
QVERIFY(a);
|
||||||
|
|
||||||
|
for (QQmlRefPointer<QQmlContextData> 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()
|
void tst_qqmlcontext::numericContextProperty()
|
||||||
{
|
{
|
||||||
QQmlEngine engine;
|
QQmlEngine engine;
|
||||||
|
|
Loading…
Reference in New Issue