diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2018-07-20 09:22:47 -0700 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-07-27 04:56:14 +0000 |
commit | 447e2e024609a22fe052cf458c27efdef2e3d3eb (patch) | |
tree | f2665cfdea84b9d85044a3221658eb8ce10e7d9a | |
parent | 65fd9165162a8498c5bbe71e5372b65770c240b0 (diff) |
Try to load QML cache from CacheLocation if side-by-side fails
This could happen if the .qmlc file is stale or corrupt for some reason.
In that case, we should try to load a cache file from the user's
CacheLocation.
Change-Id: Id2be776c7ae0467c9d9ffffd1543204272a531d1
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 44 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 4 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 5 | ||||
-rw-r--r-- | tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 8 | ||||
-rw-r--r-- | tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 42 |
5 files changed, 82 insertions, 21 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 02aee47eea..0c81a5b3c6 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -76,7 +76,18 @@ namespace QV4 { namespace CompiledData { +#if defined(QML_COMPILE_HASH) +# ifdef Q_OS_LINUX +// Place on a separate section on Linux so it's easier to check from outside +// what the hash version is. +__attribute__((section(".qml_compile_hash"))) +# endif +const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH; static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); +#else +# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" +#endif + CompilationUnit::CompilationUnit(const Unit *unitData) { @@ -351,26 +362,27 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); - QString cachePath = sourcePath + QLatin1Char('c'); - if (!QFile::exists(cachePath)) - cachePath = localCacheFilePath(url); + const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) }; + for (const QString &cachePath : cachePaths) { + CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString); + if (!mappedUnit) + continue; - CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString); - if (!mappedUnit) - return false; + const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; + QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); - const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { + *errorString = QStringLiteral("QML source file has moved to a different location."); + continue; + } - if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - return false; + dataPtrChange.commit(); + free(const_cast<Unit*>(oldDataPtr)); + backingFile.reset(cacheFile.take()); + return true; } - dataPtrChange.commit(); - free(const_cast<Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; + return false; } void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) @@ -783,7 +795,7 @@ bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) } #if defined(QML_COMPILE_HASH) - if (qstrcmp(QML_COMPILE_HASH, libraryVersionHash) != 0) { + if (qstrcmp(CompiledData::qml_compile_hash, libraryVersionHash) != 0) { *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); return false; } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index c1be00ea27..0ac40d2800 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -752,7 +752,9 @@ struct Import }; static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +enum { QmlCompileHashSpace = 48 }; static const char magic_str[] = "qv4cdata"; +extern const char qml_compile_hash[QmlCompileHashSpace + 1]; struct Unit { @@ -764,7 +766,7 @@ struct Unit quint32_le unitSize; // Size of the Unit and any depending data. // END DO NOT CHANGE THESE FIELDS EVER - char libraryVersionHash[48]; + char libraryVersionHash[QmlCompileHashSpace]; char md5Checksum[16]; // checksum of all bytes following this field. void generateChecksum(); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index f1afad4965..76779fece8 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -48,9 +48,6 @@ #include <wtf/MathExtras.h> #include <QCryptographicHash> -// generated by qmake: -#include "qml_compile_hash_p.h" - QV4::Compiler::StringTableGenerator::StringTableGenerator() { clear(); @@ -482,7 +479,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.flags |= module->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; - qstrcpy(unit.libraryVersionHash, QML_COMPILE_HASH); + qstrcpy(unit.libraryVersionHash, CompiledData::qml_compile_hash); memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 48eb694167..60717dfafa 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -32,6 +32,7 @@ #include <QQmlEngine> #include <QProcess> #include <QLibraryInfo> +#include <QStandardPaths> #include <QSysInfo> #include <QLoggingCategory> #include <private/qqmlcomponent_p.h> @@ -105,6 +106,13 @@ static bool generateCache(const QString &qmlFileName, QByteArray *capturedStderr void tst_qmlcachegen::initTestCase() { qputenv("QML_FORCE_DISK_CACHE", "1"); + QStandardPaths::setTestModeEnabled(true); + + // make sure there's no pre-existing cache dir + QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + if (!cacheDir.isEmpty()) + //QDir(cacheDir).removeRecursively(); + qDebug() << cacheDir; } void tst_qmlcachegen::loadGeneratedFile() diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index d363180c06..284a324227 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -48,6 +48,7 @@ class tst_qmldiskcache: public QObject private slots: void initTestCase(); + void loadLocalAsFallback(); void regenerateAfterChange(); void registerImportForImplicitComponent(); void basicVersionChecks(); @@ -204,6 +205,47 @@ struct TestCompiler void tst_qmldiskcache::initTestCase() { qputenv("QML_FORCE_DISK_CACHE", "1"); + QStandardPaths::setTestModeEnabled(true); +} + +void tst_qmldiskcache::loadLocalAsFallback() +{ + QQmlEngine engine; + TestCompiler testCompiler(&engine); + + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + "}"); + + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + // Create an invalid side-by-side .qmlc + { + QFile f(testCompiler.tempDir.path() + "/test.qmlc"); + QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); + QV4::CompiledData::Unit unit = {}; + memcpy(unit.magic, QV4::CompiledData::magic_str, sizeof(unit.magic)); + unit.version = QV4_DATA_STRUCTURE_VERSION; + unit.qtVersion = QT_VERSION; + unit.sourceTimeStamp = testCompiler.mappedFile.fileTime(QFile::FileModificationTime).toMSecsSinceEpoch(); + unit.unitSize = ~0U; // make the size a silly number + // write something to the library hash that should cause it not to be loaded + memset(unit.libraryVersionHash, 'z', sizeof(unit.libraryVersionHash)); + memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); + + // leave the other fields unset, since they don't matter + + f.write(reinterpret_cast<const char *>(&unit), sizeof(unit)); + } + + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); + bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), QFileInfo(testCompiler.testFilePath).lastModified(), + &testCompiler.lastErrorString); + QVERIFY2(loaded, qPrintable(testCompiler.lastErrorString)); + QCOMPARE(unit->objectCount(), 1); } void tst_qmldiskcache::regenerateAfterChange() |