diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2024-02-15 14:12:47 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2024-02-20 22:46:15 +0000 |
commit | a88a9f4943652699ca48047a16ca5df88b0c1afe (patch) | |
tree | 545a9b6f951144180527c68523e2bf95edbd5383 | |
parent | 723fc1ade8d17d60a71f75512afc02422d17a5ac (diff) |
QtQml: Don't let objects be deleted during incubation
As long as the incubator object is alive, it should hold on to the
object being incubated. Anything else makes no sense. Re-use the
"valuemap" slot to hold on to the object once its ready.
Pick-to: 6.5
Fixes: QTBUG-119911
Change-Id: Ia9823aced5ec16a8da00e61ad7d606c63e40ed31
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 95965ba19f6a1cc04d8bc04e2c55ececcd87edbb)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 5723be147af1a77076924da1e422c39927140a46)
-rw-r--r-- | src/qml/qml/qqmlcomponent.cpp | 25 | ||||
-rw-r--r-- | tests/auto/qml/qqmlincubator/data/garbageCollection2.qml | 12 | ||||
-rw-r--r-- | tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp | 6 |
3 files changed, 33 insertions, 10 deletions
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 11ef88444e..0d2885209e 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1498,7 +1498,7 @@ namespace QV4 { namespace Heap { #define QmlIncubatorObjectMembers(class, Member) \ - Member(class, HeapValue, HeapValue, valuemap) \ + Member(class, HeapValue, HeapValue, valuemapOrObject) \ Member(class, HeapValue, HeapValue, statusChanged) \ Member(class, Pointer, QmlContext *, qmlContext) \ Member(class, NoMark, QQmlComponentIncubator *, incubator) \ @@ -1911,7 +1911,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) r->setPrototypeOf(p); if (!valuemap->isUndefined()) - r->d()->valuemap.set(scope.engine, valuemap); + r->d()->valuemapOrObject.set(scope.engine, valuemap); r->d()->qmlContext.set(scope.engine, v4->qmlContext()); r->d()->parent = parent; @@ -2014,7 +2014,7 @@ QQmlComponentExtension::~QQmlComponentExtension() void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m) { Object::init(); - valuemap.set(internalClass->engine, QV4::Value::undefinedValue()); + valuemapOrObject.set(internalClass->engine, QV4::Value::undefinedValue()); statusChanged.set(internalClass->engine, QV4::Value::undefinedValue()); parent.init(); qmlContext.set(internalClass->engine, nullptr); @@ -2031,13 +2031,13 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties *re { QQmlComponent_setQmlParent(o, d()->parent); - if (!d()->valuemap.isUndefined()) { + if (!d()->valuemapOrObject.isUndefined()) { QV4::ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o)); QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext); QQmlComponentPrivate::setInitialProperties( - v4, qmlCtxt, obj, d()->valuemap, requiredProperties, o, + v4, qmlCtxt, obj, d()->valuemapOrObject, requiredProperties, o, QQmlIncubatorPrivate::get(d()->incubator)->creator.data()); } } @@ -2045,13 +2045,18 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties *re void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) { QV4::Scope scope(engine()); - // hold the incubated object in a scoped value to prevent it's destruction before this method returns - QV4::ScopedObject incubatedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, d()->incubator->object())); + + QObject *object = d()->incubator->object(); if (s == QQmlIncubator::Ready) { - Q_ASSERT(QQmlData::get(d()->incubator->object())); - QQmlData::get(d()->incubator->object())->explicitIndestructibleSet = false; - QQmlData::get(d()->incubator->object())->indestructible = false; + // We don't need the arguments anymore, but we still want to hold on to the object so + // that it doesn't get gc'd + d()->valuemapOrObject.set(scope.engine, QV4::QObjectWrapper::wrap(scope.engine, object)); + + QQmlData *ddata = QQmlData::get(object); + Q_ASSERT(ddata); + ddata->explicitIndestructibleSet = false; + ddata->indestructible = false; } QV4::ScopedFunctionObject f(scope, d()->statusChanged); diff --git a/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml b/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml new file mode 100644 index 0000000000..b5ba531ede --- /dev/null +++ b/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml @@ -0,0 +1,12 @@ +import QtQml +QtObject { + property Component comp: Component { + QtObject {} + } + + property QtObject incubated: { + var i = comp.incubateObject(null, {}, Qt.Synchronous); + gc(); + return i.object + } +} diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp index 1baf61574e..eaa59f036d 100644 --- a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp +++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp @@ -1168,6 +1168,12 @@ void tst_qqmlincubator::garbageCollection() // verify incubator is correctly collected now that incubation is complete and all references are gone engine.collectGarbage(); QVERIFY(weakIncubatorRef.isNullOrUndefined()); + + QQmlComponent component2(&engine, testFileUrl("garbageCollection2.qml")); + QVERIFY2(component2.isReady(), qPrintable(component2.errorString())); + QScopedPointer<QObject> obj2(component2.create()); + QVERIFY(!obj2.isNull()); + QVERIFY(obj2->property("incubated").value<QObject *>() != nullptr); } void tst_qqmlincubator::requiredProperties() |