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
Pick-to: 6.8
Change-Id: I2fd7c8829267a2e3de1ac374859a4d21d948dd8f
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Olivier De Cannière 2024-11-11 13:23:39 +01:00
parent 48d1ae7f76
commit d148d8d784
2 changed files with 55 additions and 2 deletions

View File

@ -227,6 +227,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)))
@ -238,7 +251,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.
@ -247,7 +262,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)

View File

@ -46,6 +46,7 @@ private slots:
void jittedStoreLocalMarksValue();
void forInOnProxyMarksTarget();
void allocWithMemberDataMidwayDrain();
void markObjectWrappersAfterMarkWeakValues();
};
tst_qv4mm::tst_qv4mm()
@ -798,6 +799,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"