1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qmetatypesjsonprocessor_p.h"
5
6#include "qanystringviewutils_p.h"
7#include "qqmltyperegistrarconstants_p.h"
8#include "qqmltyperegistrarutils_p.h"
9#include "qqmltypesclassdescription_p.h"
10#include "qqmltyperegistrarutils_p.h"
11
12#include <QtCore/qcborarray.h>
13#include <QtCore/qcbormap.h>
14#include <QtCore/qdir.h>
15#include <QtCore/qfile.h>
16#include <QtCore/qjsondocument.h>
17#include <QtCore/qqueue.h>
18
19QT_BEGIN_NAMESPACE
20
21using namespace Qt::StringLiterals;
22using namespace Constants;
23using namespace Constants::MetatypesDotJson;
24using namespace Constants::MetatypesDotJson::Qml;
25using namespace QAnyStringViewUtils;
26
27const MetaTypePrivate MetaType::s_empty;
28
29// TODO: This could be optimized to store the objects in a more compact way.
30std::vector<std::unique_ptr<MetaTypePrivate>> s_pool;
31
32static QCborValue fromJson(const QByteArray &json, QJsonParseError *error)
33{
34 const QJsonDocument jsonValue = QJsonDocument::fromJson(json, error);
35 if (jsonValue.isArray())
36 return QCborValue::fromJsonValue(v: jsonValue.array());
37 if (jsonValue.isObject())
38 return QCborValue::fromJsonValue(v: jsonValue.object());
39 return QCborValue();
40}
41
42QList<QAnyStringView> MetaTypesJsonProcessor::namespaces(const MetaType &classDef)
43{
44 const QAnyStringView unqualified = classDef.className();
45 const QAnyStringView qualified = classDef.qualifiedClassName();
46
47 QList<QAnyStringView> namespaces;
48 if (qualified != unqualified) {
49 namespaces = split(source: qualified, sep: "::"_L1);
50 Q_ASSERT(namespaces.last() == unqualified);
51 namespaces.pop_back();
52 }
53
54 return namespaces;
55}
56
57bool MetaTypesJsonProcessor::processTypes(const QStringList &files)
58{
59 for (const QString &source: files) {
60 if (m_seenMetaTypesFiles.hasSeen(s: QDir::cleanPath(path: source)))
61 continue;
62
63 QCborValue metaObjects;
64 {
65 QFile f(source);
66 if (!f.open(flags: QIODevice::ReadOnly)) {
67 error(fileName: source) << "Cannot open file for reading";
68 return false;
69 }
70 QJsonParseError parseError = {.offset: 0, .error: QJsonParseError::NoError};
71 metaObjects = fromJson(json: f.readAll(), error: &parseError);
72 if (parseError.error != QJsonParseError::NoError) {
73 error(fileName: source)
74 << "Failed to parse JSON:" << parseError.error
75 << parseError.errorString();
76 return false;
77 }
78 }
79
80 if (metaObjects.isArray()) {
81 const QCborArray metaObjectsArray = metaObjects.toArray();
82 for (const QCborValue &metaObject : metaObjectsArray) {
83 if (!metaObject.isMap()) {
84 error(fileName: source) << "JSON is not an object";
85 return false;
86 }
87
88 processTypes(types: metaObject.toMap());
89 }
90 } else if (metaObjects.isMap()) {
91 processTypes(types: metaObjects.toMap());
92 } else {
93 error(fileName: source) << "JSON is not an object or an array";
94 return false;
95 }
96 }
97
98 return true;
99}
100
101bool MetaTypesJsonProcessor::processForeignTypes(const QString &types)
102{
103 QFile typesFile(types);
104 if (!typesFile.open(flags: QIODevice::ReadOnly)) {
105 error(fileName: types) << "Cannot open foreign types file";
106 return false;
107 }
108
109 QJsonParseError parseError = {.offset: 0, .error: QJsonParseError::NoError};
110 QCborValue foreignMetaObjects = fromJson(json: typesFile.readAll(), error: &parseError);
111 if (parseError.error != QJsonParseError::NoError) {
112 error(fileName: types)
113 << "Failed to parse JSON:" << parseError.error
114 << parseError.errorString();
115 return false;
116 }
117
118 const QCborArray foreignObjectsArray = foreignMetaObjects.toArray();
119 for (const QCborValue &metaObject : foreignObjectsArray) {
120 if (!metaObject.isMap()) {
121 error(fileName: types) << "JSON is not an object";
122 return false;
123 }
124
125 processForeignTypes(types: metaObject.toMap());
126 }
127
128 return true;
129}
130
131bool MetaTypesJsonProcessor::processForeignTypes(const QStringList &foreignTypesFiles)
132{
133 bool success = true;
134
135 for (const QString &types : foreignTypesFiles) {
136 if (m_seenMetaTypesFiles.hasSeen(s: QDir::cleanPath(path: types)))
137 continue;
138
139 if (!processForeignTypes(types))
140 success = false;
141 }
142 return success;
143}
144
145template<typename String>
146static void sortStringList(QList<String> *list)
147{
148 std::sort(list->begin(), list->end());
149 const auto newEnd = std::unique(list->begin(), list->end());
150 list->erase(typename QList<String>::const_iterator(newEnd), list->constEnd());
151}
152
153void MetaTypesJsonProcessor::postProcessTypes()
154{
155 sortTypes(types&: m_types);
156}
157
158void MetaTypesJsonProcessor::postProcessForeignTypes()
159{
160 sortTypes(types&: m_foreignTypes);
161 sortStringList(list: &m_primitiveTypes);
162 sortStringList(list: &m_usingDeclarations);
163 addRelatedTypes();
164 sortStringList(list: &m_referencedTypes);
165 sortStringList(list: &m_includes);
166}
167
168QString MetaTypesJsonProcessor::extractRegisteredTypes() const
169{
170 QString registrationHelper;
171 for (const auto &obj: m_types) {
172 const QString className = obj.className().toString();
173 const QString qualifiedClassName = obj.qualifiedClassName().toString();
174 const QString foreignClassName = className + u"Foreign";
175 QStringList qmlElements;
176 QString qmlUncreatable;
177 QString qmlAttached;
178 bool isSingleton = false;
179 bool isExplicitlyUncreatable = false;
180 bool isNamespace = obj.kind() == MetaType::Kind::Namespace;
181 for (const ClassInfo &entry: obj.classInfos()) {
182 const auto name = entry.name;
183 const auto value = entry.value;
184 if (name == S_ELEMENT) {
185 if (value == S_AUTO) {
186 qmlElements.append(t: u"QML_NAMED_ELEMENT("_s + className + u")"_s);
187 } else if (value == S_ANONYMOUS) {
188 qmlElements.append(t: u"QML_ANONYMOUS"_s);
189 } else {
190 qmlElements.append(t: u"QML_NAMED_ELEMENT("_s + value.toString() + u")");
191 }
192 } else if (name == S_CREATABLE && value == S_FALSE) {
193 isExplicitlyUncreatable = true;
194 } else if (name == S_UNCREATABLE_REASON) {
195 qmlUncreatable = u"QML_UNCREATABLE(\""_s + value.toString() + u"\")";
196 } else if (name == S_ATTACHED) {
197 qmlAttached = u"QML_ATTACHED("_s + value.toString() + u")";
198 } else if (name == S_SINGLETON) {
199 isSingleton = true;
200 }
201 }
202 if (qmlElements.isEmpty())
203 continue; // no relevant entries found
204 const QString spaces = u" "_s;
205 if (isNamespace) {
206 registrationHelper += u"\nnamespace "_s + foreignClassName + u"{\n Q_NAMESPACE\n"_s;
207 registrationHelper += spaces + u"QML_FOREIGN_NAMESPACE(" + qualifiedClassName + u")\n"_s;
208 } else {
209 registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
210 registrationHelper += spaces + u"QML_FOREIGN(" + qualifiedClassName + u")\n"_s;
211 }
212 registrationHelper += spaces + qmlElements.join(sep: u"\n"_s) + u"\n"_s;
213 if (isSingleton)
214 registrationHelper += spaces + u"QML_SINGLETON\n"_s;
215 if (isExplicitlyUncreatable) {
216 if (qmlUncreatable.isEmpty())
217 registrationHelper += spaces + uR"(QML_UNCREATABLE(""))" + u"n";
218 else
219 registrationHelper += spaces + qmlUncreatable + u"\n";
220 }
221 if (!qmlAttached.isEmpty())
222 registrationHelper += spaces + qmlAttached + u"\n";
223 registrationHelper += u"}";
224 if (!isNamespace)
225 registrationHelper += u";";
226 registrationHelper += u"\n";
227 }
228 return registrationHelper;
229}
230
231MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess(
232 const MetaType &classDef, PopulateMode populateMode)
233{
234 // If this type is a self-extending value type or a sequence type or has a JavaScript extension
235 // and is not the root object, then it's foreign type has no entry of its own.
236 // In that case we need to generate a "primitive" entry.
237
238 QList<QAnyStringView> primitiveAliases;
239 UsingDeclaration usingDeclaration;
240
241 RegistrationMode mode = NoRegistration;
242 bool isSelfExtendingValueType = false;
243 bool hasJavaScriptExtension = false;
244 bool isRootObject = false;
245 bool isSequence = false;
246
247 for (const ClassInfo &classInfo : classDef.classInfos()) {
248 if (classInfo.name == S_FOREIGN)
249 usingDeclaration.alias = classInfo.value;
250 else if (classInfo.name == S_PRIMITIVE_ALIAS)
251 primitiveAliases.append(t: classInfo.value);
252 else if (classInfo.name == S_EXTENSION_IS_JAVA_SCRIPT)
253 hasJavaScriptExtension = (classInfo.value == S_TRUE);
254 else if (classInfo.name == S_EXTENDED && classDef.kind() == MetaType::Kind::Gadget)
255 isSelfExtendingValueType = classInfo.value == classDef.className();
256 else if (classInfo.name == S_ROOT)
257 isRootObject = (classInfo.value == S_TRUE);
258 else if (classInfo.name == S_SEQUENCE)
259 isSequence = true;
260 else if (classInfo.name == S_USING)
261 usingDeclaration.original = classInfo.value;
262 else if (populateMode == PopulateMode::Yes && classInfo.name == S_ELEMENT) {
263 switch (classDef.kind()) {
264 case MetaType::Kind::Object:
265 mode = ObjectRegistration;
266 break;
267 case MetaType::Kind::Gadget:
268 mode = GadgetRegistration;
269 break;
270 case MetaType::Kind::Namespace:
271 mode = NamespaceRegistration;
272 break;
273 default:
274 warning(classDef)
275 << "Not registering a classInfo which is neither an object,"
276 << "nor a gadget, nor a namespace:"
277 << classInfo.name.toString();
278 break;
279 }
280 }
281 }
282
283 return PreProcessResult {
284 .primitiveAliases: std::move(primitiveAliases),
285 .usingDeclaration: usingDeclaration,
286 .foreignPrimitive: (!isRootObject && (isSequence || isSelfExtendingValueType || hasJavaScriptExtension))
287 ? usingDeclaration.alias
288 : QAnyStringView(),
289 .mode: mode
290 };
291
292}
293
294// TODO: Remove this when QAnyStringView gets a proper qHash()
295static size_t qHash(QAnyStringView string, size_t seed = 0)
296{
297 return string.visit(v: [seed](auto view) {
298 if constexpr (std::is_same_v<decltype(view), QStringView>)
299 return qHash(view, seed);
300 if constexpr (std::is_same_v<decltype(view), QLatin1StringView>)
301 return qHash(view, seed);
302 if constexpr (std::is_same_v<decltype(view), QUtf8StringView>)
303 return qHash(key: QByteArrayView(view.data(), view.length()), seed);
304 });
305}
306
307static bool qualifiedClassNameLessThan(const MetaType &a, const MetaType &b)
308{
309 return a.qualifiedClassName() < b.qualifiedClassName();
310}
311
312enum class TypeRelation
313{
314 Base, Property, Argument, Return, Enum, Attached, SequenceValue, Extension
315};
316
317static QLatin1StringView typeRelationString(TypeRelation relation)
318{
319 switch (relation) {
320 case TypeRelation::Property: return "property"_L1;
321 case TypeRelation::Argument: return "argument"_L1;
322 case TypeRelation::Return: return "return"_L1;
323 case TypeRelation::Enum: return "enum"_L1;
324 case TypeRelation::Attached: return "attached"_L1;
325 case TypeRelation::SequenceValue: return "sequence value"_L1;
326 case TypeRelation::Extension: return "extension"_L1;
327 default:
328 break;
329 }
330
331 Q_UNREACHABLE_RETURN(QLatin1StringView());
332}
333
334void MetaTypesJsonProcessor::addRelatedTypes()
335{
336 QSet<QAnyStringView> processedRelatedNativeNames;
337 QSet<QAnyStringView> processedRelatedJavaScriptNames;
338 QSet<QAnyStringView> unresolvedForeignNames;
339 QQueue<MetaType> typeQueue;
340 typeQueue.append(l: m_types);
341
342 const auto addRelatedName
343 = [&](QAnyStringView relatedName, const QList<QAnyStringView> &namespaces) {
344 if (const FoundType related = QmlTypesClassDescription::findType(
345 types: m_types, foreign: m_foreignTypes, name: relatedName, namespaces)) {
346
347 if (!related.javaScript.isEmpty())
348 processedRelatedJavaScriptNames.insert(value: related.javaScript.qualifiedClassName());
349
350 if (!related.native.isEmpty())
351 processedRelatedNativeNames.insert(value: related.native.qualifiedClassName());
352
353 return true;
354 } else {
355 return false;
356 }
357 };
358
359 const auto addRelatedType = [&](const MetaType &type) {
360 const QAnyStringView qualifiedName = type.qualifiedClassName();
361 if (type.inputFile().isEmpty())
362 processedRelatedJavaScriptNames.insert(value: qualifiedName);
363 else
364 processedRelatedNativeNames.insert(value: qualifiedName);
365 };
366
367 // First mark all classes registered from this module as already processed.
368 for (const MetaType &type : std::as_const(t&: m_types)) {
369 addRelatedType(type);
370 for (const ClassInfo &obj : type.classInfos()) {
371 if (obj.name == S_FOREIGN) {
372 const QAnyStringView foreign = obj.value;
373 if (!addRelatedName(foreign, namespaces(classDef: type)))
374 unresolvedForeignNames.insert(value: foreign);
375 break;
376 }
377 }
378 }
379
380 // Then mark all classes registered from other modules as already processed.
381 // We don't want to generate them again for this module.
382 for (const MetaType &foreignType : std::as_const(t&: m_foreignTypes)) {
383 bool seenQmlPrefix = false;
384 for (const ClassInfo &obj : foreignType.classInfos()) {
385 const QAnyStringView name = obj.name;
386 if (!seenQmlPrefix && startsWith(whole: name, part: "QML."_L1)) {
387 addRelatedType(foreignType);
388 seenQmlPrefix = true;
389 }
390 if (name == S_FOREIGN
391 || name == S_EXTENDED
392 || name == S_ATTACHED
393 || name == S_SEQUENCE) {
394 ResolvedTypeAlias foreign(obj.value, m_usingDeclarations);
395 if (!addRelatedName(foreign.type, namespaces(classDef: foreignType)))
396 unresolvedForeignNames.insert(value: foreign.type);
397 }
398 }
399 }
400
401 const auto addReference
402 = [&](const MetaType &type, QSet<QAnyStringView> *processedRelatedNames,
403 FoundType::Origin origin) {
404 if (type.isEmpty())
405 return;
406 QAnyStringView qualifiedName = type.qualifiedClassName();
407 m_referencedTypes.append(t: qualifiedName);
408 const qsizetype size = processedRelatedNames->size();
409 processedRelatedNames->insert(value: qualifiedName);
410
411 if (processedRelatedNames->size() == size)
412 return;
413
414 typeQueue.enqueue(t: type);
415
416 if (origin == FoundType::OwnTypes)
417 return;
418
419 // Add to own types since we need it for our registrations.
420 const auto insert = std::lower_bound(
421 first: m_types.constBegin(), last: m_types.constEnd(), val: type,
422 comp: qualifiedClassNameLessThan);
423 m_types.insert(before: insert, t: type);
424
425 // We only add types to m_types of which we know we can reach them via the existing
426 // m_includes. We do not add to m_includes, because any further headers may not be
427 // #include'able.
428
429 // Remove from the foreign types to avoid the ODR warning.
430 const auto remove = std::equal_range(
431 first: m_foreignTypes.constBegin(), last: m_foreignTypes.constEnd(), val: type,
432 comp: qualifiedClassNameLessThan);
433 for (auto it = remove.first; it != remove.second; ++it) {
434 if (*it == type) {
435 m_foreignTypes.erase(pos: it);
436 break;
437 }
438 }
439 };
440
441 const auto addInterface
442 = [&](QAnyStringView typeName, const QList<QAnyStringView> &namespaces) {
443 if (const FoundType other = QmlTypesClassDescription::findType(
444 types: m_types, foreign: m_foreignTypes, name: typeName, namespaces)) {
445 if (!other.native.isEmpty()) {
446 addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
447 return true;
448 }
449 } else {
450 // Do not warn about unresolved interfaces.
451 // They don't have to have Q_OBJECT or Q_GADGET.
452 unresolvedForeignNames.insert(value: typeName);
453 }
454
455 processedRelatedNativeNames.insert(value: typeName);
456 return false;
457 };
458
459 const auto doAddReferences = [&](QAnyStringView typeName,
460 const QList<QAnyStringView> &namespaces) {
461 if (const FoundType other = QmlTypesClassDescription::findType(
462 types: m_types, foreign: m_foreignTypes, name: typeName, namespaces)) {
463 addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
464 addReference(
465 other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin);
466 return true;
467 }
468
469 return false;
470 };
471
472 const auto addType = [&](const MetaType &context, QAnyStringView typeName,
473 const QList<QAnyStringView> &namespaces, TypeRelation relation) {
474 if (doAddReferences(typeName, namespaces))
475 return true;
476
477 // If it's an enum, add the surrounding type.
478 const QLatin1StringView separator("::");
479 if (const qsizetype index = lastIndexOf(whole: typeName, part: separator); index > 0) {
480 if (const FoundType other = QmlTypesClassDescription::findType(
481 types: m_types, foreign: m_foreignTypes, name: typeName.left(n: index), namespaces)) {
482
483 const QAnyStringView enumName = typeName.mid(pos: index + separator.length());
484
485 for (const Enum &enumerator : other.native.enums()) {
486 if (enumerator.name != enumName && enumerator.alias != enumName)
487 continue;
488
489 addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
490 addReference(
491 other.javaScript, &processedRelatedJavaScriptNames,
492 other.javaScriptOrigin);
493 return true;
494 }
495 }
496 }
497
498 // If it's an enum of the context type itself, we don't have to do anything.
499 for (const Enum &enumerator : context.enums()) {
500 if (enumerator.name == typeName || enumerator.alias == typeName)
501 return true;
502 }
503
504 // If we've detected this type as unresolved foreign and it actually belongs to this module,
505 // we'll get to it again when we process it as foreign type. In that case we'll look at the
506 // special cases for sequences and extensions.
507 if (!unresolvedForeignNames.contains(value: typeName) && !isPrimitive(type: typeName)) {
508 warning(classDef: context) << typeName << "is used as" << typeRelationString(relation)
509 << "type but cannot be found.";
510 }
511
512 processedRelatedNativeNames.insert(value: typeName);
513 processedRelatedJavaScriptNames.insert(value: typeName);
514 return false;
515 };
516
517
518
519 const auto addSupers = [&](const MetaType &context, const QList<QAnyStringView> &namespaces) {
520 for (const Interface &iface : context.ifaces())
521 addInterface(interfaceName(iface), namespaces);
522
523 // We don't warn about missing bases for value types. They don't have to be registered.
524 bool warnAboutSupers = context.kind() != MetaType::Kind::Gadget;
525
526 QList<QAnyStringView> missingSupers;
527
528 for (const BaseType &superObject : context.superClasses()) {
529 if (superObject.access != Access::Public)
530 continue;
531
532 QAnyStringView typeName = superObject.name;
533 if (doAddReferences(typeName, namespaces))
534 warnAboutSupers = false;
535 else
536 missingSupers.append(t: typeName);
537 }
538
539 for (QAnyStringView typeName : std::as_const(t&: missingSupers)) {
540 // If we've found one valid base type, don't complain about the others.
541 if (warnAboutSupers
542 && !unresolvedForeignNames.contains(value: typeName)
543 && !isPrimitive(type: typeName)) {
544 warning(classDef: context) << typeName << "is used as base type but cannot be found.";
545 }
546
547 processedRelatedNativeNames.insert(value: typeName);
548 processedRelatedJavaScriptNames.insert(value: typeName);
549 }
550 };
551
552 const auto addEnums = [&](const MetaType &context,
553 const QList<QAnyStringView> &namespaces) {
554 for (const Enum &enumerator : context.enums()) {
555 ResolvedTypeAlias resolved(enumerator.type, m_usingDeclarations);
556 if (!resolved.type.isEmpty())
557 addType(context, resolved.type, namespaces, TypeRelation::Enum);
558 }
559 };
560
561 const auto addRelation = [&](const MetaType &classDef, const ClassInfo &obj,
562 const QList<QAnyStringView> &namespaces) {
563 const QAnyStringView objNameValue = obj.name;
564 if (objNameValue == S_ATTACHED) {
565 addType(classDef, obj.value, namespaces, TypeRelation::Attached);
566 return true;
567 } else if (objNameValue == S_SEQUENCE) {
568 ResolvedTypeAlias value(obj.value, m_usingDeclarations);
569 addType(classDef, value.type, namespaces, TypeRelation::SequenceValue);
570 return true;
571 } else if (objNameValue == S_EXTENDED) {
572 const QAnyStringView value = obj.value;
573 addType(classDef, value, namespaces, TypeRelation::Extension);
574 return true;
575 }
576 return false;
577 };
578
579 // Then recursively iterate the super types and attached types, marking the
580 // ones we are interested in as related.
581 while (!typeQueue.isEmpty()) {
582 QAnyStringView unresolvedForeign;
583
584 const MetaType classDef = typeQueue.dequeue();
585 const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
586
587 for (const ClassInfo &obj : classDef.classInfos()) {
588 if (addRelation(classDef, obj, namespaces))
589 continue;
590 if (obj.name != S_FOREIGN)
591 continue;
592
593 const QAnyStringView foreignClassName = obj.value;
594
595 // A type declared as QML_FOREIGN will usually be a foreign type, but it can
596 // actually be an additional registration of a local type, too.
597 if (const FoundType found = QmlTypesClassDescription::findType(
598 types: m_foreignTypes, foreign: {}, name: foreignClassName, namespaces)) {
599 const MetaType other = found.select(category: classDef, relation: "Foreign");
600 const QList<QAnyStringView> otherNamespaces
601 = MetaTypesJsonProcessor::namespaces(classDef: other);
602 addSupers(other, otherNamespaces);
603 addEnums(other, otherNamespaces);
604
605 for (const ClassInfo &obj : other.classInfos()) {
606 if (addRelation(classDef, obj, otherNamespaces))
607 break;
608 // No, you cannot chain S_FOREIGN declarations. Sorry.
609 }
610 } else if (!QmlTypesClassDescription::findType(
611 types: m_types, foreign: {}, name: foreignClassName, namespaces)) {
612 unresolvedForeign = foreignClassName;
613 }
614 }
615
616 if (!unresolvedForeign.isEmpty() && !isPrimitive(type: unresolvedForeign)) {
617 warning(classDef)
618 << unresolvedForeign
619 << "is declared as foreign type, but cannot be found.";
620 }
621
622 addSupers(classDef, namespaces);
623 addEnums(classDef, namespaces);
624 }
625}
626
627void MetaTypesJsonProcessor::sortTypes(QVector<MetaType> &types)
628{
629 std::sort(first: types.begin(), last: types.end(), comp: qualifiedClassNameLessThan);
630}
631
632QString MetaTypesJsonProcessor::resolvedInclude(QAnyStringView include)
633{
634 if (!m_privateIncludes)
635 return include.toString();
636
637 if (endsWith(whole: include, part: "_p.h"_L1))
638 return QLatin1String("private/") + include.toString();
639
640 if (startsWith(whole: include, part: "qplatform"_L1) || startsWith(whole: include, part: "qwindowsystem"_L1))
641 return QLatin1String("qpa/") + include.toString();
642
643 return include.toString();
644}
645
646void MetaTypesJsonProcessor::processTypes(const QCborMap &types)
647{
648 const QString include = resolvedInclude(include: types[S_INPUT_FILE].toStringView());
649 const QCborArray classes = types[S_CLASSES].toArray();
650 for (const QCborValue &cls : classes) {
651 const MetaType classDef(cls.toMap(), include);
652
653 const PreProcessResult preprocessed = preProcess(classDef, populateMode: PopulateMode::Yes);
654 switch (preprocessed.mode) {
655 case NamespaceRegistration:
656 case GadgetRegistration:
657 case ObjectRegistration: {
658 if (!endsWith(whole: include, part: QLatin1String(".h"))
659 && !endsWith(whole: include, part: QLatin1String(".hpp"))
660 && !endsWith(whole: include, part: QLatin1String(".hxx"))
661 && !endsWith(whole: include, part: QLatin1String(".hh"))
662 && !endsWith(whole: include, part: QLatin1String(".py"))
663 && contains(whole: include, part: QLatin1Char('.'))) {
664 warning(fileName: include)
665 << "Class" << classDef.qualifiedClassName()
666 << "is declared in" << include << "which appears not to be a header."
667 << "The compilation of its registration to QML may fail.";
668 }
669 m_includes.append(t: include);
670 m_types.emplaceBack(args: classDef);
671 break;
672 }
673 case NoRegistration:
674 m_foreignTypes.emplaceBack(args: classDef);
675 break;
676 }
677
678 if (!preprocessed.foreignPrimitive.isEmpty()) {
679 m_primitiveTypes.emplaceBack(args: preprocessed.foreignPrimitive);
680 m_primitiveTypes.append(l: preprocessed.primitiveAliases);
681 }
682
683 if (preprocessed.usingDeclaration.isValid())
684 m_usingDeclarations.append(t: preprocessed.usingDeclaration);
685 }
686}
687
688void MetaTypesJsonProcessor::processForeignTypes(const QCborMap &types)
689{
690 const QString include = resolvedInclude(include: types[S_INPUT_FILE].toStringView());
691 const QCborArray classes = types[S_CLASSES].toArray();
692 for (const QCborValue &cls : classes) {
693 const MetaType classDef(cls.toMap(), include);
694 PreProcessResult preprocessed = preProcess(classDef, populateMode: PopulateMode::No);
695
696 m_foreignTypes.emplaceBack(args: classDef);
697 if (!preprocessed.foreignPrimitive.isEmpty()) {
698 m_primitiveTypes.emplaceBack(args&: preprocessed.foreignPrimitive);
699 m_primitiveTypes.append(l: preprocessed.primitiveAliases);
700 }
701
702 if (preprocessed.usingDeclaration.isValid())
703 m_usingDeclarations.append(t: preprocessed.usingDeclaration);
704 }
705}
706
707static QTypeRevision getRevision(const QCborMap &cbor)
708{
709 const auto it = cbor.find(key: S_REVISION);
710 return it == cbor.end()
711 ? QTypeRevision()
712 : QTypeRevision::fromEncodedVersion(value: it->toInteger());
713}
714
715static Access getAccess(const QCborMap &cbor)
716{
717 const QAnyStringView access = cbor[S_ACCESS].toStringView();
718 if (access == S_PUBLIC)
719 return Access::Public;
720 if (access == S_PROTECTED)
721 return Access::Protected;
722 return Access::Private;
723}
724
725BaseType::BaseType(const QCborMap &cbor)
726 : name(cbor[S_NAME].toStringView())
727 , access(getAccess(cbor))
728{
729}
730
731ClassInfo::ClassInfo(const QCborMap &cbor)
732 : name(cbor[S_NAME].toStringView())
733 , value(cbor[S_VALUE].toStringView())
734{
735}
736
737Interface::Interface(const QCborValue &cbor)
738{
739 if (cbor.isArray()) {
740 QCborArray needlessWrapping = cbor.toArray();
741 className = needlessWrapping.size() > 0
742 ? needlessWrapping[0].toMap()[S_CLASS_NAME].toStringView()
743 : QAnyStringView();
744 } else {
745 className = cbor.toMap()[S_CLASS_NAME].toStringView();
746 }
747}
748
749Property::Property(const QCborMap &cbor)
750 : name(cbor[S_NAME].toStringView())
751 , type(cbor[S_TYPE].toStringView())
752 , member(cbor[S_MEMBER].toStringView())
753 , read(cbor[S_READ].toStringView())
754 , write(cbor[S_WRITE].toStringView())
755 , reset(cbor[S_RESET].toStringView())
756 , notify(cbor[S_NOTIFY].toStringView())
757 , bindable(cbor[S_BINDABLE].toStringView())
758 , privateClass(cbor[S_PRIVATE_CLASS].toStringView())
759 , index(cbor[S_INDEX].toInteger(defaultValue: -1))
760 , revision(getRevision(cbor))
761 , isFinal(cbor[S_FINAL].toBool())
762 , isConstant(cbor[S_CONSTANT].toBool())
763 , isRequired(cbor[S_REQUIRED].toBool())
764{
765}
766
767Argument::Argument(const QCborMap &cbor)
768 : name(cbor[S_NAME].toStringView())
769 , type(cbor[S_TYPE].toStringView())
770{
771}
772
773Method::Method(const QCborMap &cbor, bool isConstructor)
774 : name(cbor[S_NAME].toStringView())
775 , returnType(cbor[S_RETURN_TYPE].toStringView())
776 , index(cbor[S_INDEX].toInteger(defaultValue: InvalidIndex))
777 , revision(getRevision(cbor))
778 , access(getAccess(cbor))
779 , isCloned(cbor[S_IS_CLONED].toBool())
780 , isJavaScriptFunction(cbor[S_IS_JAVASCRIPT_FUNCTION].toBool())
781 , isConstructor(isConstructor || cbor[S_IS_CONSTRUCTOR].toBool())
782 , isConst(cbor[S_IS_CONST].toBool())
783{
784 const QCborArray args = cbor[S_ARGUMENTS].toArray();
785 for (const QCborValue &argument : args)
786 arguments.emplace_back(args: argument.toMap());
787
788 if (arguments.size() == 1) {
789 const QAnyStringView type = arguments[0].type;
790 if (type == "QQmlV4FunctionPtr"_L1 || type == "QQmlV4Function*"_L1) {
791 isJavaScriptFunction = true;
792 arguments.clear();
793 }
794 }
795}
796
797Enum::Enum(const QCborMap &cbor)
798 : name(cbor[S_NAME].toStringView())
799 , alias(cbor[S_ALIAS].toStringView())
800 , type(cbor[S_TYPE].toStringView())
801 , isFlag(cbor[S_IS_FLAG].toBool())
802 , isClass(cbor[S_IS_CLASS].toBool())
803{
804 const QCborArray vals = cbor[S_VALUES].toArray();
805 for (const QCborValue &value : vals)
806 values.emplace_back(args: value.toStringView());
807}
808
809MetaTypePrivate::MetaTypePrivate(const QCborMap &cbor, const QString &inputFile)
810 : cbor(cbor)
811 , inputFile(inputFile)
812{
813 className = cbor[S_CLASS_NAME].toStringView();
814 lineNumber = cbor[S_LINENUMBER].toInteger(defaultValue: 0);
815 qualifiedClassName = cbor[S_QUALIFIED_CLASS_NAME].toStringView();
816
817 const QCborArray cborSuperClasses = cbor[S_SUPER_CLASSES].toArray();
818 for (const QCborValue &superClass : cborSuperClasses)
819 superClasses.emplace_back(args: superClass.toMap());
820
821 const QCborArray cborClassInfos = cbor[S_CLASS_INFOS].toArray();
822 for (const QCborValue &classInfo : cborClassInfos)
823 classInfos.emplace_back(args: classInfo.toMap());
824
825 const QCborArray cborIfaces = cbor[S_INTERFACES].toArray();
826 for (const QCborValue &iface : cborIfaces)
827 ifaces.emplace_back(args: iface);
828
829 const QCborArray cborProperties = cbor[S_PROPERTIES].toArray();
830 for (const QCborValue &property : cborProperties)
831 properties.emplace_back(args: property.toMap());
832
833 for (const QCborArray &cborMethods : { cbor[S_SLOTS].toArray(), cbor[S_METHODS].toArray() }) {
834 for (const QCborValue &method : cborMethods)
835 methods.emplace_back(args: method.toMap(), args: false);
836 }
837
838 const QCborArray cborSigs = cbor[S_SIGNALS].toArray();
839 for (const QCborValue &sig : cborSigs)
840 sigs.emplace_back(args: sig.toMap(), args: false);
841
842 const QCborArray cborConstructors = cbor[S_CONSTRUCTORS].toArray();
843 for (const QCborValue &constructor : cborConstructors)
844 constructors.emplace_back(args: constructor.toMap(), args: true);
845
846 const QCborArray cborEnums = cbor[S_ENUMS].toArray();
847 for (const QCborValue &enumerator : cborEnums)
848 enums.emplace_back(args: enumerator.toMap());
849
850 if (cbor[S_GADGET].toBool())
851 kind = Kind::Gadget;
852 else if (cbor[S_OBJECT].toBool())
853 kind = Kind::Object;
854 else if (cbor[S_NAMESPACE].toBool())
855 kind = Kind::Namespace;
856}
857
858MetaType::MetaType(const QCborMap &cbor, const QString &inputFile)
859 : d(s_pool.emplace_back(args: std::make_unique<MetaTypePrivate>(args: cbor, args: inputFile)).get())
860{}
861
862QT_END_NAMESPACE
863

source code of qtdeclarative/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp