|
|
966857 |
From 00dd37619f35db3cddb2ac59198d3bde7ce73cd7 Mon Sep 17 00:00:00 2001
|
|
|
966857 |
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
|
966857 |
Date: Sun, 10 Oct 2021 21:04:21 +0300
|
|
|
966857 |
Subject: [PATCH 09/20] Fix sweep step for tainted QObject JavaScript wrappers
|
|
|
966857 |
|
|
|
966857 |
Currently, whenever the garbage collector runs, it will destroy all
|
|
|
966857 |
valid tainted wrappers.
|
|
|
966857 |
|
|
|
966857 |
Only null or undefined wrappers will be preserved in the
|
|
|
966857 |
m_multiplyWrappedQObjects map.
|
|
|
966857 |
|
|
|
966857 |
It seems like "!" was overlooked in
|
|
|
966857 |
3b5d37ce3841c4bfdf1c629d33f0e33b881b47fb. Prior to that change, it
|
|
|
966857 |
was "!it.value()->markBit()", so calling erase() in the then branch
|
|
|
966857 |
did make sense. But with "!it.value().isNullOrUndefined()", erase()
|
|
|
966857 |
will be called for every valid wrapper, which is the opposite what we
|
|
|
966857 |
want.
|
|
|
966857 |
|
|
|
966857 |
Pick-to: 5.15 6.2
|
|
|
966857 |
Change-Id: I2bf2630f538af8cbd4bfffcff29d67be6c278265
|
|
|
966857 |
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
966857 |
(cherry picked from commit e6b2f88d892dcf396580a61662f569bf69d6d9d1)
|
|
|
966857 |
---
|
|
|
966857 |
src/qml/memory/qv4mm.cpp | 2 +-
|
|
|
966857 |
tests/auto/qml/qjsengine/tst_qjsengine.cpp | 39 ++++++++++++++++++++++
|
|
|
966857 |
tests/auto/qml/qv4mm/tst_qv4mm.cpp | 6 ++--
|
|
|
966857 |
3 files changed, 43 insertions(+), 4 deletions(-)
|
|
|
966857 |
|
|
|
966857 |
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
|
|
|
966857 |
index 06caf04e5a..da149a67c4 100644
|
|
|
966857 |
--- a/src/qml/memory/qv4mm.cpp
|
|
|
966857 |
+++ b/src/qml/memory/qv4mm.cpp
|
|
|
966857 |
@@ -981,7 +981,7 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
|
|
|
966857 |
|
|
|
966857 |
if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = engine->m_multiplyWrappedQObjects) {
|
|
|
966857 |
for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) {
|
|
|
966857 |
- if (!it.value().isNullOrUndefined())
|
|
|
966857 |
+ if (it.value().isNullOrUndefined())
|
|
|
966857 |
it = multiplyWrappedQObjects->erase(it);
|
|
|
966857 |
else
|
|
|
966857 |
++it;
|
|
|
966857 |
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
|
|
|
966857 |
index 3b7d74df63..b75bf820d5 100644
|
|
|
966857 |
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
|
|
|
966857 |
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
|
|
|
966857 |
@@ -102,6 +102,7 @@ private slots:
|
|
|
966857 |
void valueConversion_RegularExpression();
|
|
|
966857 |
void castWithMultipleInheritance();
|
|
|
966857 |
void collectGarbage();
|
|
|
966857 |
+ void collectGarbageNestedWrappersTwoEngines();
|
|
|
966857 |
void gcWithNestedDataStructure();
|
|
|
966857 |
void stacktrace();
|
|
|
966857 |
void numberParsing_data();
|
|
|
966857 |
@@ -1809,6 +1810,44 @@ void tst_QJSEngine::collectGarbage()
|
|
|
966857 |
QVERIFY(ptr.isNull());
|
|
|
966857 |
}
|
|
|
966857 |
|
|
|
966857 |
+class TestObjectContainer : public QObject
|
|
|
966857 |
+{
|
|
|
966857 |
+ Q_OBJECT
|
|
|
966857 |
+ Q_PROPERTY(QObject *dummy MEMBER m_dummy CONSTANT)
|
|
|
966857 |
+
|
|
|
966857 |
+public:
|
|
|
966857 |
+ TestObjectContainer() : m_dummy(new QObject(this)) {}
|
|
|
966857 |
+
|
|
|
966857 |
+private:
|
|
|
966857 |
+ QObject *m_dummy;
|
|
|
966857 |
+};
|
|
|
966857 |
+
|
|
|
966857 |
+void tst_QJSEngine::collectGarbageNestedWrappersTwoEngines()
|
|
|
966857 |
+{
|
|
|
966857 |
+ QJSEngine engine1;
|
|
|
966857 |
+ QJSEngine engine2;
|
|
|
966857 |
+
|
|
|
966857 |
+ TestObjectContainer container;
|
|
|
966857 |
+ QQmlEngine::setObjectOwnership(&container, QQmlEngine::CppOwnership);
|
|
|
966857 |
+
|
|
|
966857 |
+ engine1.globalObject().setProperty("foobar", engine1.newQObject(&container));
|
|
|
966857 |
+ engine2.globalObject().setProperty("foobar", engine2.newQObject(&container));
|
|
|
966857 |
+
|
|
|
966857 |
+ engine1.evaluate("foobar.dummy.baz = 42");
|
|
|
966857 |
+ engine2.evaluate("foobar.dummy.baz = 43");
|
|
|
966857 |
+
|
|
|
966857 |
+ QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42);
|
|
|
966857 |
+ QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43);
|
|
|
966857 |
+
|
|
|
966857 |
+ engine1.collectGarbage();
|
|
|
966857 |
+ engine2.collectGarbage();
|
|
|
966857 |
+
|
|
|
966857 |
+ // The GC should not collect dummy object wrappers neither in engine1 nor engine2, we
|
|
|
966857 |
+ // verify that by checking whether the baz property still has its previous value.
|
|
|
966857 |
+ QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42);
|
|
|
966857 |
+ QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43);
|
|
|
966857 |
+}
|
|
|
966857 |
+
|
|
|
966857 |
void tst_QJSEngine::gcWithNestedDataStructure()
|
|
|
966857 |
{
|
|
|
966857 |
// The GC must be able to traverse deeply nested objects, otherwise this
|
|
|
966857 |
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
|
|
|
966857 |
index 5d635aa63b..824fd89e5b 100644
|
|
|
966857 |
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
|
|
|
966857 |
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
|
|
|
966857 |
@@ -76,10 +76,10 @@ void tst_qv4mm::multiWrappedQObjects()
|
|
|
966857 |
QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1);
|
|
|
966857 |
QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
|
|
|
966857 |
|
|
|
966857 |
- // Moves the additional WeakValue from m_multiplyWrappedQObjects to
|
|
|
966857 |
- // m_pendingFreedObjectWrapperValue. It's still alive after all.
|
|
|
966857 |
+ // The additional WeakValue from m_multiplyWrappedQObjects hasn't been moved
|
|
|
966857 |
+ // to m_pendingFreedObjectWrapperValue yet. It's still alive after all.
|
|
|
966857 |
engine1.memoryManager->runGC();
|
|
|
966857 |
- QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 2);
|
|
|
966857 |
+ QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1);
|
|
|
966857 |
|
|
|
966857 |
// engine2 doesn't own the object as engine1 was the first to wrap it above.
|
|
|
966857 |
// Therefore, no effect here.
|
|
|
966857 |
--
|
|
|
966857 |
2.35.1
|
|
|
966857 |
|