aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2018-07-20 09:22:47 -0700
committerSimon Hausmann <simon.hausmann@qt.io>2018-07-27 04:56:14 +0000
commit447e2e024609a22fe052cf458c27efdef2e3d3eb (patch)
treef2665cfdea84b9d85044a3221658eb8ce10e7d9a
parent65fd9165162a8498c5bbe71e5372b65770c240b0 (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.cpp44
-rw-r--r--src/qml/compiler/qv4compileddata_p.h4
-rw-r--r--src/qml/compiler/qv4compiler.cpp5
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp8
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp42
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()