/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv4compileddata_p.h" #include #include #include #include #include #include #include // generated by qmake: #include "qml_compile_hash_p.h" #include QT_BEGIN_NAMESPACE 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, const QString &fileName, const QString &finalUrlString) { setUnitData(unitData, nullptr, fileName, finalUrlString); } CompilationUnit::~CompilationUnit() { if (data) { if (data->qmlUnit() != qmlData) free(const_cast(qmlData)); qmlData = nullptr; if (!(data->flags & QV4::CompiledData::Unit::StaticData)) free(const_cast(data)); } data = nullptr; #if Q_BYTE_ORDER == Q_BIG_ENDIAN delete [] constants; constants = nullptr; #endif delete [] imports; imports = nullptr; } bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString) const { errorString->clear(); #if QT_CONFIG(temporaryfile) // Foo.qml -> Foo.qmlc QSaveFile cacheFile(outputFileName); if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { *errorString = cacheFile.errorString(); return false; } SaveableUnitPointer saveable(this); qint64 headerWritten = cacheFile.write(saveable.data(), saveable.size()); if (headerWritten != saveable.size()) { *errorString = cacheFile.errorString(); return false; } if (!cacheFile.commit()) { *errorString = cacheFile.errorString(); return false; } return true; #else Q_UNUSED(outputFileName) *errorString = QStringLiteral("features.temporaryfile is disabled."); return false; #endif // QT_CONFIG(temporaryfile) } void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit, const QString &fileName, const QString &finalUrlString) { data = unitData; qmlData = nullptr; #if Q_BYTE_ORDER == Q_BIG_ENDIAN delete [] constants; #endif constants = nullptr; m_fileName.clear(); m_finalUrlString.clear(); if (!data) return; qmlData = qmlUnit ? qmlUnit : data->qmlUnit(); #if Q_BYTE_ORDER == Q_BIG_ENDIAN StaticValue *bigEndianConstants = new StaticValue[data->constantTableSize]; const quint64_le *littleEndianConstants = data->constants(); for (uint i = 0; i < data->constantTableSize; ++i) bigEndianConstants[i] = StaticValue::fromReturnedValue(littleEndianConstants[i]); constants = bigEndianConstants; #else constants = reinterpret_cast(data->constants()); #endif m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex); m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex); } //reverse of Lexer::singleEscape() QString Binding::escapedString(const QString &string) { QString tmp = QLatin1String("\""); for (int i = 0; i < string.length(); ++i) { const QChar &c = string.at(i); switch (c.unicode()) { case 0x08: tmp += QLatin1String("\\b"); break; case 0x09: tmp += QLatin1String("\\t"); break; case 0x0A: tmp += QLatin1String("\\n"); break; case 0x0B: tmp += QLatin1String("\\v"); break; case 0x0C: tmp += QLatin1String("\\f"); break; case 0x0D: tmp += QLatin1String("\\r"); break; case 0x22: tmp += QLatin1String("\\\""); break; case 0x27: tmp += QLatin1String("\\\'"); break; case 0x5C: tmp += QLatin1String("\\\\"); break; default: tmp += c; break; } } tmp += QLatin1Char('\"'); return tmp; } void CompilationUnit::unlink() { free(runtimeStrings); runtimeStrings = nullptr; delete [] runtimeRegularExpressions; runtimeRegularExpressions = nullptr; free(runtimeClasses); runtimeClasses = nullptr; } void Unit::generateChecksum() { #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 QCryptographicHash hash(QCryptographicHash::Md5); const int checksummableDataOffset = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum); const char *dataPtr = reinterpret_cast(this) + checksummableDataOffset; hash.addData(dataPtr, unitSize - checksummableDataOffset); QByteArray checksum = hash.result(); Q_ASSERT(checksum.size() == sizeof(md5Checksum)); memcpy(md5Checksum, checksum.constData(), sizeof(md5Checksum)); #else memset(md5Checksum, 0, sizeof(md5Checksum)); #endif } bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const { if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) { *errorString = QStringLiteral("Magic bytes in the header do not match"); return false; } if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) { *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); return false; } if (qtVersion != quint32(QT_VERSION)) { *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16); return false; } if (sourceTimeStamp) { // Files from the resource system do not have any time stamps, so fall back to the application // executable. if (!expectedSourceTimeStamp.isValid()) expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); if (expectedSourceTimeStamp.isValid() && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) { *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); return false; } } #if defined(QML_COMPILE_HASH) if (qstrcmp(CompiledData::qml_compile_hash, libraryVersionHash) != 0) { *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); return false; } #else #error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" #endif return true; } } } QT_END_NAMESPACE