5c5ecc4
From 98358715930739ca8de172d88c5ce6941c275ff3 Mon Sep 17 00:00:00 2001
5c5ecc4
From: Simon Hausmann <simon.hausmann@qt.io>
5c5ecc4
Date: Tue, 12 Sep 2017 15:13:33 +0200
5c5ecc4
Subject: [PATCH 111/153] Fix qml cache invalidation when changing dependent
5c5ecc4
 C++ registered QML singletons
5c5ecc4
5c5ecc4
When a qml file uses a qml singleton, we need to reliably detect when
5c5ecc4
the singleton changes and re-generate the cache of the qml file using
5c5ecc4
it. This is a scenario covered and fixed by commit
5c5ecc4
5b94de09cc738837d1539e28b3c0dccd17c18d29, with the exception that
5c5ecc4
currently QML singletons registered via qmlRegisterSingleton were not
5c5ecc4
added to the list of dependent singletons for a qml file. We can fix
5c5ecc4
this by extending findCompositeSingletons() to also cover the singletons
5c5ecc4
that do not originate from a qmldir file.
5c5ecc4
5c5ecc4
[ChangeLog][Qt][Qml] Fixed bug where sometimes changes to a qml
5c5ecc4
singleton would not propagate to the users or cause crashes.
5c5ecc4
5c5ecc4
Task-number: QTBUG-62243
5c5ecc4
Change-Id: I16c3d9ba65fd82e898a29b946c341907751135a9
5c5ecc4
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
5c5ecc4
---
5c5ecc4
 src/qml/qml/qqmlimport.cpp                       | 11 +++++
5c5ecc4
 src/qml/qml/qqmlmetatype.cpp                     | 12 +++++
5c5ecc4
 src/qml/qml/qqmlmetatype_p.h                     |  2 +
5c5ecc4
 tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 61 ++++++++++++++++++++++++
5c5ecc4
 4 files changed, 86 insertions(+)
5c5ecc4
5c5ecc4
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
5c5ecc4
index 0bd731747..ccd287e1b 100644
5c5ecc4
--- a/src/qml/qml/qqmlimport.cpp
5c5ecc4
+++ b/src/qml/qml/qqmlimport.cpp
5c5ecc4
@@ -471,6 +471,17 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList
5c5ecc4
                 resultList.append(ref);
5c5ecc4
             }
5c5ecc4
         }
5c5ecc4
+
5c5ecc4
+        if (QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion)) {
5c5ecc4
+            module->walkCompositeSingletons([&resultList, &set](const QQmlType &singleton) {
5c5ecc4
+                QQmlImports::CompositeSingletonReference ref;
5c5ecc4
+                ref.typeName = singleton.elementName();
5c5ecc4
+                ref.prefix = set.prefix;
5c5ecc4
+                ref.majorVersion = singleton.majorVersion();
5c5ecc4
+                ref.minorVersion = singleton.minorVersion();
5c5ecc4
+                resultList.append(ref);
5c5ecc4
+            });
5c5ecc4
+        }
5c5ecc4
     }
5c5ecc4
 }
5c5ecc4
 
5c5ecc4
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
5c5ecc4
index 5bbc250d5..8f5d11a96 100644
5c5ecc4
--- a/src/qml/qml/qqmlmetatype.cpp
5c5ecc4
+++ b/src/qml/qml/qqmlmetatype.cpp
5c5ecc4
@@ -1258,6 +1258,18 @@ QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const
5c5ecc4
     return QQmlType();
5c5ecc4
 }
5c5ecc4
 
5c5ecc4
+void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const
5c5ecc4
+{
5c5ecc4
+    QMutexLocker lock(metaTypeDataLock());
5c5ecc4
+    for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end();
5c5ecc4
+         typeCandidates != end; ++typeCandidates) {
5c5ecc4
+        for (auto type: typeCandidates.value()) {
5c5ecc4
+            if (type->regType == QQmlType::CompositeSingletonType)
5c5ecc4
+                callback(QQmlType(type));
5c5ecc4
+        }
5c5ecc4
+    }
5c5ecc4
+}
5c5ecc4
+
5c5ecc4
 QQmlTypeModuleVersion::QQmlTypeModuleVersion()
5c5ecc4
 : m_module(0), m_minor(0)
