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

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