aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-02-10 09:07:38 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-02-11 19:33:02 +0000
commit5ea246591ce85ef7c0f906ccff96c7ac3a23b310 (patch)
treeaef5655d1d67c593068fbe479ec44e975a5c84f3
parentacc496945b801feac5f99a103ed05a7ba8471fb6 (diff)
QML: Invalidate any existing cache files when saving a CU to disk
Otherwise we just re-load the old cache file next time. That's clearly not intended. Fixes: QTBUG-111078 Change-Id: Ia65b46880eca2b6e8c4792a09f20716125beada3 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 0a5eff09203ebb2547431de689d9c07e6c97f636) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp11
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h1
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp10
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp108
4 files changed, 128 insertions, 2 deletions
diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp
index 0479b8b8f1..61b0f1d497 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp
@@ -29,6 +29,11 @@ public:
s_staticUnits.insert(file, staticUnit);
}
+ void remove(const QString &file)
+ {
+ s_staticUnits.remove(file);
+ }
+
private:
QMutexLocker<QMutex> m_lock;
@@ -69,4 +74,10 @@ CompiledData::Unit *CompilationUnitMapper::get(
return data;
}
+void CompilationUnitMapper::invalidate(const QString &cacheFilePath)
+{
+ StaticUnitCache cache;
+ cache.remove(cacheFilePath);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h
index 0d3ec4eeee..07952be62c 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h
@@ -33,6 +33,7 @@ public:
CompiledData::Unit *get(
const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
+ static void invalidate(const QString &cacheFilePath);
private:
CompiledData::Unit *open(
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 744a463648..2affa4f6dd 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -819,8 +819,14 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt
return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
[&unitUrl, errorString](const char *data, quint32 size) {
- return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data,
- size, errorString);
+ const QString cachePath = localCacheFilePath(unitUrl);
+ if (CompiledData::SaveableUnitPointer::writeDataToFile(
+ cachePath, data, size, errorString)) {
+ CompilationUnitMapper::invalidate(cachePath);
+ return true;
+ }
+
+ return false;
});
}
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 19a6731ff7..7aeb23a398 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -40,6 +40,7 @@ private slots:
void cppRegisteredSingletonDependency();
void cacheModuleScripts();
void reuseStaticMappings();
+ void invalidateSaveLoadCache();
private:
QDir m_qmlCacheDirectory;
@@ -527,6 +528,7 @@ void tst_qmldiskcache::recompileAfterChange()
CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
QScopedPointer<TypeVersion2> obj(qobject_cast<TypeVersion2*>(component.create()));
QVERIFY(!obj.isNull());
+ qDebug() << obj->property("x");
QVERIFY(QFileInfo(testCompiler.cacheFilePath).lastModified() > initialCacheTimeStamp);
}
}
@@ -1007,6 +1009,112 @@ void tst_qmldiskcache::reuseStaticMappings()
QCOMPARE(testCompiler.unitData(), data1);
}
+class AParent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int x MEMBER x)
+public:
+ AParent(QObject *parent = nullptr) : QObject(parent) {}
+ int x = 25;
+};
+
+class BParent : public QObject
+{
+ Q_OBJECT
+
+ // Insert y before x, to change the property index of x
+ Q_PROPERTY(int y MEMBER y)
+
+ Q_PROPERTY(int x MEMBER x)
+public:
+ BParent(QObject *parent = nullptr) : QObject(parent) {}
+ int y = 13;
+ int x = 25;
+};
+
+static QString writeTempFile(
+ const QTemporaryDir &tempDir, const QString &fileName, const char *contents) {
+ QFile f(tempDir.path() + '/' + fileName);
+ const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ Q_ASSERT(ok);
+ f.write(contents);
+ return f.fileName();
+};
+
+void tst_qmldiskcache::invalidateSaveLoadCache()
+{
+ qmlRegisterType<AParent>("Base", 1, 0, "Parent");
+ QQmlEngine e;
+
+ // If you store a CU to a .qmlc file at run time, the .qmlc file will contain
+ // alias entries with the encodedMetaPropertyIndex pre-resolved. That's in
+ // contrast to .qmlc files generated ahead of time. Exploit that to cause
+ // a need to recompile the file.
+
+ QTemporaryDir tempDir;
+ const QString fileName = writeTempFile(
+ tempDir, QLatin1String("a.qml"),
+ "import Base\nParent { id: self; property alias z: self.x }");
+ const QUrl url = QUrl::fromLocalFile(fileName);
+ waitForFileSystem();
+
+ {
+ QQmlComponent a(&e, url);
+ QVERIFY2(a.isReady(), qPrintable(a.errorString()));
+ QScopedPointer<QObject> ao(a.create());
+ QVERIFY(!ao.isNull());
+ AParent *ap = qobject_cast<AParent *>(ao.data());
+ QCOMPARE(ap->property("z").toInt(), ap->x);
+ }
+
+ QString errorString;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> oldUnit
+ = QV4::ExecutableCompilationUnit::create();
+ QVERIFY2(oldUnit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString));
+
+ // Produce a checksum mismatch.
+ e.clearComponentCache();
+ qmlClearTypeRegistrations();
+ qmlRegisterType<BParent>("Base", 1, 0, "Parent");
+
+ {
+ QQmlComponent b(&e, url);
+ QVERIFY2(b.isReady(), qPrintable(b.errorString()));
+ QScopedPointer<QObject> bo(b.create());
+ QVERIFY(!bo.isNull());
+ BParent *bp = qobject_cast<BParent *>(bo.data());
+ QCOMPARE(bp->property("z").toInt(), bp->x);
+ }
+
+ // Make it recompile again.
+ e.clearComponentCache();
+ {
+ QFile file(fileName);
+ file.open(QIODevice::WriteOnly | QIODevice::Append);
+ file.write(" ");
+ }
+ waitForFileSystem();
+
+ {
+ QQmlComponent b(&e, url);
+ QVERIFY2(b.isReady(), qPrintable(b.errorString()));
+ QScopedPointer<QObject> bo(b.create());
+ QVERIFY(!bo.isNull());
+ BParent *bp = qobject_cast<BParent *>(bo.data());
+ QCOMPARE(bp->property("z").toInt(), bp->x);
+ }
+
+ // Verify that the mapped unit data is actually different now.
+ // The cache should have been invalidated after all.
+ // So, now we should be able to load a freshly written CU.
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
+ = QV4::ExecutableCompilationUnit::create();
+ QVERIFY2(unit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString));
+
+ QVERIFY(unit->unitData() != oldUnit->unitData());
+}
+
QTEST_MAIN(tst_qmldiskcache)
#include "tst_qmldiskcache.moc"