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

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