at master 5.5 kB view raw
1diff --git a/src/qml/common/qv4compileddata.cpp b/src/qml/common/qv4compileddata.cpp 2index 9dee91f713..9dec5cae67 100644 3--- a/src/qml/common/qv4compileddata.cpp 4+++ b/src/qml/common/qv4compileddata.cpp 5@@ -15,6 +15,8 @@ 6 #include <QtCore/qscopeguard.h> 7 #include <QtCore/qstandardpaths.h> 8 9+#include <QtCore/qcoreapplication.h> 10+ 11 static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH); 12 13 #if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0 14@@ -26,6 +28,35 @@ __attribute__((section(".qml_compile_hash"))) 15 const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH; 16 static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH, 17 "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); 18+ 19+bool nix__isNixApplication() { 20+ static const bool value = QCoreApplication::applicationFilePath().startsWith(QStringLiteral("@nixStore@")); 21+ return value; 22+} 23+ 24+static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > 25+ /*sha1*/ 20 + /*NIX:*/ 4, 26+ "Nix compile hash length exceeds the reserved space in data " 27+ "structure. Please review the patch."); 28+ 29+const QByteArray &nix__applicationHash() { 30+ static const QByteArray value = [](){ 31+ QCryptographicHash applicationHash(QCryptographicHash::Sha1); 32+ applicationHash.addData(QByteArrayView(qml_compile_hash, QML_COMPILE_HASH_LENGTH)); 33+ 34+ // We only care about the package, not the specific file path. 35+ auto view = QCoreApplication::applicationFilePath().sliced(@nixStoreLength@); 36+ auto pkgEndIdx = view.indexOf(QStringLiteral("/")); 37+ if (pkgEndIdx != -1) view = view.sliced(0, pkgEndIdx); 38+ 39+ applicationHash.addData(view.toUtf8()); 40+ 41+ return QByteArray("NIX:") + applicationHash.result(); 42+ }(); 43+ 44+ return value; 45+} 46+ 47 #else 48 # error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" 49 #endif 50@@ -69,13 +100,29 @@ bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) 51 } 52 53 #if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0 54- if (qstrncmp(qml_compile_hash, libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) { 55+ const bool nixUnit = qstrncmp("NIX:", this->libraryVersionHash, 4) == 0; 56+ 57+ if (nixUnit && !nix__isNixApplication()) { 58+ *errorString = QStringLiteral("QML compile hash is for a nix store application."); 59+ return false; 60+ } 61+ 62+ const char *targetHash = qml_compile_hash; 63+ size_t targetHashLength = QML_COMPILE_HASH_LENGTH; 64+ 65+ if (nixUnit) { 66+ const auto &applicationHash = nix__applicationHash(); 67+ targetHash = applicationHash.constData(); 68+ targetHashLength = applicationHash.length(); 69+ } 70+ 71+ if (qstrncmp(targetHash, this->libraryVersionHash, targetHashLength) != 0) { 72 *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2") 73 .arg(QString::fromLatin1( 74- QByteArray(libraryVersionHash, QML_COMPILE_HASH_LENGTH) 75+ QByteArray(this->libraryVersionHash, targetHashLength) 76 .toPercentEncoding()), 77 QString::fromLatin1( 78- QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH) 79+ QByteArray(targetHash, targetHashLength) 80 .toPercentEncoding())); 81 return false; 82 } 83@@ -213,6 +260,29 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) 84 return false; 85 } 86 87+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0 88+ if (nix__isNixApplication() && unitUrl.scheme() == QStringLiteral("qrc")) { 89+ // If the application is running from the nix store, we can safely save 90+ // bytecode for its embedded QML files as long as we hash the 91+ // application path into the version. This will invalidate the caches 92+ // when the store path changes. 93+ const auto &applicationHash = nix__applicationHash(); 94+ 95+ memcpy(const_cast<char *>(unitData()->libraryVersionHash), 96+ applicationHash.constData(), applicationHash.length()); 97+ } else if (unitUrl.path().startsWith(QStringLiteral("@nixStore@"))) { 98+ // We don't store bytecode for bare QML files in the nix store as the 99+ // paths will change every time the application updates, filling caches 100+ // endlessly with junk. 101+ *errorString = QStringLiteral("Refusing to save bytecode for bare @nixStore@ path."); 102+ return false; 103+ } else { 104+ // If the QML file is loaded from a normal file path it doesn't matter 105+ // if the application itself is running from a nix path, so we fall back 106+ // to the default Qt behavior. 107+ } 108+#endif 109+ 110 return SaveableUnitPointer(unitData()).saveToDisk<char>( 111 [&unitUrl, errorString](const char *data, quint32 size) { 112 const QString cachePath = localCacheFilePath(unitUrl);