diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2023-02-10 09:07:38 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-02-11 19:33:02 +0000 |
commit | 5ea246591ce85ef7c0f906ccff96c7ac3a23b310 (patch) | |
tree | aef5655d1d67c593068fbe479ec44e975a5c84f3 | |
parent | acc496945b801feac5f99a103ed05a7ba8471fb6 (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.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4compilationunitmapper_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit.cpp | 10 | ||||
-rw-r--r-- | tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 108 |
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" |