Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qv4compileddata.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4compileddata_p.h"
5
6#include <private/inlinecomponentutils_p.h>
7#include <private/qml_compile_hash_p.h>
8#include <private/qqmlscriptdata_p.h>
9#include <private/qqmltypenamecache_p.h>
10#include <private/qv4resolvedtypereference_p.h>
11
12#include <QtQml/qqmlfile.h>
13
14#include <QtCore/qdir.h>
15#include <QtCore/qscopeguard.h>
16#include <QtCore/qstandardpaths.h>
17
18static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
19
20#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
21# ifdef Q_OS_LINUX
22// Place on a separate section on Linux so it's easier to check from outside
23// what the hash version is.
24__attribute__((section(".qml_compile_hash")))
25# endif
26const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH;
27static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH,
28 "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
29#else
30# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
31#endif
32
34
35namespace QV4 {
36namespace CompiledData {
37
38
39bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const
40{
41 if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) {
42 *errorString = QStringLiteral("Magic bytes in the header do not match");
43 return false;
44 }
45
47 *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2")
48 .arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
49 return false;
50 }
51
53 *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2")
54 .arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
55 return false;
56 }
57
58 if (sourceTimeStamp) {
59 // Files from the resource system do not have any time stamps, so fall back to the application
60 // executable.
61 if (!expectedSourceTimeStamp.isValid())
63
64 if (expectedSourceTimeStamp.isValid()
65 && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) {
66 *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
67 return false;
68 }
69 }
70
71#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
72 if (qstrncmp(qml_compile_hash, libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) {
73 *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2")
75 QByteArray(libraryVersionHash, QML_COMPILE_HASH_LENGTH)
76 .toPercentEncoding()),
78 QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH)
79 .toPercentEncoding()));
80 return false;
81 }
82#else
83#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
84#endif
85 return true;
86}
87
94 QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const
95{
96 std::vector<int> keys (size());
97 int i = 0;
98 for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
99 keys[i] = it.key();
100 ++i;
101 }
102 std::sort(keys.begin(), keys.end());
103 for (int key: keys) {
104 if (!this->operator[](key)->addToHash(hash, checksums))
105 return false;
106 }
107
108 return true;
109}
110
112 const Unit *unitData, const QString &fileName, const QString &finalUrlString)
113{
115}
116
118{
120
121 if (data) {
122 if (data->qmlUnit() != qmlData)
123 free(const_cast<QmlUnit *>(qmlData));
124 qmlData = nullptr;
125
127 free(const_cast<Unit *>(data));
128 }
129 data = nullptr;
130#if Q_BYTE_ORDER == Q_BIG_ENDIAN
131 delete [] constants;
132 constants = nullptr;
133#endif
134}
135
137{
138 static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH");
139
140 const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
141 const QString cacheFileSuffix
142 = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
144 fileNameHash.addData(localSourcePath.toUtf8());
145 QString directory = envCachePath.isEmpty()
147 + QLatin1String("/qmlcache/")
148 : QString::fromLocal8Bit(envCachePath) + QLatin1String("/");
149 QDir::root().mkpath(directory);
150 return directory + QString::fromUtf8(fileNameHash.result().toHex())
151 + QLatin1Char('.') + cacheFileSuffix;
152}
153
155 const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
156{
158 *errorString = QStringLiteral("File has to be a local file.");
159 return false;
160 }
161
162 const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
163 auto cacheFile = std::make_unique<CompilationUnitMapper>();
164
165 const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
166 for (const QString &cachePath : cachePaths) {
167 Unit *mappedUnit = cacheFile->get(cachePath, sourceTimeStamp, errorString);
168 if (!mappedUnit)
169 continue;
170
171 const Unit *oldData = unitData();
172 const Unit * const oldDataPtr
173 = (oldData && !(oldData->flags & Unit::StaticData))
174 ? oldData
175 : nullptr;
176
177 auto dataPtrRevert = qScopeGuard([this, oldData](){
178 setUnitData(oldData);
179 });
180 setUnitData(mappedUnit);
181
182 if (mappedUnit->sourceFileIndex != 0) {
183 if (mappedUnit->sourceFileIndex >=
184 mappedUnit->stringTableSize + dynamicStrings.size()) {
185 *errorString = QStringLiteral("QML source file index is invalid.");
186 continue;
187 }
188 if (sourcePath !=
189 QQmlFile::urlToLocalFileOrQrc(stringAt(mappedUnit->sourceFileIndex))) {
190 *errorString = QStringLiteral("QML source file has moved to a different location.");
191 continue;
192 }
193 }
194
195 dataPtrRevert.dismiss();
196 free(const_cast<Unit*>(oldDataPtr));
197 backingFile = std::move(cacheFile);
198 return true;
199 }
200
201 return false;
202}
203
204bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
205{
206 if (unitData()->sourceTimeStamp == 0) {
207 *errorString = QStringLiteral("Missing time stamp for source file");
208 return false;
209 }
210
211 if (!QQmlFile::isLocalFile(unitUrl)) {
212 *errorString = QStringLiteral("File has to be a local file.");
213 return false;
214 }
215
217 [&unitUrl, errorString](const char *data, quint32 size) {
218 const QString cachePath = localCacheFilePath(unitUrl);
220 cachePath, data, size, errorString)) {
222 return true;
223 }
224
225 return false;
226 });
227}
228
230{
232 requests.reserve(data->moduleRequestTableSize);
233 for (uint i = 0; i < data->moduleRequestTableSize; ++i)
234 requests << stringAt(data->moduleRequestTable()[i]);
235 return requests;
236}
237
239{
240 for (ResolvedTypeReference *ref : std::as_const(resolvedTypes)) {
241 if (ref->type().typeId() == type)
242 return ref;
243 }
244 return nullptr;
245
246}
247
249{
250 if (!icRootName)
252 return inlineComponentData[*icRootName].totalBindingCount;
253}
254
256{
257 if (!icRootName)
258 return m_totalObjectCount;
259 return inlineComponentData[*icRootName].totalObjectCount;
260}
261
262template<typename F>
264 const QQmlType &type,
265 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
266 F &&populateIcData)
267{
268 if (type.isInlineComponentType()) {
269 QString icRootName;
270 if (compilationUnit->icRootName) {
271 icRootName = type.elementName();
272 std::swap(*compilationUnit->icRootName, icRootName);
273 } else {
274 compilationUnit->icRootName = std::make_unique<QString>(type.elementName());
275 }
276
277 populateIcData();
278
279 if (icRootName.isEmpty())
280 compilationUnit->icRootName.reset();
281 else
282 std::swap(*compilationUnit->icRootName, icRootName);
283 } else {
284 populateIcData();
285 }
286}
287
289{
290 // Add to type registry of composites
291 if (propertyCaches.needsVMEMetaObject(/*root object*/0)) {
292 // qmlType is only valid for types that have references to themselves.
293 if (type.isValid()) {
294 qmlType = type;
295 } else {
297 finalUrl(), this, (unitData()->flags & CompiledData::Unit::IsSingleton)
300 }
301
303 } else {
304 const QV4::CompiledData::Object *obj = objectAt(/*root object*/0);
305 auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
306 Q_ASSERT(typeRef);
307 if (const auto compilationUnit = typeRef->compilationUnit())
308 qmlType = compilationUnit->qmlType;
309 else
310 qmlType = typeRef->type();
311 }
312
313 // Collect some data for instantiation later.
314 using namespace icutils;
315 std::vector<QV4::CompiledData::InlineComponent> allICs {};
316 for (int i=0; i != objectCount(); ++i) {
317 const CompiledObject *obj = objectAt(i);
318 for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
319 allICs.push_back(*it);
320 }
321 }
322 NodeList nodes;
323 nodes.resize(allICs.size());
324 std::iota(nodes.begin(), nodes.end(), 0);
325 AdjacencyList adjacencyList;
326 adjacencyList.resize(nodes.size());
327 fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs);
328 bool hasCycle = false;
329 auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
330 Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator
331
332 // We need to first iterate over all inline components,
333 // as the containing component might create instances of them
334 // and in that case we need to add its object count
335 for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
336 const auto &ic = allICs.at(nodeIt->index());
337 const int lastICRoot = ic.objectIndex;
338 for (int i = ic.objectIndex; i<objectCount(); ++i) {
339 const QV4::CompiledData::Object *obj = objectAt(i);
340 bool leftCurrentInlineComponent
341 = (i != lastICRoot
344 if (leftCurrentInlineComponent)
345 break;
346 const QString lastICRootName = stringAt(ic.nameIndex);
347 inlineComponentData[lastICRootName].totalBindingCount
348 += obj->nBindings;
349
350 if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
351 const auto type = typeRef->type();
352 if (type.isValid() && type.parserStatusCast() != -1)
353 ++inlineComponentData[lastICRootName].totalParserStatusCount;
354
355 ++inlineComponentData[lastICRootName].totalObjectCount;
356 if (const auto compilationUnit = typeRef->compilationUnit()) {
357 // if the type is an inline component type, we have to extract the information
358 // from it.
359 // This requires that inline components are visited in the correct order.
360 processInlinComponentType(type, compilationUnit, [&]() {
361 auto &icData = inlineComponentData[lastICRootName];
362 icData.totalBindingCount += compilationUnit->totalBindingsCount();
363 icData.totalParserStatusCount += compilationUnit->totalParserStatusCount();
364 icData.totalObjectCount += compilationUnit->totalObjectCount();
365 });
366 }
367 }
368 }
369 }
370 int bindingCount = 0;
371 int parserStatusCount = 0;
372 int objectCount = 0;
373 for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
374 const QV4::CompiledData::Object *obj = objectAt(i);
376 continue;
377
378 bindingCount += obj->nBindings;
379 if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
380 const auto type = typeRef->type();
381 if (type.isValid() && type.parserStatusCast() != -1)
382 ++parserStatusCount;
383 ++objectCount;
384 if (const auto compilationUnit = typeRef->compilationUnit()) {
385 processInlinComponentType(type, compilationUnit, [&](){
386 bindingCount += compilationUnit->totalBindingsCount();
387 parserStatusCount += compilationUnit->totalParserStatusCount();
388 objectCount += compilationUnit->totalObjectCount();
389 });
390 }
391 }
392 }
393
394 m_totalBindingsCount = bindingCount;
395 m_totalParserStatusCount = parserStatusCount;
396 m_totalObjectCount = objectCount;
397}
398
400{
401 if (!icRootName)
403 return inlineComponentData[*icRootName].totalParserStatusCount;
404}
405
406bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const
407{
408 if (!dependencyHasher) {
409 for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
410 if (data->dependencyMD5Checksum[i] != 0)
411 return false;
412 }
413 return true;
414 }
415 const QByteArray checksum = dependencyHasher();
416 return checksum.size() == sizeof(data->dependencyMD5Checksum)
417 && memcmp(data->dependencyMD5Checksum, checksum.constData(),
418 sizeof(data->dependencyMD5Checksum)) == 0;
419}
420
422{
423 if (inlineComponentName.isEmpty())
424 return qmlType;
425 return inlineComponentData[inlineComponentName].qmlType;
426}
427
428} // namespace CompiledData
429} // namespace QV4
430
struct capHdr __attribute__
DarwinBluetooth::RequestQueue requests
\inmodule QtCore
Definition qbytearray.h:57
static QString applicationFilePath()
Returns the file path of the application executable.
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDir root()
Returns the root directory.
Definition qdir.h:224
QDateTime lastModified() const
Returns the date and time when the file was last modified.
Definition qfileinfo.h:160
QString completeSuffix() const
Returns the complete suffix (extension) of the file.
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:927
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1219
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1215
QList< int > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition qhash.h:1086
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
iterator end()
Definition qlist.h:626
iterator begin()
Definition qlist.h:625
\inmodule QtCore
Definition qmetatype.h:341
static bool isLocalFile(const QString &url)
Returns true if url is a local file that can be opened with \l{QFile}.
Definition qqmlfile.cpp:644
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
static QQmlType findCompositeType(const QUrl &url, const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &compilationUnit, CompositeTypeLookupMode mode=NonSingleton)
static void registerInternalCompositeType(const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &compilationUnit)
static QString writableLocation(StandardLocation type)
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
\inmodule QtCore
Definition qurl.h:94
static void invalidate(const QString &cacheFilePath)
static bool writeDataToFile(const QString &outputFileName, const char *data, quint32 size, QString *errorString)
bool saveToDisk(const std::function< bool(const Char *, quint32)> &writer) const
QHash< int, QWidget * > hash
[35multi]
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
Combined button and popup list for selecting options.
void processInlinComponentType(const QQmlType &type, const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &compilationUnit, F &&populateIcData)
std::function< QByteArray()> DependentTypesHasher
static const char magic_str[]
std::vector< std::vector< Node * > > AdjacencyList
int qstrncmp(const char *str1, const char *str2, size_t len)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
static quint32 checksum(const QByteArray &table)
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLbitfield flags
GLint ref
GLhandleARB obj
[2]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define QT_VERSION
unsigned int quint32
Definition qtypes.h:50
unsigned int uint
Definition qtypes.h:34
#define QV4_DATA_STRUCTURE_VERSION
QUrl url("example.com")
[constructor-url-reference]
\inmodule QtCore \reentrant
Definition qchar.h:18
std::unique_ptr< CompilationUnitMapper > backingFile
QHash< QString, InlineComponentData > inlineComponentData
void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit=nullptr, const QString &fileName=QString(), const QString &finalUrlString=QString())
std::unique_ptr< QString > icRootName
Q_QML_EXPORT bool saveToDisk(const QUrl &unitUrl, QString *errorString)
ResolvedTypeReference * resolvedType(int id) const
CompilationUnit(const Unit *unitData, const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions, const QString &fileName=QString(), const QString &finalUrlString=QString())
ResolvedTypeReferenceMap resolvedTypes
bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const
Q_QML_EXPORT QStringList moduleRequests() const
QQmlType qmlTypeForComponent(const QString &inlineComponentName=QString()) const
void finalizeCompositeType(const QQmlType &type)
static Q_QML_EXPORT QString localCacheFilePath(const QUrl &url)
Q_QML_EXPORT bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
bool addToHash(QCryptographicHash *hash, QHash< quintptr, QByteArray > *checksums) const
bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const
char libraryVersionHash[QmlCompileHashSpace]