From b34fca34941ecdbf99819bbf58b45332ac9e6fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20De=20Canni=C3=A8re?= Date: Mon, 11 Nov 2024 13:23:39 +0100 Subject: Engine: Mark created wrapped objects after GCState::MarkWeakValues 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 (cherry picked from commit d148d8d78419effdc28b6bf74704379aa9a5950a) Reviewed-by: Qt Cherry-pick Bot (cherry picked from commit fbdbed919f59cc7b4520f5aab2149f4c99b63e24) Reviewed-by: Jani Heikkinen --- src/qml/jsruntime/qv4qobjectwrapper_p.h | 21 +++++++++++++++++-- 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(retrieved)); + QCOMPARE(qvariant_cast(retrieved)->objectName(), "yep"); +} + QTEST_MAIN(tst_qv4mm) #include "tst_qv4mm.moc" -- cgit v1.2.3