aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2016-07-21 16:30:57 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2016-07-23 05:18:59 +0000
commit4c1a51006e5936dc69e3373539787120092f6719 (patch)
treea8979a9e61604958bce581ffd38361b0265510c3
parent8a8b826cad197cf39e50f88521d0c40dc9c9344d (diff)
Improve robustness of qml disk caching
Perform various basic checks before proceeding to load an existing cache file, including the qt version, architecture, data structure version and others. Change-Id: Ie822b056e944ac120643aad260e97f62616688bf Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
-rw-r--r--src/qml/compiler/qv4compileddata.cpp55
-rw-r--r--src/qml/compiler/qv4compileddata_p.h15
-rw-r--r--src/qml/compiler/qv4compiler.cpp10
-rw-r--r--src/qml/compiler/qv4compiler_p.h3
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp4
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h9
-rw-r--r--src/qml/compiler/qv4isel_p.cpp3
-rw-r--r--src/qml/compiler/qv4isel_p.h6
-rw-r--r--src/qml/jit/qv4isel_masm.cpp4
-rw-r--r--src/qml/jit/qv4isel_masm_p.h11
-rw-r--r--src/qml/qml/qqmltypeloader.cpp4
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp121
13 files changed, 210 insertions, 37 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 1bcd6892d2..965924262d 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -331,20 +331,56 @@ bool CompilationUnit::saveToDisk(QString *errorString)
return true;
}
-bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString)
+bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString)
{
if (!url.isLocalFile()) {
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
- QScopedPointer<QFile> cacheFile(new QFile(url.toLocalFile() + QLatin1Char('c')));
+ const QString sourcePath = url.toLocalFile();
+ QScopedPointer<QFile> cacheFile(new QFile(sourcePath + QLatin1Char('c')));
if (!cacheFile->open(QIODevice::ReadOnly)) {
*errorString = cacheFile->errorString();
return false;
}
+ {
+ CompiledData::Unit header;
+ qint64 bytesRead = cacheFile->read(reinterpret_cast<char *>(&header), sizeof(header));
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return false;
+ }
+
+ if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) {
+ *errorString = QStringLiteral("Magic bytes in the header do not match");
+ return false;
+ }
+
+ if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
+ *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
+ return false;
+ }
+
+ if (header.qtVersion != quint32(QT_VERSION)) {
+ *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
+ return false;
+ }
+
+ {
+ QFileInfo sourceCode(sourcePath);
+ if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) {
+ *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
+ return false;
+ }
+ }
+
+ }
+ // Data structure and qt version matched, so now we can access the rest of the file safely.
+
uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size());
if (!cacheData) {
*errorString = cacheFile->errorString();
@@ -354,13 +390,22 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString)
QScopedValueRollback<const Unit *> dataPtrChange(data, reinterpret_cast<const Unit *>(cacheData));
{
- QFileInfo sourceCode(url.toLocalFile());
- if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != data->sourceTimeStamp) {
- *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
+ const QString foundArchitecture = stringAt(data->architectureIndex);
+ const QString expectedArchitecture = QSysInfo::buildAbi();
+ if (foundArchitecture != expectedArchitecture) {
+ *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture);
return false;
}
}
+ {
+ const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex);
+ const QString expectedCodeGenerator = iselFactory->codeGeneratorName;
+ if (foundCodeGenerator != expectedCodeGenerator) {
+ *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator);
+ return false;
+ }
+ }
if (!memoryMapCode(errorString))
return false;
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 20b68026e9..4153259760 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -70,6 +70,9 @@
QT_BEGIN_NAMESPACE
+// Bump this whenever the compiler data structures change in an incompatible way.
+#define QV4_DATA_STRUCTURE_VERSION 0x01
+
class QIODevice;
class QQmlPropertyCache;
class QQmlPropertyData;
@@ -88,6 +91,7 @@ struct Function;
}
struct Function;
+class EvalISelFactory;
namespace CompiledData {
@@ -597,11 +601,16 @@ static const char magic_str[] = "qv4cdata";
struct Unit
{
+ // DO NOT CHANGE THESE FIELDS EVER
char magic[8];
- LEInt16 architecture;
- LEInt16 version;
+ LEUInt32 version;
+ LEUInt32 qtVersion;
LEInt64 sourceTimeStamp;
LEUInt32 unitSize; // Size of the Unit and any depending data.
+ // END DO NOT CHANGE THESE FIELDS EVER
+
+ LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi()
+ LEUInt32 codeGeneratorIndex;
enum : unsigned int {
IsJavascript = 0x1,
@@ -880,7 +889,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
void destroy() Q_DECL_OVERRIDE;
bool saveToDisk(QString *errorString);
- bool loadFromDisk(const QUrl &url, QString *errorString);
+ bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString);
protected:
virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0;
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 5d13734247..768a4ffcd3 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -352,17 +352,19 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i
}
}
-QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset) const
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset)
{
CompiledData::Unit unit;
memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic));
- unit.architecture = 0; // ###
unit.flags = QV4::CompiledData::Unit::IsJavascript;
- unit.version = 1;
- unit.functionTableSize = irModule->functions.size();
+ unit.version = QV4_DATA_STRUCTURE_VERSION;
+ unit.qtVersion = QT_VERSION;
+ unit.architectureIndex = registerString(QSysInfo::buildAbi());
+ unit.codeGeneratorIndex = registerString(codeGeneratorName);
quint32 nextOffset = sizeof(CompiledData::Unit);
+ unit.functionTableSize = irModule->functions.size();
unit.offsetToFunctionTable = nextOffset;
nextOffset += unit.functionTableSize * sizeof(uint);
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 4b67fe8600..49b8664513 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -118,8 +118,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
void writeFunction(char *f, IR::Function *irFunction) const;
StringTableGenerator stringTable;
+ QString codeGeneratorName;
private:
- CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset) const;
+ CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset);
IR::Module *irModule;
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index e95a7f7046..112003ebb8 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -174,6 +174,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
namespace Moth {
+ // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
+
struct Param {
// Params are looked up as follows:
// Constant: 0
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index 283948727b..dc803f1ee2 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -313,8 +313,8 @@ protected:
};
} // anonymous namespace
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- : EvalInstructionSelection(execAllocator, module, jsGenerator)
+InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
+ : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
, qmlEngine(qmlEngine)
, _block(0)
, _codeStart(0)
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
index eaca299c94..ea323497ef 100644
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ b/src/qml/compiler/qv4isel_moth_p.h
@@ -80,7 +80,7 @@ class Q_QML_EXPORT InstructionSelection:
public EvalInstructionSelection
{
public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator);
+ InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
~InstructionSelection();
virtual void run(int functionIndex);
@@ -205,10 +205,11 @@ private:
class Q_QML_EXPORT ISelFactory: public EvalISelFactory
{
public:
+ ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {}
virtual ~ISelFactory() {}
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); }
- virtual bool jitCompileRegexps() const
+ EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); }
+ bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL
{ return false; }
QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE;
diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp
index 0ae08160ab..d97eec5e1d 100644
--- a/src/qml/compiler/qv4isel_p.cpp
+++ b/src/qml/compiler/qv4isel_p.cpp
@@ -52,7 +52,7 @@
using namespace QV4;
using namespace QV4::IR;
-EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
+EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
: useFastLookups(true)
, useTypeInference(true)
, executableAllocator(execAllocator)
@@ -67,6 +67,7 @@ EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *exe
Q_ASSERT(execAllocator);
#endif
Q_ASSERT(module);
+ jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName;
}
EvalInstructionSelection::~EvalInstructionSelection()
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
index d93c0893ae..a3fa80b6f0 100644
--- a/src/qml/compiler/qv4isel_p.h
+++ b/src/qml/compiler/qv4isel_p.h
@@ -65,13 +65,14 @@ class QQmlEnginePrivate;
namespace QV4 {
+class EvalISelFactory;
class ExecutableAllocator;
struct Function;
class Q_QML_PRIVATE_EXPORT EvalInstructionSelection
{
public:
- EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator);
+ EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
virtual ~EvalInstructionSelection() = 0;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true);
@@ -104,10 +105,13 @@ protected:
class Q_QML_PRIVATE_EXPORT EvalISelFactory
{
public:
+ EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {}
virtual ~EvalISelFactory() = 0;
virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0;
virtual bool jitCompileRegexps() const = 0;
virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0;
+
+ const QString codeGeneratorName;
};
namespace IR {
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
index bde2c59526..4066ab213c 100644
--- a/src/qml/jit/qv4isel_masm.cpp
+++ b/src/qml/jit/qv4isel_masm.cpp
@@ -258,8 +258,8 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
return codeRef;
}
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator)
- : EvalInstructionSelection(execAllocator, module, jsGenerator)
+InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
+ : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
, _block(0)
, _as(0)
, compilationUnit(new CompilationUnit)
diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h
index 7616ba147f..4b35a72e01 100644
--- a/src/qml/jit/qv4isel_masm_p.h
+++ b/src/qml/jit/qv4isel_masm_p.h
@@ -76,7 +76,7 @@ class Q_QML_EXPORT InstructionSelection:
public EvalInstructionSelection
{
public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator);
+ InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
~InstructionSelection();
virtual void run(int functionIndex);
@@ -285,12 +285,13 @@ private:
class Q_QML_EXPORT ISelFactory: public EvalISelFactory
{
public:
+ ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {}
virtual ~ISelFactory() {}
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); }
- virtual bool jitCompileRegexps() const
+ EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); }
+ bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL
{ return true; }
- QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE;
+ QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL;
};
} // end of namespace JIT
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 52fed14ecb..851a18fa5f 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -2057,7 +2057,7 @@ bool QQmlTypeData::tryLoadFromDiskCache()
QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
{
QString error;
- if (!unit->loadFromDisk(url(), &error)) {
+ if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) {
qDebug() << "Error loading" << url().toString() << "from disk cache:" << error;
return false;
}
@@ -2840,7 +2840,7 @@ void QQmlScriptBlob::dataReceived(const Data &data)
if (diskCache() && !forceDiskCacheRefresh()) {
QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
QString error;
- if (unit->loadFromDisk(url(), &error)) {
+ if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) {
initializeFromCompilationUnit(unit);
return;
} else {
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index bb78f2856f..1fe63bb99a 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -29,6 +29,11 @@
#include <qtest.h>
#include <private/qv4compileddata_p.h>
+#include <private/qv4compiler_p.h>
+#include <private/qv4jsir_p.h>
+#include <private/qv4isel_p.h>
+#include <private/qv8engine_p.h>
+#include <private/qv4engine_p.h>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QThread>
@@ -42,6 +47,7 @@ private slots:
void regenerateAfterChange();
void registerImportForImplicitComponent();
+ void basicVersionChecks();
};
struct TestCompiler
@@ -58,11 +64,8 @@ struct TestCompiler
bool compile(const QByteArray &contents)
{
- if (currentMapping) {
- mappedFile.unmap(currentMapping);
- currentMapping = nullptr;
- }
- mappedFile.close();
+ closeMapping();
+ engine->clearComponentCache();
// Qt API limits the precision of QFileInfo::modificationTime() to seconds, so to ensure that
// the newly written file has a modification date newer than an existing cache file, we must
@@ -106,6 +109,44 @@ struct TestCompiler
return unitPtr;
}
+ typedef void (*HeaderTweakFunction)(QV4::CompiledData::Unit *header);
+ bool tweakHeader(HeaderTweakFunction function)
+ {
+ closeMapping();
+
+ QFile f(cacheFilePath);
+ if (!f.open(QIODevice::ReadWrite))
+ return false;
+ QV4::CompiledData::Unit header;
+ if (f.read(reinterpret_cast<char *>(&header), sizeof(header)) != sizeof(header))
+ return false;
+ function(&header);
+ f.seek(0);
+ return f.write(reinterpret_cast<const char *>(&header), sizeof(header)) == sizeof(header);
+ }
+
+ bool verify()
+ {
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), v4->iselFactory.data(), &lastErrorString);
+ }
+
+ void closeMapping()
+ {
+ if (currentMapping) {
+ mappedFile.unmap(currentMapping);
+ currentMapping = nullptr;
+ }
+ mappedFile.close();
+ }
+
+ void clearCache()
+ {
+ closeMapping();
+ QFile::remove(cacheFilePath);
+ }
+
QQmlEngine *engine;
const QTemporaryDir tempDir;
const QString testFilePath;
@@ -151,8 +192,6 @@ void tst_qmldiskcache::regenerateAfterChange()
QVERIFY(bindingFunction->codeOffset > testUnit->unitSize);
}
- engine.clearComponentCache();
-
{
const QByteArray newContents = QByteArrayLiteral("import QtQml 2.0\n"
"QtObject {\n"
@@ -214,6 +253,74 @@ void tst_qmldiskcache::registerImportForImplicitComponent()
}
}
+void tst_qmldiskcache::basicVersionChecks()
+{
+ 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"
+ "}");
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+ QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString));
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->qtVersion = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Qt version mismatch. Found 0 expected %1").arg(QT_VERSION, 0, 16));
+ testCompiler.clearCache();
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->version = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("V4 data structure version mismatch. Found 0 expected %1").arg(QV4_DATA_STRUCTURE_VERSION, 0, 16));
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->architectureIndex = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Architecture mismatch. Found expected %1").arg(QSysInfo::buildAbi()));
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->codeGeneratorIndex = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Code generator mismatch. Found code generated by but expected %1").arg(QV8Engine::getV4(&engine)->iselFactory->codeGeneratorName));
+ }
+}
+
QTEST_MAIN(tst_qmldiskcache)
#include "tst_qmldiskcache.moc"