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