diff options
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 55 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 15 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 10 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 4 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth_p.h | 9 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.h | 6 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm.cpp | 4 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm_p.h | 11 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 121 |
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" |