diff options
-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" |