aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier De Cannière <[email protected]>2024-11-11 13:23:39 +0100
committerOlivier De Cannière <[email protected]>2024-11-20 08:23:08 +0000
commitb34fca34941ecdbf99819bbf58b45332ac9e6fa7 (patch)
treec6d701873e841e708b57faf8d319b3b2e89e4aa9
parente9659a684e2b87cc2a0e08329d8f1d63c92d4027 (diff)
Engine: Mark created wrapped objects after GCState::MarkWeakValuesv6.8.16.8.1
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.h21
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp36
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"