5c5ecc4
 {
5c5ecc4
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
5c5ecc4
index ac2133ba3..9a7736ffc 100644
5c5ecc4
--- a/src/qml/qml/qqmlmetatype_p.h
5c5ecc4
+++ b/src/qml/qml/qqmlmetatype_p.h
5c5ecc4
@@ -298,6 +298,8 @@ public:
5c5ecc4
     QQmlType type(const QHashedStringRef &, int) const;
5c5ecc4
     QQmlType type(const QV4::String *, int) const;
5c5ecc4
 
5c5ecc4
+    void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const;
5c5ecc4
+
5c5ecc4
     QQmlTypeModulePrivate *priv() { return d; }
5c5ecc4
 private:
5c5ecc4
     //Used by register functions and creates the QQmlTypeModule for them
5c5ecc4
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
5c5ecc4
index 6ab84774f..e75e51ed2 100644
5c5ecc4
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
5c5ecc4
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
5c5ecc4
@@ -59,6 +59,7 @@ private slots:
5c5ecc4
     void cacheResources();
5c5ecc4
     void stableOrderOfDependentCompositeTypes();
5c5ecc4
     void singletonDependency();
5c5ecc4
+    void cppRegisteredSingletonDependency();
5c5ecc4
 };
5c5ecc4
 
5c5ecc4
 // A wrapper around QQmlComponent to ensure the temporary reference counts
5c5ecc4
@@ -790,6 +791,66 @@ void tst_qmldiskcache::singletonDependency()
5c5ecc4
     }
5c5ecc4
 }
5c5ecc4
 
5c5ecc4
+void tst_qmldiskcache::cppRegisteredSingletonDependency()
5c5ecc4
+{
5c5ecc4
+    qmlClearTypeRegistrations();
5c5ecc4
+    QScopedPointer<QQmlEngine> engine(new QQmlEngine);
5c5ecc4
+
5c5ecc4
+    QTemporaryDir tempDir;
5c5ecc4
+    QVERIFY(tempDir.isValid());
5c5ecc4
+
5c5ecc4
+    const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) {
5c5ecc4
+        QFile f(tempDir.path() + '/' + fileName);
5c5ecc4
+        const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate);
5c5ecc4
+        Q_ASSERT(ok);
5c5ecc4
+        f.write(contents);
5c5ecc4
+        return f.fileName();
5c5ecc4
+    };
5c5ecc4
+
5c5ecc4
+    writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 42 }");
5c5ecc4
+
5c5ecc4
+    qmlRegisterSingletonType(QUrl::fromLocalFile(tempDir.path() + QLatin1String("/MySingleton.qml")), "CppRegisteredSingletonDependency", 1, 0, "Singly");
5c5ecc4
+
5c5ecc4
+    const QString testFilePath = writeTempFile("main.qml", "import QtQml 2.0\nimport CppRegisteredSingletonDependency 1.0\nQtObject {\n"
5c5ecc4
+                                                           "    function getValue() { return Singly.value; }\n"
5c5ecc4
+                                                           "}");
5c5ecc4
+
5c5ecc4
+    {
5c5ecc4
+        CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath));
5c5ecc4
+        QScopedPointer<QObject> obj(component.create());
5c5ecc4
+        QVERIFY(!obj.isNull());
5c5ecc4
+        QVariant value;
5c5ecc4
+        QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value)));
5c5ecc4
+        QCOMPARE(value.toInt(), 42);
5c5ecc4
+    }
5c5ecc4
+
5c5ecc4
+    const QString testFileCachePath = testFilePath + QLatin1Char('c');
5c5ecc4
+    QVERIFY(QFile::exists(testFileCachePath));
5c5ecc4
+    QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
5c5ecc4
+
5c5ecc4
+    engine.reset(new QQmlEngine);
5c5ecc4
+    waitForFileSystem();
5c5ecc4
+
5c5ecc4
+    writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 100 }");
5c5ecc4
+    waitForFileSystem();
5c5ecc4
+
5c5ecc4
+    {
5c5ecc4
+        CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath));
5c5ecc4
+        QScopedPointer<QObject> obj(component.create());
5c5ecc4
+        QVERIFY(!obj.isNull());
5c5ecc4
+
5c5ecc4
+        {
5c5ecc4
+            QVERIFY(QFile::exists(testFileCachePath));
5c5ecc4
+            QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
5c5ecc4
+            QVERIFY2(newCacheTimeStamp > initialCacheTimeStamp, qPrintable(newCacheTimeStamp.toString()));
5c5ecc4
+        }
5c5ecc4
+
5c5ecc4
+        QVariant value;
5c5ecc4
+        QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value)));
5c5ecc4
+        QCOMPARE(value.toInt(), 100);
5c5ecc4
+    }
5c5ecc4
+}
5c5ecc4
+
5c5ecc4
 QTEST_MAIN(tst_qmldiskcache)
5c5ecc4
 
5c5ecc4
 #include "tst_qmldiskcache.moc"
5c5ecc4
-- 
5c5ecc4
2.14.3
5c5ecc4