diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2023-12-21 16:53:36 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-01-10 11:22:37 +0100 |
commit | be6d1499af75228341227d15441284e07cfe1e41 (patch) | |
tree | 99c5b1e3dc7f1b99d8c141f1b8138feacd29e020 /src/qml/common | |
parent | cfdc612c3022b3f35545fd5e4e0bcd2661f657f1 (diff) |
QtQml: Always link executable CU on creation
We don't want floating unlinked executable CUs. They should always be
tied to an engine, and the engine should not change. This gives us one
definite point where to register them with the engine (to be done in
subsequent change).
Unfortunately, due to the refcounting, we need to remove the engine from
any still-referenced CUs when the engine itself is destructed. We will
be able to drop the refcounting and make the engine fully own its
executable CUs once we can hold base CUs in most places.
Change-Id: I9a53e83d5c4746c2b2bca896b51baa4fe7fee757
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/common')
-rw-r--r-- | src/qml/common/qv4compileddata.cpp | 114 | ||||
-rw-r--r-- | src/qml/common/qv4compileddata_p.h | 8 |
2 files changed, 122 insertions, 0 deletions
diff --git a/src/qml/common/qv4compileddata.cpp b/src/qml/common/qv4compileddata.cpp new file mode 100644 index 0000000000..4ed53e6907 --- /dev/null +++ b/src/qml/common/qv4compileddata.cpp @@ -0,0 +1,114 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4compileddata_p.h" + +#include <QtQml/qqmlfile.h> + +#include <QtCore/qcryptographichash.h> +#include <QtCore/qdir.h> +#include <QtCore/qscopeguard.h> +#include <QtCore/qstandardpaths.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace CompiledData { + +QString CompilationUnit::localCacheFilePath(const QUrl &url) +{ + static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH"); + + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); + const QString cacheFileSuffix + = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix(); + QCryptographicHash fileNameHash(QCryptographicHash::Sha1); + fileNameHash.addData(localSourcePath.toUtf8()); + QString directory = envCachePath.isEmpty() + ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + + QLatin1String("/qmlcache/") + : QString::fromLocal8Bit(envCachePath) + QLatin1String("/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + + QLatin1Char('.') + cacheFileSuffix; +} + +bool CompilationUnit::loadFromDisk( + const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) +{ + if (!QQmlFile::isLocalFile(url)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); + auto cacheFile = std::make_unique<CompilationUnitMapper>(); + + const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) }; + for (const QString &cachePath : cachePaths) { + Unit *mappedUnit = cacheFile->get(cachePath, sourceTimeStamp, errorString); + if (!mappedUnit) + continue; + + const Unit *oldData = unitData(); + const Unit * const oldDataPtr + = (oldData && !(oldData->flags & Unit::StaticData)) + ? oldData + : nullptr; + + auto dataPtrRevert = qScopeGuard([this, oldData](){ + setUnitData(oldData); + }); + setUnitData(mappedUnit); + + if (mappedUnit->sourceFileIndex != 0) { + if (mappedUnit->sourceFileIndex >= + mappedUnit->stringTableSize + dynamicStrings.size()) { + *errorString = QStringLiteral("QML source file index is invalid."); + continue; + } + if (sourcePath != + QQmlFile::urlToLocalFileOrQrc(stringAt(mappedUnit->sourceFileIndex))) { + *errorString = QStringLiteral("QML source file has moved to a different location."); + continue; + } + } + + dataPtrRevert.dismiss(); + free(const_cast<Unit*>(oldDataPtr)); + backingFile = std::move(cacheFile); + return true; + } + + return false; +} + +bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) +{ + if (unitData()->sourceTimeStamp == 0) { + *errorString = QStringLiteral("Missing time stamp for source file"); + return false; + } + + if (!QQmlFile::isLocalFile(unitUrl)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + return SaveableUnitPointer(unitData()).saveToDisk<char>( + [&unitUrl, errorString](const char *data, quint32 size) { + const QString cachePath = localCacheFilePath(unitUrl); + if (SaveableUnitPointer::writeDataToFile( + cachePath, data, size, errorString)) { + CompilationUnitMapper::invalidate(cachePath); + return true; + } + + return false; + }); +} + +} // namespace CompiledData +} // namespace QV4 + +QT_END_NAMESPACE diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 82424c1c9d..f7654661e2 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -32,6 +32,7 @@ #include <private/qendian_p.h> #include <private/qqmlrefcount_p.h> #include <private/qv4staticvalue_p.h> +#include <private/qv4compilationunitmapper_p.h> #include <functional> #include <limits.h> @@ -1434,6 +1435,8 @@ struct CompilationUnit final : public QQmlRefCounted<CompilationUnit> // pointers either to data->constants() or little-endian memory copy. const StaticValue *constants = nullptr; + + std::unique_ptr<CompilationUnitMapper> backingFile; public: using CompiledObject = CompiledData::Object; @@ -1550,6 +1553,11 @@ public: return constants[binding->value.constantValueIndex].doubleValue(); } + Q_QML_EXPORT static QString localCacheFilePath(const QUrl &url); + Q_QML_EXPORT bool loadFromDisk( + const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); + Q_QML_EXPORT bool saveToDisk(const QUrl &unitUrl, QString *errorString); + private: QString m_fileName; // initialized from data->sourceFileIndex QString m_finalUrlString; // initialized from data->finalUrlIndex |