diff options
author | Olivier De Cannière <[email protected]> | 2024-11-11 13:23:39 +0100 |
---|---|---|
committer | Olivier De Cannière <[email protected]> | 2024-11-20 08:23:08 +0000 |
commit | b34fca34941ecdbf99819bbf58b45332ac9e6fa7 (patch) | |
tree | c6d701873e841e708b57faf8d319b3b2e89e4aa9 | |
parent | e9659a684e2b87cc2a0e08329d8f1d63c92d4027 (diff) |
If an object is wrapped after the MarkWeakValues state of the GC but
before the sweep, it will not have been marked and will be collected.
We need to mark wrapped objects when they are created in those cases.
Fixes: QTBUG-130767
Change-Id: I2fd7c8829267a2e3de1ac374859a4d21d948dd8f
Reviewed-by: Ulf Hermann <[email protected]>
(cherry picked from commit d148d8d78419effdc28b6bf74704379aa9a5950a)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
(cherry picked from commit fbdbed919f59cc7b4520f5aab2149f4c99b63e24)
Reviewed-by: Jani Heikkinen <[email protected]>
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper_p.h | 21 | ||||
-rw-r--r-- | tests/auto/qml/qv4mm/tst_qv4mm.cpp | 36 |
2 files changed, 55 insertions, 2 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 826930cd25..a2538e7d5d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -222,6 +222,19 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags) +// We generally musn't pass ReturnedValue as arguments to other functions. +// In this case, we do it solely for marking purposes so it's fine. +inline void markIfPastMarkWeakValues(ExecutionEngine *engine, ReturnedValue rv) +{ + const auto gcState = engine->memoryManager->gcStateMachine->state; + if (gcState != GCStateMachine::Invalid && gcState >= GCState::MarkWeakValues) { + QV4::WriteBarrier::markCustom(engine, [rv](QV4::MarkStack *ms) { + auto *m = StaticValue::fromReturnedValue(rv).m(); + m->mark(ms); + }); + } +} + inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) { if (Q_UNLIKELY(QQmlData::wasDeleted(object))) @@ -233,7 +246,9 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje return ddata->jsWrapper.value(); } - return wrap_slowPath(engine, object); + const auto rv = wrap_slowPath(engine, object); + markIfPastMarkWeakValues(engine, rv); + return rv; } // Unfortunately we still need a non-const QObject* here because QQmlData needs to register itself in QObjectPrivate. @@ -242,7 +257,9 @@ inline ReturnedValue QObjectWrapper::wrapConst(ExecutionEngine *engine, QObject if (Q_UNLIKELY(QQmlData::wasDeleted(object))) return QV4::Encode::null(); - return wrapConst_slowPath(engine, object); + const auto rv = wrapConst_slowPath(engine, object); + markIfPastMarkWeakValues(engine, rv); + return rv; } inline bool canConvert(const QQmlPropertyCache *fromMo, const QQmlPropertyCache *toMo) diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index 7859d10551..6f502c7331 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -46,6 +46,7 @@ private slots: void jittedStoreLocalMarksValue(); void forInOnProxyMarksTarget(); void allocWithMemberDataMidwayDrain(); + void markObjectWrappersAfterMarkWeakValues(); }; tst_qv4mm::tst_qv4mm() @@ -795,6 +796,41 @@ void tst_qv4mm::allocWithMemberDataMidwayDrain() QVERIFY(o); // dummy check } +void tst_qv4mm::markObjectWrappersAfterMarkWeakValues() +{ + // Advance gc to just after MarkWeakValues + const auto setupGC = [](QV4::ExecutionEngine *v4) { + QCOMPARE(v4->memoryManager->gcBlocked, QV4::MemoryManager::Unblocked); + auto sm = v4->memoryManager->gcStateMachine.get(); + sm->reset(); + v4->memoryManager->gcBlocked = QV4::MemoryManager::NormalBlocked; + const QV4::GCState targetState = QV4::GCState(QV4::GCState::MarkWeakValues + 1); + while (sm->state != targetState) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QCOMPARE(sm->state, targetState); + }; + + QQmlEngine engine; + QV4::ExecutionEngine *v4 = engine.handle(); + setupGC(v4); + + QObject *object = new QObject; + object->setObjectName("yep"); + QJSEngine::setObjectOwnership(object, QJSEngine::JavaScriptOwnership); + engine.rootContext()->setContextProperty("prop", object); + (void) QV4::QObjectWrapper::wrap(v4, object); + QVERIFY(v4->memoryManager->tryForceGCCompletion()); + + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + const QVariant retrieved = engine.rootContext()->contextProperty("prop"); + QVERIFY(qvariant_cast<QObject *>(retrieved)); + QCOMPARE(qvariant_cast<QObject *>(retrieved)->objectName(), "yep"); +} + QTEST_MAIN(tst_qv4mm) #include "tst_qv4mm.moc" |