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
344
346{
347 switch (relation) {
348 case TypeRelation::Property: return "property"_L1;
349 case TypeRelation::Argument: return "argument"_L1;
350 case TypeRelation::Return: return "return"_L1;
351 case TypeRelation::Enum: return "enum"_L1;
352 case TypeRelation::Attached: return "attached"_L1;
353 case TypeRelation::SequenceValue: return "sequence value"_L1;
354 case TypeRelation::Extension: return "extension"_L1;
355 default:
356 break;
357 }
358
359 Q_UNREACHABLE_RETURN(QLatin1StringView());
360}
361
362void MetaTypesJsonProcessor::addRelatedTypes()
363{
364 QSet<QAnyStringView> processedRelatedNativeNames;
365 QSet<QAnyStringView> processedRelatedJavaScriptNames;
366 QSet<QAnyStringView> unresolvedForeignNames;
367 QQueue<MetaType> typeQueue;
368 typeQueue.append(m_types);
369
370 const auto addRelatedName
371 = [&](QAnyStringView relatedName, const QList<QAnyStringView> &namespaces) {
372 if (const FoundType related = QmlTypesClassDescription::findType(
373 m_types, m_foreignTypes, relatedName, namespaces)) {
374
375 if (!related.javaScript.isEmpty())
376 processedRelatedJavaScriptNames.insert(related.javaScript.qualifiedClassName());
377
378 if (!related.native.isEmpty())
379 processedRelatedNativeNames.insert(related.native.qualifiedClassName());
380
381 return true;
382 } else {
383 return false;
384 }
385 };
386
387 const auto addRelatedType = [&](const MetaType &type) {
388 const QAnyStringView qualifiedName = type.qualifiedClassName();
389 if (type.inputFile().isEmpty())
390 processedRelatedJavaScriptNames.insert(qualifiedName);
391 else
392 processedRelatedNativeNames.insert(qualifiedName);
393 };
394
395 // First mark all classes registered from this module as already processed.
396 for (const MetaType &type : std::as_const(m_types)) {
397 addRelatedType(type);
398 for (const ClassInfo &obj : type.classInfos()) {
399 if (obj.name == S_FOREIGN) {
400 const QAnyStringView foreign = obj.value;
401 if (!addRelatedName(foreign, namespaces(type)))
402 unresolvedForeignNames.insert(foreign);
403 break;
404 }
405 }
406 }
407
408 // Then mark all classes registered from other modules as already processed.
409 // We don't want to generate them again for this module.
410 for (const MetaType &foreignType : std::as_const(m_foreignTypes)) {
411 bool seenQmlPrefix = false;
412 for (const ClassInfo &obj : foreignType.classInfos()) {
413 const QAnyStringView name = obj.name;
414 if (!seenQmlPrefix && startsWith(name, "QML."_L1)) {
415 addRelatedType(foreignType);
416 seenQmlPrefix = true;
417 }
418 if (name == S_FOREIGN
419 || name == S_EXTENDED
420 || name == S_ATTACHED
421 || name == S_SEQUENCE) {
422 ResolvedTypeAlias foreign(obj.value, m_usingDeclarations);
423 if (!addRelatedName(foreign.type, namespaces(foreignType)))
424 unresolvedForeignNames.insert(foreign.type);
425 }
426 }
427 }
428
429 const auto addReference
430 = [&](const MetaType &type, QSet<QAnyStringView> *processedRelatedNames,
431 FoundType::Origin origin, TypeRelation relation) {
432 if (type.isEmpty())
433 return;
434 QAnyStringView qualifiedName = type.qualifiedClassName();
435 m_referencedTypes.append(qualifiedName);
436 const qsizetype size = processedRelatedNames->size();
437 processedRelatedNames->insert(qualifiedName);
438
439 if (processedRelatedNames->size() == size) {
440 // a type might change from opaque to non-opaque
441
442 // Remove from opaque types - might happen if the type is used as both a property
443 // and e.g. as a base type
444 switch (relation) {
448 // still only opaque, we can return
449 return;
450 }
451 default:
452 break;
453 }
454 if (removeFromSortedMetaTypeList(m_opaqueTypes, type) == NotFound)
455 return; // type used in two non-opaque usages; e.g. Base + Extension
456 } else {
457 typeQueue.enqueue(type);
458 }
459
460 if (origin == FoundType::OwnTypes)
461 return;
462
463 switch (relation) {
467 insertIntoSortedMetaTypeList(m_opaqueTypes, type);
468 return;
469 }
470 default:
471 break;
472 }
473
474 // Add to own types since we need it for our registrations.
475 insertIntoSortedMetaTypeList(m_types, type);
476
477 // We only add types to m_types of which we know we can reach them via the existing
478 // m_includes. We do not add to m_includes, because any further headers may not be
479 // #include'able.
480
481 // Remove from the foreign types to avoid the ODR warning.
482 removeFromSortedMetaTypeList(m_foreignTypes, type);
483 };
484
485 const auto addInterface
486 = [&](QAnyStringView typeName, const QList<QAnyStringView> &namespaces,
487 TypeRelation relation) {
488 if (const FoundType other = QmlTypesClassDescription::findType(
489 m_types, m_foreignTypes, typeName, namespaces)) {
490 if (!other.native.isEmpty()) {
491 addReference(
492 other.native, &processedRelatedNativeNames, other.nativeOrigin, relation);
493 return true;
494 }
495 } else {
496 // Do not warn about unresolved interfaces.
497 // They don't have to have Q_OBJECT or Q_GADGET.
498 unresolvedForeignNames.insert(typeName);
499 }
500
501 processedRelatedNativeNames.insert(typeName);
502 return false;
503 };
504
505 const auto doAddReferences = [&](QAnyStringView typeName,
506 const QList<QAnyStringView> &namespaces,
507 TypeRelation relation) {
508 if (const FoundType other = QmlTypesClassDescription::findType(
509 m_types, m_foreignTypes, typeName, namespaces)) {
510 addReference(
511 other.native, &processedRelatedNativeNames, other.nativeOrigin, relation);
512 addReference(
513 other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin,
514 relation);
515 return true;
516 }
517
518 return false;
519 };
520
521 const auto addType = [&](const MetaType &context, QAnyStringView typeName,
522 const QList<QAnyStringView> &namespaces, TypeRelation relation) {
523 if (doAddReferences(typeName, namespaces, relation))
524 return true;
525
526 // If it's an enum, add the surrounding type.
527 const QLatin1StringView separator("::");
528 if (const qsizetype index = lastIndexOf(typeName, separator); index > 0) {
529 if (const FoundType other = QmlTypesClassDescription::findType(
530 m_types, m_foreignTypes, typeName.left(index), namespaces)) {
531
532 const QAnyStringView enumName = typeName.mid(index + separator.length());
533
534 for (const Enum &enumerator : other.native.enums()) {
535 if (enumerator.name != enumName && enumerator.alias != enumName)
536 continue;
537
538 addReference(
539 other.native, &processedRelatedNativeNames, other.nativeOrigin,
540 relation);
541 addReference(
542 other.javaScript, &processedRelatedJavaScriptNames,
543 other.javaScriptOrigin, relation);
544 return true;
545 }
546 }
547 }
548
549 // If it's an enum of the context type itself, we don't have to do anything.
550 for (const Enum &enumerator : context.enums()) {
551 if (enumerator.name == typeName || enumerator.alias == typeName)
552 return true;
553 }
554
555 // If we've detected this type as unresolved foreign and it actually belongs to this module,
556 // we'll get to it again when we process it as foreign type. In that case we'll look at the
557 // special cases for sequences and extensions.
558 if (!unresolvedForeignNames.contains(typeName) && !isPrimitive(typeName)) {
559 // Also, if the type is used as a property/method argument, we want to still make it
560 // known to tooling as an opaque type, and don't warn about it
561 switch (relation) {
565 MetaType type = MetaType::createOpaqueType(typeName);
566 insertIntoSortedMetaTypeList(m_opaqueTypes, type);
567 break;
568 }
569 default:
570 warning(context) << typeName << "is used as" << typeRelationString(relation)
571 << "type but cannot be found.";
572 }
573 }
574
575 processedRelatedNativeNames.insert(typeName);
576 processedRelatedJavaScriptNames.insert(typeName);
577 return false;
578 };
579
580
581
582 const auto addSupers = [&](const MetaType &context, const QList<QAnyStringView> &namespaces) {
583 for (const Interface &iface : context.ifaces())
584 addInterface(interfaceName(iface), namespaces, TypeRelation::Base);
585
586 // We don't warn about missing bases for value types. They don't have to be registered.
587 bool warnAboutSupers = context.kind() != MetaType::Kind::Gadget;
588
589 QList<QAnyStringView> missingSupers;
590
591 for (const BaseType &superObject : context.superClasses()) {
592 if (superObject.access != Access::Public)
593 continue;
594
595 QAnyStringView typeName = superObject.name;
596 if (doAddReferences(typeName, namespaces, TypeRelation::Base))
597 warnAboutSupers = false;
598 else
599 missingSupers.append(typeName);
600 }
601
602 for (QAnyStringView typeName : std::as_const(missingSupers)) {
603 // If we've found one valid base type, don't complain about the others.
604 if (warnAboutSupers
605 && !unresolvedForeignNames.contains(typeName)
606 && !isPrimitive(typeName)) {
607 warning(context) << typeName << "is used as base type but cannot be found.";
608 }
609
610 processedRelatedNativeNames.insert(typeName);
611 processedRelatedJavaScriptNames.insert(typeName);
612 }
613 };
614
615 const auto addProperties = [&](const MetaType &context,
616 const QList<QAnyStringView> &namespaces) {
617 for (const Property &property : context.properties()) {
618 ResolvedTypeAlias resolved(property.type, m_usingDeclarations);
619 if (!resolved.type.isEmpty())
620 addType(context, resolved.type, namespaces, TypeRelation::Property);
621 }
622 };
623
624 const auto addMethods = [&](const MetaType &context,
625 const QList<QAnyStringView> &namespaces) {
626 for (const Method::Container &methods
627 : {context.methods(), context.constructors(), context.sigs() }) {
628 for (const Method &methodObject : methods) {
629 if (methodObject.access != Access::Public)
630 continue;
631 for (const Argument &argument : std::as_const(methodObject.arguments)) {
632 ResolvedTypeAlias resolved(argument.type, m_usingDeclarations);
633 if (!resolved.type.isEmpty())
634 addType(context, resolved.type, namespaces, TypeRelation::Argument);
635 }
636
637 ResolvedTypeAlias resolved(methodObject.returnType, m_usingDeclarations);
638 if (!resolved.type.isEmpty())
639 addType(context, resolved.type, namespaces, TypeRelation::Return);
640 }
641 }
642 };
643
644 const auto addEnums = [&](const MetaType &context,
645 const QList<QAnyStringView> &namespaces) {
646 for (const Enum &enumerator : context.enums()) {
647 ResolvedTypeAlias resolved(enumerator.type, m_usingDeclarations);
648 if (!resolved.type.isEmpty())
649 addType(context, resolved.type, namespaces, TypeRelation::Enum);
650 }
651 };
652
653 const auto addRelation = [&](const MetaType &classDef, const ClassInfo &obj,
654 const QList<QAnyStringView> &namespaces) {
655 const QAnyStringView objNameValue = obj.name;
656 if (objNameValue == S_ATTACHED) {
657 addType(classDef, obj.value, namespaces, TypeRelation::Attached);
658 return true;
659 } else if (objNameValue == S_SEQUENCE) {
660 ResolvedTypeAlias value(obj.value, m_usingDeclarations);
661 addType(classDef, value.type, namespaces, TypeRelation::SequenceValue);
662 return true;
663 } else if (objNameValue == S_EXTENDED) {
664 const QAnyStringView value = obj.value;
665 addType(classDef, value, namespaces, TypeRelation::Extension);
666 return true;
667 }
668 return false;
669 };
670
671 // Then recursively iterate the super types and attached types, marking the
672 // ones we are interested in as related.
673 while (!typeQueue.isEmpty()) {
674 QAnyStringView unresolvedForeign;
675
676 const MetaType classDef = typeQueue.dequeue();
677 const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
678
679 for (const ClassInfo &obj : classDef.classInfos()) {
680 if (addRelation(classDef, obj, namespaces))
681 continue;
682 if (obj.name != S_FOREIGN)
683 continue;
684
685 const QAnyStringView foreignClassName = obj.value;
686
687 // A type declared as QML_FOREIGN will usually be a foreign type, but it can
688 // actually be an additional registration of a local type, too.
689 if (const FoundType found = QmlTypesClassDescription::findType(
690 m_foreignTypes, {}, foreignClassName, namespaces)) {
691 const MetaType other = found.select(classDef, "Foreign");
692 const QList<QAnyStringView> otherNamespaces
693 = MetaTypesJsonProcessor::namespaces(other);
694 addSupers(other, otherNamespaces);
695 addProperties(other, otherNamespaces);
696 addMethods(other, otherNamespaces);
697 addEnums(other, otherNamespaces);
698
699 for (const ClassInfo &obj : other.classInfos()) {
700 if (addRelation(classDef, obj, otherNamespaces))
701 break;
702 // No, you cannot chain S_FOREIGN declarations. Sorry.
703 }
704 } else if (!QmlTypesClassDescription::findType(
705 m_types, {}, foreignClassName, namespaces)) {
706 unresolvedForeign = foreignClassName;
707 }
708 }
709
710 if (!unresolvedForeign.isEmpty() && !isPrimitive(unresolvedForeign)) {
711 warning(classDef)
712 << unresolvedForeign
713 << "is declared as foreign type, but cannot be found.";
714 }
715
716 addSupers(classDef, namespaces);
717 addProperties(classDef, namespaces);
718 addMethods(classDef, namespaces);
719 addEnums(classDef, namespaces);
720 }
721}
722
723void MetaTypesJsonProcessor::sortTypes(QList<MetaType> &types)
724{
725 std::sort(types.begin(), types.end(), qualifiedClassNameLessThan);
726}
727
728QString MetaTypesJsonProcessor::resolvedInclude(QAnyStringView include)
729{
730 if (!m_privateIncludes)
731 return include.toString();
732
733 if (endsWith(include, "_p.h"_L1))
734 return QLatin1String("private/") + include.toString();
735
736 if (startsWith(include, "qplatform"_L1) || startsWith(include, "qwindowsystem"_L1))
737 return QLatin1String("qpa/") + include.toString();
738
739 return include.toString();
740}
741
742void MetaTypesJsonProcessor::processTypes(const QCborMap &types)
743{
744 const QString include = resolvedInclude(types[S_INPUT_FILE].toStringView());
745 const QCborArray classes = types[S_CLASSES].toArray();
746 for (const QCborValue &cls : classes) {
747 const MetaType classDef(cls.toMap(), include);
748
749 const PreProcessResult preprocessed = preProcess(classDef, PopulateMode::Yes);
750 switch (preprocessed.mode) {
751 case NamespaceRegistration:
752 case GadgetRegistration:
753 case ObjectRegistration: {
754 if (!endsWith(include, QLatin1String(".h"))
755 && !endsWith(include, QLatin1String(".hpp"))
756 && !endsWith(include, QLatin1String(".hxx"))
757 && !endsWith(include, QLatin1String(".hh"))
758 && !endsWith(include, QLatin1String(".py"))
759 && contains(include, QLatin1Char('.'))) {
760 warning(include)
761 << "Class" << classDef.qualifiedClassName()
762 << "is declared in" << include << "which appears not to be a header."
763 << "The compilation of its registration to QML may fail.";
764 }
765 m_includes.append(include);
766 m_types.emplaceBack(classDef);
767 break;
768 }
769 case NoRegistration:
770 m_foreignTypes.emplaceBack(classDef);
771 break;
772 }
773
774 if (!preprocessed.foreignPrimitive.isEmpty()) {
775 m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
776 m_primitiveTypes.append(preprocessed.primitiveAliases);
777 }
778
779 if (preprocessed.usingDeclaration.isValid())
780 m_usingDeclarations.append(preprocessed.usingDeclaration);
781 }
782}
783
784void MetaTypesJsonProcessor::processForeignTypes(const QCborMap &types)
785{
786 const QString include = resolvedInclude(types[S_INPUT_FILE].toStringView());
787 const QCborArray classes = types[S_CLASSES].toArray();
788 for (const QCborValue &cls : classes) {
789 const MetaType classDef(cls.toMap(), include);
790 PreProcessResult preprocessed = preProcess(classDef, PopulateMode::No);
791
792 m_foreignTypes.emplaceBack(classDef);
793 if (!preprocessed.foreignPrimitive.isEmpty()) {
794 m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
795 m_primitiveTypes.append(preprocessed.primitiveAliases);
796 }
797
798 if (preprocessed.usingDeclaration.isValid())
799 m_usingDeclarations.append(preprocessed.usingDeclaration);
800 }
801}
802
803static QTypeRevision getRevision(const QCborMap &cbor)
804{
805 const auto it = cbor.find(S_REVISION);
806 return it == cbor.end()
807 ? QTypeRevision()
808 : QTypeRevision::fromEncodedVersion(it->toInteger());
809}
810
811static Access getAccess(const QCborMap &cbor)
812{
813 const QAnyStringView access = cbor[S_ACCESS].toStringView();
814 if (access == S_PUBLIC)
815 return Access::Public;
816 if (access == S_PROTECTED)
817 return Access::Protected;
818 return Access::Private;
819}
820
821BaseType::BaseType(const QCborMap &cbor)
824{
825}
826
827ClassInfo::ClassInfo(const QCborMap &cbor)
830{
831}
832
833Interface::Interface(const QCborValue &cbor)
834{
835 if (cbor.isArray()) {
836 QCborArray needlessWrapping = cbor.toArray();
837 className = needlessWrapping.size() > 0
838 ? needlessWrapping[0].toMap()[S_CLASS_NAME].toStringView()
839 : QAnyStringView();
840 } else {
841 className = cbor.toMap()[S_CLASS_NAME].toStringView();
842 }
843}
844
845Property::Property(const QCborMap &cbor)
855 , index(cbor[S_INDEX].toInteger(-1))
856 , lineNumber(cbor[S_LINENUMBER].toInteger(0))
858 , isFinal(cbor[S_FINAL].toBool())
859 , isVirtual(cbor[S_VIRTUAL].toBool())
860 , isOverride(cbor[S_OVERRIDE].toBool())
861 , isConstant(cbor[S_CONSTANT].toBool())
862 , isRequired(cbor[S_REQUIRED].toBool())
863{
864}
865
866Argument::Argument(const QCborMap &cbor)
869{
870}
871
872Method::Method(const QCborMap &cbor, bool isConstructor)
875 , index(cbor[S_INDEX].toInteger(InvalidIndex))
876 , lineNumber(cbor[S_LINENUMBER].toInteger(0))
879 , isCloned(cbor[S_IS_CLONED].toBool())
880 , isJavaScriptFunction(cbor[S_IS_JAVASCRIPT_FUNCTION].toBool())
881 , isConstructor(isConstructor || cbor[S_IS_CONSTRUCTOR].toBool())
882 , isConst(cbor[S_IS_CONST].toBool())
883{
884 const QCborArray args = cbor[S_ARGUMENTS].toArray();
885 for (const QCborValue &argument : args)
886 arguments.emplace_back(argument.toMap());
887
888 if (arguments.size() == 1) {
889 const QAnyStringView type = arguments[0].type;
890 if (type == "QQmlV4FunctionPtr"_L1 || type == "QQmlV4Function*"_L1) {
892 arguments.clear();
893 }
894 }
895}
896
897Enum::Enum(const QCborMap &cbor)
901 , lineNumber(cbor[S_LINENUMBER].toInteger(0))
902 , isFlag(cbor[S_IS_FLAG].toBool())
903 , isClass(cbor[S_IS_CLASS].toBool())
904{
905 const QCborArray vals = cbor[S_VALUES].toArray();
906 for (const QCborValue &value : vals)
907 values.emplace_back(value.toStringView());
908}
909
910MetaTypePrivate::MetaTypePrivate(const QCborMap &cbor, const QString &inputFile)
911 : cbor(cbor)
913{
914 className = cbor[S_CLASS_NAME].toStringView();
915 lineNumber = cbor[S_LINENUMBER].toInteger(0);
916 qualifiedClassName = cbor[S_QUALIFIED_CLASS_NAME].toStringView();
917
918 const QCborArray cborSuperClasses = cbor[S_SUPER_CLASSES].toArray();
919 for (const QCborValue &superClass : cborSuperClasses)
920 superClasses.emplace_back(superClass.toMap());
921
922 const QCborArray cborClassInfos = cbor[S_CLASS_INFOS].toArray();
923 for (const QCborValue &classInfo : cborClassInfos)
924 classInfos.emplace_back(classInfo.toMap());
925
926 const QCborArray cborIfaces = cbor[S_INTERFACES].toArray();
927 for (const QCborValue &iface : cborIfaces)
928 ifaces.emplace_back(iface);
929
930 const QCborArray cborProperties = cbor[S_PROPERTIES].toArray();
931 for (const QCborValue &property : cborProperties)
932 properties.emplace_back(property.toMap());
933
934 for (const QCborArray &cborMethods : { cbor[S_SLOTS].toArray(), cbor[S_METHODS].toArray() }) {
935 for (const QCborValue &method : cborMethods)
936 methods.emplace_back(method.toMap(), false);
937 }
938
939 const QCborArray cborSigs = cbor[S_SIGNALS].toArray();
940 for (const QCborValue &sig : cborSigs)
941 sigs.emplace_back(sig.toMap(), false);
942
943 const QCborArray cborConstructors = cbor[S_CONSTRUCTORS].toArray();
944 for (const QCborValue &constructor : cborConstructors)
945 constructors.emplace_back(constructor.toMap(), true);
946
947 const QCborArray cborEnums = cbor[S_ENUMS].toArray();
948 for (const QCborValue &enumerator : cborEnums)
949 enums.emplace_back(enumerator.toMap());
950
951 if (cbor[S_GADGET].toBool())
952 kind = Kind::Gadget;
953 else if (cbor[S_OBJECT].toBool())
954 kind = Kind::Object;
955 else if (cbor[S_NAMESPACE].toBool())
956 kind = Kind::Namespace;
957}
958
959MetaType::MetaType(const QCborMap &cbor, const QString &inputFile)
961{}
962
963/* metatype for a type about which we don't know anything
964 Caller is expected to track duplicates, as we otherwise end up with two opaque types
965 of the same name
966 */
967MetaType MetaType::createOpaqueType(QAnyStringView typeName)
968{
969 auto priv = s_pool.emplace_back(std::make_unique<MetaTypePrivate>()).get();
970 priv->className = typeName;
971 priv->qualifiedClassName = typeName;
972 priv->kind = MetaType::Kind::Gadget;
973
974 MetaType type;
975 type.d = priv;
976 return type;
977}
978
979QT_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 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)