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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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