aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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"