Blame SOURCES/0009-Fix-sweep-step-for-tainted-QObject-JavaScript-wrappe.patch

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