OpenCoverage

generateloader.cpp

Absolute File Name:/home/opencoverage/opencoverage/guest-scripts/qtdeclarative/src/qtdeclarative/tools/qmlcachegen/generateloader.cpp
Source codeSwitch to Preprocessed file
LineSourceCount
1/****************************************************************************-
2**-
3** Copyright (C) 2016 The Qt Company Ltd.-
4** Contact: https://www.qt.io/licensing/-
5**-
6** This file is part of the QtQml module of the Qt Toolkit.-
7**-
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$-
9** Commercial License Usage-
10** Licensees holding valid commercial Qt licenses may use this file in-
11** accordance with the commercial license agreement provided with the-
12** Software or, alternatively, in accordance with the terms contained in-
13** a written agreement between you and The Qt Company. For licensing terms-
14** and conditions see https://www.qt.io/terms-conditions. For further-
15** information use the contact form at https://www.qt.io/contact-us.-
16**-
17** GNU General Public License Usage-
18** Alternatively, this file may be used under the terms of the GNU-
19** General Public License version 3 as published by the Free Software-
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT-
21** included in the packaging of this file. Please review the following-
22** information to ensure the GNU General Public License requirements will-
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.-
24**-
25** $QT_END_LICENSE$-
26**-
27****************************************************************************/-
28#include <QByteArray>-
29#include <QString>-
30#include <QStringList>-
31#include <QTextStream>-
32#include <QVector>-
33#include <QtEndian>-
34#include <QStack>-
35#include <QFileInfo>-
36#include <QSaveFile>-
37-
38/*!-
39 * \internal-
40 * Mangles \a str to be a unique C++ identifier. Characters that are invalid for C++ identifiers-
41 * are replaced by the pattern \c _0x<hex>_ where <hex> is the hexadecimal unicode-
42 * representation of the character. As identifiers with leading underscores followed by either-
43 * another underscore or a capital letter are reserved in C++, we also escape those, by escaping-
44 * the first underscore, using the above method.-
45 *-
46 * \note-
47 * Although C++11 allows for non-ascii (unicode) characters to be used in identifiers,-
48 * many compilers forgot to read the spec and do not implement this. Some also do not-
49 * implement C99 identifiers, because that is \e {at the implementation's discretion}. So,-
50 * we are stuck with plain old boring identifiers.-
51 */-
52QString mangledIdentifier(const QString &str)-
53{-
54 Q_ASSERT(!str.isEmpty());-
55-
56 QString mangled;-
57 mangled.reserve(str.size());-
58-
59 int i = 0;-
60 if (str.startsWith(QLatin1Char('_')) && str.size() > 1) {
str.startsWith...tin1Char('_'))Description
TRUEnever evaluated
FALSEnever evaluated
str.size() > 1Description
TRUEnever evaluated
FALSEnever evaluated
0
61 QChar ch = str.at(1);-
62 if (ch == QLatin1Char('_')
ch == QLatin1Char('_')Description
TRUEnever evaluated
FALSEnever evaluated
0
63 || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) {
ch >= QLatin1Char('A')Description
TRUEnever evaluated
FALSEnever evaluated
ch <= QLatin1Char('Z')Description
TRUEnever evaluated
FALSEnever evaluated
0
64 mangled += QLatin1String("_0x5f_");-
65 ++i;-
66 }
never executed: end of block
0
67 }
never executed: end of block
0
68-
69 for (int ei = str.length(); i != ei; ++i) {
i != eiDescription
TRUEnever evaluated
FALSEnever evaluated
0
70 auto c = str.at(i).unicode();-
71 if ((c >= QLatin1Char('0') && c <= QLatin1Char('9'))
c >= QLatin1Char('0')Description
TRUEnever evaluated
FALSEnever evaluated
c <= QLatin1Char('9')Description
TRUEnever evaluated
FALSEnever evaluated
0
72 || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))
c >= QLatin1Char('a')Description
TRUEnever evaluated
FALSEnever evaluated
c <= QLatin1Char('z')Description
TRUEnever evaluated
FALSEnever evaluated
0
73 || (c >= QLatin1Char('A') && c <= QLatin1Char('Z'))
c >= QLatin1Char('A')Description
TRUEnever evaluated
FALSEnever evaluated
c <= QLatin1Char('Z')Description
TRUEnever evaluated
FALSEnever evaluated
0
74 || c == QLatin1Char('_')) {
c == QLatin1Char('_')Description
TRUEnever evaluated
FALSEnever evaluated
0
75 mangled += c;-
76 } else {
never executed: end of block
0
77 mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_');-
78 }
never executed: end of block
0
79 }-
80-
81 return mangled;
never executed: return mangled;
0
82}-
83-
84QString symbolNamespaceForPath(const QString &relativePath)-
85{-
86 QFileInfo fi(relativePath);-
87 QString symbol = fi.path();-
88 if (symbol.length() == 1 && symbol.startsWith(QLatin1Char('.'))) {
symbol.length() == 1Description
TRUEnever evaluated
FALSEnever evaluated
symbol.startsW...tin1Char('.'))Description
TRUEnever evaluated
FALSEnever evaluated
0
89 symbol.clear();-
90 } else {
never executed: end of block
0
91 symbol.replace(QLatin1Char('/'), QLatin1Char('_'));-
92 symbol += QLatin1Char('_');-
93 }
never executed: end of block
0
94 symbol += fi.baseName();-
95 symbol += QLatin1Char('_');-
96 symbol += fi.completeSuffix();-
97 return mangledIdentifier(symbol);
never executed: return mangledIdentifier(symbol);
0
98}-
99-
100struct VirtualDirectoryEntry-
101{-
102 QString name;-
103 QVector<VirtualDirectoryEntry*> dirEntries;-
104 int firstChildIndex = -1; // node index inside generated data-
105 bool isDirectory = true;-
106-
107 VirtualDirectoryEntry()-
108 {}-
109-
110 ~VirtualDirectoryEntry()-
111 {-
112 qDeleteAll(dirEntries);-
113 }
never executed: end of block
0
114-
115 VirtualDirectoryEntry *append(const QString &name)-
116 {-
117 for (QVector<VirtualDirectoryEntry*>::Iterator it = dirEntries.begin(), end = dirEntries.end();-
118 it != end; ++it) {
it != endDescription
TRUEnever evaluated
FALSEnever evaluated
0
119 if ((*it)->name == name)
(*it)->name == nameDescription
TRUEnever evaluated
FALSEnever evaluated
0
120 return *it;
never executed: return *it;
0
121 }
never executed: end of block
0
122-
123 VirtualDirectoryEntry *subEntry = new VirtualDirectoryEntry;-
124 subEntry->name = name;-
125 dirEntries.append(subEntry);-
126 return subEntry;
never executed: return subEntry;
0
127 }-
128-
129 void appendEmptyFile(const QString &name)-
130 {-
131 VirtualDirectoryEntry *subEntry = new VirtualDirectoryEntry;-
132 subEntry->name = name;-
133 subEntry->isDirectory = false;-
134 dirEntries.append(subEntry);-
135 }
never executed: end of block
0
136-
137 bool isEmpty() const { return dirEntries.isEmpty(); }
never executed: return dirEntries.isEmpty();
0
138};-
139-
140struct DataStream-
141{-
142 DataStream(QVector<unsigned char > *data = nullptr)-
143 : data(data)-
144 {}
never executed: end of block
0
145-
146 qint64 currentOffset() const { return data->size(); }
never executed: return data->size();
0
147-
148 DataStream &operator<<(quint16 value)-
149 {-
150 unsigned char d[2];-
151 qToBigEndian(value, d);-
152 data->append(d[0]);-
153 data->append(d[1]);-
154 return *this;
never executed: return *this;
0
155 }-
156 DataStream &operator<<(quint32 value)-
157 {-
158 unsigned char d[4];-
159 qToBigEndian(value, d);-
160 data->append(d[0]);-
161 data->append(d[1]);-
162 data->append(d[2]);-
163 data->append(d[3]);-
164 return *this;
never executed: return *this;
0
165 }-
166private:-
167 QVector<unsigned char> *data;-
168};-
169-
170static bool resource_sort_order(const VirtualDirectoryEntry *lhs, const VirtualDirectoryEntry *rhs)-
171{-
172 return qt_hash(lhs->name) < qt_hash(rhs->name);
never executed: return qt_hash(lhs->name) < qt_hash(rhs->name);
0
173}-
174-
175struct ResourceTree-
176{-
177 ResourceTree()-
178 {}-
179-
180 void serialize(VirtualDirectoryEntry &root, QVector<unsigned char> *treeData, QVector<unsigned char> *stringData)-
181 {-
182 treeStream = DataStream(treeData);-
183 stringStream = DataStream(stringData);-
184-
185 QStack<VirtualDirectoryEntry *> directories;-
186-
187 {-
188 directories.push(&root);-
189 while (!directories.isEmpty()) {
!directories.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
190 VirtualDirectoryEntry *entry = directories.pop();-
191 registerString(entry->name);-
192 if (entry->isDirectory)
entry->isDirectoryDescription
TRUEnever evaluated
FALSEnever evaluated
0
193 directories << entry->dirEntries;
never executed: directories << entry->dirEntries;
0
194 }
never executed: end of block
0
195 }-
196-
197 {-
198 quint32 currentDirectoryIndex = 1;-
199 directories.push(&root);-
200 while (!directories.isEmpty()) {
!directories.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
201 VirtualDirectoryEntry *entry = directories.pop();-
202 entry->firstChildIndex = currentDirectoryIndex;-
203 currentDirectoryIndex += entry->dirEntries.count();-
204 std::sort(entry->dirEntries.begin(), entry->dirEntries.end(), resource_sort_order);-
205-
206 for (QVector<VirtualDirectoryEntry*>::ConstIterator child = entry->dirEntries.constBegin(), end = entry->dirEntries.constEnd();-
207 child != end; ++child) {
child != endDescription
TRUEnever evaluated
FALSEnever evaluated
0
208 if ((*child)->isDirectory)
(*child)->isDirectoryDescription
TRUEnever evaluated
FALSEnever evaluated
0
209 directories << *child;
never executed: directories << *child;
0
210 }
never executed: end of block
0
211 }
never executed: end of block
0
212 }-
213-
214 {-
215 writeTreeEntry(&root);-
216 directories.push(&root);-
217 while (!directories.isEmpty()) {
!directories.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
218 VirtualDirectoryEntry *entry = directories.pop();-
219-
220 for (QVector<VirtualDirectoryEntry*>::ConstIterator child = entry->dirEntries.constBegin(), end = entry->dirEntries.constEnd();-
221 child != end; ++child) {
child != endDescription
TRUEnever evaluated
FALSEnever evaluated
0
222 writeTreeEntry(*child);-
223 if ((*child)->isDirectory)
(*child)->isDirectoryDescription
TRUEnever evaluated
FALSEnever evaluated
0
224 directories << (*child);
never executed: directories << (*child);
0
225 }
never executed: end of block
0
226 }
never executed: end of block
0
227 }-
228 }
never executed: end of block
0
229-
230private:-
231 DataStream treeStream;-
232 DataStream stringStream;-
233 QHash<QString, qint64> stringOffsets;-
234-
235 void registerString(const QString &name)-
236 {-
237 if (stringOffsets.contains(name))
stringOffsets.contains(name)Description
TRUEnever evaluated
FALSEnever evaluated
0
238 return;
never executed: return;
0
239 const qint64 offset = stringStream.currentOffset();-
240 stringOffsets.insert(name, offset);-
241-
242 stringStream << quint16(name.length())-
243 << quint32(qt_hash(name));-
244 for (int i = 0; i < name.length(); ++i)
i < name.length()Description
TRUEnever evaluated
FALSEnever evaluated
0
245 stringStream << quint16(name.at(i).unicode());
never executed: stringStream << quint16(name.at(i).unicode());
0
246 }
never executed: end of block
0
247-
248 void writeTreeEntry(VirtualDirectoryEntry *entry)-
249 {-
250 treeStream << quint32(stringOffsets.value(entry->name))-
251 << quint16(entry->isDirectory ? 0x2 : 0x0); // Flags: File or Directory-
252-
253 if (entry->isDirectory) {
entry->isDirectoryDescription
TRUEnever evaluated
FALSEnever evaluated
0
254 treeStream << quint32(entry->dirEntries.count())-
255 << quint32(entry->firstChildIndex);-
256 } else {
never executed: end of block
0
257 treeStream << quint16(QLocale::AnyCountry) << quint16(QLocale::C)-
258 << quint32(0x0);-
259 }
never executed: end of block
0
260 }-
261};-
262-
263static QByteArray generateResourceDirectoryTree(QTextStream &code, const QStringList &qrcFiles)-
264{-
265 QByteArray call;-
266 if (qrcFiles.isEmpty())
qrcFiles.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
267 return call;
never executed: return call;
0
268-
269 VirtualDirectoryEntry resourceDirs;-
270 resourceDirs.name = QStringLiteral("/");-
271-
272 foreach (const QString &entry, qrcFiles) {
_container_.controlDescription
TRUEnever evaluated
FALSEnever evaluated
_container_.controlDescription
TRUEnever evaluated
FALSEnever evaluated
_container_.i != _container_.eDescription
TRUEnever evaluated
FALSEnever evaluated
0
273 const QStringList segments = entry.split(QLatin1Char('/'), QString::SkipEmptyParts);-
274-
275 VirtualDirectoryEntry *dirEntry = &resourceDirs;-
276-
277 for (int i = 0; i < segments.count() - 1; ++i)
i < segments.count() - 1Description
TRUEnever evaluated
FALSEnever evaluated
0
278 dirEntry = dirEntry->append(segments.at(i));
never executed: dirEntry = dirEntry->append(segments.at(i));
0
279 dirEntry->appendEmptyFile(segments.last());-
280 }
never executed: end of block
0
281-
282 if (resourceDirs.isEmpty())
resourceDirs.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
283 return call;
never executed: return call;
0
284-
285 QVector<unsigned char> names;-
286 QVector<unsigned char> tree;-
287 ResourceTree().serialize(resourceDirs, &tree, &names);-
288-
289 code << "static const unsigned char qt_resource_tree[] = {\n";-
290 for (int i = 0; i < tree.count(); ++i) {
i < tree.count()Description
TRUEnever evaluated
FALSEnever evaluated
0
291 code << uint(tree.at(i));-
292 if (i < tree.count() - 1)
i < tree.count() - 1Description
TRUEnever evaluated
FALSEnever evaluated
0
293 code << ',';
never executed: code << ',';
0
294 if (i % 16 == 0)
i % 16 == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
295 code << '\n';
never executed: code << '\n';
0
296 }
never executed: end of block
0
297 code << "};\n";-
298-
299 code << "static const unsigned char qt_resource_names[] = {\n";-
300 for (int i = 0; i < names.count(); ++i) {
i < names.count()Description
TRUEnever evaluated
FALSEnever evaluated
0
301 code << uint(names.at(i));-
302 if (i < names.count() - 1)
i < names.count() - 1Description
TRUEnever evaluated
FALSEnever evaluated
0
303 code << ',';
never executed: code << ',';
0
304 if (i % 16 == 0)
i % 16 == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
305 code << '\n';
never executed: code << '\n';
0
306 }
never executed: end of block
0
307 code << "};\n";-
308-
309 code << "static const unsigned char qt_resource_empty_payout[] = { 0, 0, 0, 0, 0 };\n";-
310-
311 code << "QT_BEGIN_NAMESPACE\n";-
312 code << "extern Q_CORE_EXPORT bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);\n";-
313 code << "QT_END_NAMESPACE\n";-
314-
315 call = "QT_PREPEND_NAMESPACE(qRegisterResourceData)(/*version*/0x01, qt_resource_tree, qt_resource_names, qt_resource_empty_payout);\n";-
316-
317 return call;
never executed: return call;
0
318}-
319-
320static QString qtResourceNameForFile(const QString &fileName)-
321{-
322 QFileInfo fi(fileName);-
323 QString name = fi.completeBaseName();-
324 if (name.isEmpty())
name.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
325 name = fi.fileName();
never executed: name = fi.fileName();
0
326 name.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));-
327 return name;
never executed: return name;
0
328}-
329-
330bool generateLoader(const QStringList &compiledFiles, const QString &outputFileName, const QStringList &resourceFileMappings, QString *errorString)-
331{-
332 QByteArray generatedLoaderCode;-
333-
334 {-
335 QTextStream stream(&generatedLoaderCode);-
336 stream << "#include <QtQml/qqmlprivate.h>\n";-
337 stream << "#include <QtCore/qdir.h>\n";-
338 stream << "#include <QtCore/qurl.h>\n";-
339 stream << "\n";-
340-
341 QByteArray resourceRegisterCall = generateResourceDirectoryTree(stream, compiledFiles);-
342-
343 stream << "namespace QmlCacheGeneratedCode {\n";-
344 for (int i = 0; i < compiledFiles.count(); ++i) {
i < compiledFiles.count()Description
TRUEnever evaluated
FALSEnever evaluated
0
345 const QString compiledFile = compiledFiles.at(i);-
346 const QString ns = symbolNamespaceForPath(compiledFile);-
347 stream << "namespace " << symbolNamespaceForPath(compiledFile) << " { \n";-
348 stream << " extern const unsigned char qmlData[];\n";-
349 stream << " const QQmlPrivate::CachedQmlUnit unit = {\n";-
350 stream << " reinterpret_cast<const QV4::CompiledData::Unit*>(&qmlData), nullptr, nullptr\n";-
351 stream << " };\n";-
352 stream << "}\n";-
353 }
never executed: end of block
0
354-
355 stream << "\n}\n";-
356 stream << "namespace {\n";-
357-
358 stream << "struct Registry {\n";-
359 stream << " Registry();\n";-
360 stream << " QHash<QString, const QQmlPrivate::CachedQmlUnit*> resourcePathToCachedUnit;\n";-
361 stream << " static const QQmlPrivate::CachedQmlUnit *lookupCachedUnit(const QUrl &url);\n";-
362 stream << "};\n\n";-
363 stream << "Q_GLOBAL_STATIC(Registry, unitRegistry)\n";-
364 stream << "\n\n";-
365-
366 stream << "Registry::Registry() {\n";-
367-
368 for (int i = 0; i < compiledFiles.count(); ++i) {
i < compiledFiles.count()Description
TRUEnever evaluated
FALSEnever evaluated
0
369 const QString qrcFile = compiledFiles.at(i);-
370 const QString ns = symbolNamespaceForPath(qrcFile);-
371 stream << " resourcePathToCachedUnit.insert(QStringLiteral(\"" << qrcFile << "\"), &QmlCacheGeneratedCode::" << ns << "::unit);\n";-
372 }
never executed: end of block
0
373-
374 stream << " QQmlPrivate::RegisterQmlUnitCacheHook registration;\n";-
375 stream << " registration.version = 0;\n";-
376 stream << " registration.lookupCachedQmlUnit = &lookupCachedUnit;\n";-
377 stream << " QQmlPrivate::qmlregister(QQmlPrivate::QmlUnitCacheHookRegistration, &registration);\n";-
378-
379 if (!resourceRegisterCall.isEmpty())
!resourceRegis...Call.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
380 stream << resourceRegisterCall;
never executed: stream << resourceRegisterCall;
0
381-
382 stream << "}\n";-
383 stream << "const QQmlPrivate::CachedQmlUnit *Registry::lookupCachedUnit(const QUrl &url) {\n";-
384 stream << " if (url.scheme() != QLatin1String(\"qrc\"))\n";-
385 stream << " return nullptr;\n";-
386 stream << " QString resourcePath = QDir::cleanPath(url.path());\n";-
387 stream << " if (resourcePath.isEmpty())\n";-
388 stream << " return nullptr;\n";-
389 stream << " if (!resourcePath.startsWith(QLatin1Char('/')))\n";-
390 stream << " resourcePath.prepend(QLatin1Char('/'));\n";-
391 stream << " return unitRegistry()->resourcePathToCachedUnit.value(resourcePath, nullptr);\n";-
392 stream << "}\n";-
393 stream << "}\n";-
394-
395 for (const QString &mapping: resourceFileMappings) {-
396 QString originalResourceFile = mapping;-
397 QString newResourceFile;-
398 const int mappingSplit = originalResourceFile.indexOf(QLatin1Char('='));-
399 if (mappingSplit != -1) {
mappingSplit != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
400 newResourceFile = originalResourceFile.mid(mappingSplit + 1);-
401 originalResourceFile.truncate(mappingSplit);-
402 }
never executed: end of block
0
403-
404 const QString suffix = qtResourceNameForFile(originalResourceFile);-
405 const QString initFunction = QLatin1String("qInitResources_") + suffix;-
406-
407 stream << QStringLiteral("int QT_MANGLE_NAMESPACE(%1)() {\n").arg(initFunction);
never executed: return qstring_literal_temp;
0
408 stream << " ::unitRegistry();\n";-
409 if (!newResourceFile.isEmpty())
!newResourceFile.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
410 stream << " Q_INIT_RESOURCE(" << qtResourceNameForFile(newResourceFile) << ");\n";
never executed: stream << " Q_INIT_RESOURCE(" << qtResourceNameForFile(newResourceFile) << ");\n";
0
411 stream << " return 1;\n";-
412 stream << "}\n";-
413 stream << "Q_CONSTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(" << initFunction << "))\n";-
414-
415 const QString cleanupFunction = QLatin1String("qCleanupResources_") + suffix;-
416 stream << QStringLiteral("int QT_MANGLE_NAMESPACE(%1)() {\n").arg(cleanupFunction);
never executed: return qstring_literal_temp;
0
417 if (!newResourceFile.isEmpty())
!newResourceFile.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
418 stream << " Q_CLEANUP_RESOURCE(" << qtResourceNameForFile(newResourceFile) << ");\n";
never executed: stream << " Q_CLEANUP_RESOURCE(" << qtResourceNameForFile(newResourceFile) << ");\n";
0
419 stream << " return 1;\n";-
420 stream << "}\n";-
421 }
never executed: end of block
0
422 }-
423-
424 QSaveFile f(outputFileName);-
425 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
!f.open(QIODev...ice::Truncate)Description
TRUEnever evaluated
FALSEnever evaluated
0
426 *errorString = f.errorString();-
427 return false;
never executed: return false;
0
428 }-
429-
430 if (f.write(generatedLoaderCode) != generatedLoaderCode.size()) {
f.write(genera...derCode.size()Description
TRUEnever evaluated
FALSEnever evaluated
0
431 *errorString = f.errorString();-
432 return false;
never executed: return false;
0
433 }-
434-
435 if (!f.commit()) {
!f.commit()Description
TRUEnever evaluated
FALSEnever evaluated
0
436 *errorString = f.errorString();-
437 return false;
never executed: return false;
0
438 }-
439-
440 return true;
never executed: return true;
0
441}-
Source codeSwitch to Preprocessed file

Generated by Squish Coco 4.2.0