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>
20using namespace Qt::StringLiterals;
29std::vector<std::unique_ptr<MetaTypePrivate>> s_pool;
33 const QJsonDocument jsonValue = QJsonDocument::fromJson(json, error);
34 if (jsonValue.isArray())
35 return QCborValue::fromJsonValue(jsonValue.array());
36 if (jsonValue.isObject())
37 return QCborValue::fromJsonValue(jsonValue.object());
43 const QAnyStringView unqualified = classDef.className();
44 const QAnyStringView qualified = classDef.qualifiedClassName();
46 QList<QAnyStringView> namespaces;
47 if (qualified != unqualified) {
48 namespaces = split(qualified,
"::"_L1);
49 Q_ASSERT(namespaces.last() == unqualified);
50 namespaces.pop_back();
58 for (
const QString &source: files) {
59 QCborValue metaObjects;
62 if (!f.open(QIODevice::ReadOnly)) {
63 error(source) <<
"Cannot open file for reading";
66 QJsonParseError parseError = {0, QJsonParseError::NoError};
67 metaObjects = fromJson(f.readAll(), &parseError);
68 if (parseError.error != QJsonParseError::NoError) {
70 <<
"Failed to parse JSON:" << parseError.error
71 << parseError.errorString();
76 if (metaObjects.isArray()) {
77 const QCborArray metaObjectsArray = metaObjects.toArray();
78 for (
const QCborValue &metaObject : metaObjectsArray) {
79 if (!metaObject.isMap()) {
80 error(source) <<
"JSON is not an object";
84 processTypes(metaObject.toMap());
86 }
else if (metaObjects.isMap()) {
87 processTypes(metaObjects.toMap());
89 error(source) <<
"JSON is not an object or an array";
99 QFile typesFile(types);
100 if (!typesFile.open(QIODevice::ReadOnly)) {
101 error(types) <<
"Cannot open foreign types file";
105 QJsonParseError parseError = {0, QJsonParseError::NoError};
106 QCborValue foreignMetaObjects = fromJson(typesFile.readAll(), &parseError);
107 if (parseError.error != QJsonParseError::NoError) {
109 <<
"Failed to parse JSON:" << parseError.error
110 << parseError.errorString();
114 const QCborArray foreignObjectsArray = foreignMetaObjects.toArray();
115 for (
const QCborValue &metaObject : foreignObjectsArray) {
116 if (!metaObject.isMap()) {
117 error(types) <<
"JSON is not an object";
121 processForeignTypes(metaObject.toMap());
131 for (
const QString &types : foreignTypesFiles) {
132 if (!processForeignTypes(types))
138template<
typename String>
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());
153 sortTypes(m_foreignTypes);
154 sortStringList(&m_primitiveTypes);
155 sortStringList(&m_usingDeclarations);
157 sortStringList(&m_referencedTypes);
158 sortStringList(&m_includes);
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;
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(u"QML_NAMED_ELEMENT("_s + className + u")"_s);
180 }
else if (value == S_ANONYMOUS) {
181 qmlElements.append(u"QML_ANONYMOUS"_s);
183 qmlElements.append(u"QML_NAMED_ELEMENT("_s + value.toString() + u")");
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) {
195 if (qmlElements.isEmpty())
197 const QString spaces = u" "_s;
199 registrationHelper += u"\nnamespace "_s + foreignClassName + u"{\n Q_NAMESPACE\n"_s;
200 registrationHelper += spaces + u"QML_FOREIGN_NAMESPACE(" + qualifiedClassName + u")\n"_s;
202 registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
203 registrationHelper += spaces + u"QML_FOREIGN(" + qualifiedClassName + u")\n"_s;
205 registrationHelper += spaces + qmlElements.join(u"\n"_s) + u"\n"_s;
207 registrationHelper += spaces + u"QML_SINGLETON\n"_s;
208 if (isExplicitlyUncreatable) {
209 if (qmlUncreatable.isEmpty())
210 registrationHelper += spaces + uR"(QML_UNCREATABLE(""))" + u"n";
212 registrationHelper += spaces + qmlUncreatable + u"\n";
214 if (!qmlAttached.isEmpty())
215 registrationHelper += spaces + qmlAttached + u"\n";
216 registrationHelper += u"}";
218 registrationHelper += u";";
219 registrationHelper += u"\n";
221 return registrationHelper;
225 const MetaType &classDef, PopulateMode populateMode)
231 QList<QAnyStringView> primitiveAliases;
234 RegistrationMode mode = NoRegistration;
235 bool isSelfExtendingValueType =
false;
236 bool hasJavaScriptExtension =
false;
237 bool isRootObject =
false;
238 bool isSequence =
false;
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(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)
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;
260 case MetaType::Kind::Gadget:
261 mode = GadgetRegistration;
263 case MetaType::Kind::Namespace:
264 mode = NamespaceRegistration;
268 <<
"Not registering a classInfo which is neither an object,"
269 <<
"nor a gadget, nor a namespace:"
270 << classInfo.name.toString();
276 return PreProcessResult {
277 std::move(primitiveAliases),
279 (!isRootObject && (isSequence || isSelfExtendingValueType || hasJavaScriptExtension))
280 ? usingDeclaration.alias
290 return string.visit([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(QByteArrayView(view.data(), view.length()), seed);
302 return a.qualifiedClassName() < b.qualifiedClassName();
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;
324 Q_UNREACHABLE_RETURN(QLatin1StringView());
329 QSet<QAnyStringView> processedRelatedNativeNames;
330 QSet<QAnyStringView> processedRelatedJavaScriptNames;
331 QSet<QAnyStringView> unresolvedForeignNames;
332 QQueue<MetaType> typeQueue;
333 typeQueue.append(m_types);
335 const auto addRelatedName
336 = [&](QAnyStringView relatedName,
const QList<QAnyStringView> &namespaces) {
337 if (
const FoundType related = QmlTypesClassDescription::findType(
338 m_types, m_foreignTypes, relatedName, namespaces)) {
341 processedRelatedJavaScriptNames.insert(related
.javaScript.qualifiedClassName());
344 processedRelatedNativeNames.insert(related
.native.qualifiedClassName());
352 const auto addRelatedType = [&](
const MetaType &type) {
353 const QAnyStringView qualifiedName = type.qualifiedClassName();
354 if (type.inputFile().isEmpty())
355 processedRelatedJavaScriptNames.insert(qualifiedName);
357 processedRelatedNativeNames.insert(qualifiedName);
361 for (
const MetaType &type : std::as_const(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(type)))
367 unresolvedForeignNames.insert(foreign);
375 for (
const MetaType &foreignType : std::as_const(m_foreignTypes)) {
376 bool seenQmlPrefix =
false;
377 for (
const ClassInfo &obj : foreignType.classInfos()) {
378 const QAnyStringView name = obj.name;
379 if (!seenQmlPrefix && startsWith(name,
"QML."_L1)) {
380 addRelatedType(foreignType);
381 seenQmlPrefix =
true;
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(foreignType)))
389 unresolvedForeignNames.insert(foreign.type);
394 const auto addReference
395 = [&](
const MetaType &type, QSet<QAnyStringView> *processedRelatedNames,
399 QAnyStringView qualifiedName = type.qualifiedClassName();
400 m_referencedTypes.append(qualifiedName);
401 const qsizetype size = processedRelatedNames->size();
402 processedRelatedNames->insert(qualifiedName);
404 if (processedRelatedNames->size() == size)
407 typeQueue.enqueue(type);
413 const auto insert = std::lower_bound(
414 m_types.constBegin(), m_types.constEnd(), type,
415 qualifiedClassNameLessThan);
416 m_types.insert(insert, type);
423 const auto remove = std::equal_range(
424 m_foreignTypes.constBegin(), m_foreignTypes.constEnd(), type,
425 qualifiedClassNameLessThan);
426 for (
auto it = remove.first; it != remove.second; ++it) {
428 m_foreignTypes.erase(it);
434 const auto addInterface
435 = [&](QAnyStringView typeName,
const QList<QAnyStringView> &namespaces) {
436 if (
const FoundType other = QmlTypesClassDescription::findType(
437 m_types, m_foreignTypes, typeName, namespaces)) {
445 unresolvedForeignNames.insert(typeName);
448 processedRelatedNativeNames.insert(typeName);
452 const auto doAddReferences = [&](QAnyStringView typeName,
453 const QList<QAnyStringView> &namespaces) {
454 if (
const FoundType other = QmlTypesClassDescription::findType(
455 m_types, m_foreignTypes, typeName, namespaces)) {
465 const auto addType = [&](
const MetaType &context, QAnyStringView typeName,
466 const QList<QAnyStringView> &namespaces,
TypeRelation relation) {
467 if (doAddReferences(typeName, namespaces))
471 const QLatin1StringView separator(
"::");
472 if (
const qsizetype index = lastIndexOf(typeName, separator); index > 0) {
473 if (
const FoundType other = QmlTypesClassDescription::findType(
474 m_types, m_foreignTypes, typeName.left(index), namespaces)) {
476 const QAnyStringView enumName = typeName.mid(index + separator.length());
478 for (
const Enum &enumerator : other.native.enums()) {
479 if (enumerator.name != enumName && enumerator.alias != enumName)
482 addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
484 other.javaScript, &processedRelatedJavaScriptNames,
485 other.javaScriptOrigin);
492 for (
const Enum &enumerator : context.enums()) {
493 if (enumerator.name == typeName || enumerator.alias == typeName)
500 if (!unresolvedForeignNames.contains(typeName) && !isPrimitive(typeName)) {
501 warning(context) << typeName <<
"is used as" << typeRelationString(relation)
502 <<
"type but cannot be found.";
505 processedRelatedNativeNames.insert(typeName);
506 processedRelatedJavaScriptNames.insert(typeName);
512 const auto addSupers = [&](
const MetaType &context,
const QList<QAnyStringView> &namespaces) {
513 for (
const Interface &iface : context.ifaces())
514 addInterface(interfaceName(iface), namespaces);
517 bool warnAboutSupers = context.kind() != MetaType::Kind::Gadget;
519 QList<QAnyStringView> missingSupers;
521 for (
const BaseType &superObject : context.superClasses()) {
522 if (superObject.access != Access::Public)
525 QAnyStringView typeName = superObject.name;
526 if (doAddReferences(typeName, namespaces))
527 warnAboutSupers =
false;
529 missingSupers.append(typeName);
532 for (QAnyStringView typeName : std::as_const(missingSupers)) {
535 && !unresolvedForeignNames.contains(typeName)
536 && !isPrimitive(typeName)) {
537 warning(context) << typeName <<
"is used as base type but cannot be found.";
540 processedRelatedNativeNames.insert(typeName);
541 processedRelatedJavaScriptNames.insert(typeName);
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);
555 const QList<QAnyStringView> &namespaces) {
556 const QAnyStringView objNameValue = obj.name;
557 if (objNameValue == S_ATTACHED) {
560 }
else if (objNameValue == S_SEQUENCE) {
564 }
else if (objNameValue == S_EXTENDED) {
565 const QAnyStringView value = obj.value;
574 while (!typeQueue.isEmpty()) {
575 QAnyStringView unresolvedForeign;
577 const MetaType classDef = typeQueue.dequeue();
578 const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
580 for (
const ClassInfo &obj : classDef.classInfos()) {
581 if (addRelation(classDef, obj, namespaces))
583 if (obj.name != S_FOREIGN)
586 const QAnyStringView foreignClassName = obj.value;
590 if (
const FoundType found = QmlTypesClassDescription::findType(
591 m_foreignTypes, {}, foreignClassName, namespaces)) {
592 const MetaType other = found.select(classDef,
"Foreign");
593 const QList<QAnyStringView> otherNamespaces
594 = MetaTypesJsonProcessor::namespaces(other);
595 addSupers(other, otherNamespaces);
596 addEnums(other, otherNamespaces);
598 for (
const ClassInfo &obj : other.classInfos()) {
599 if (addRelation(classDef, obj, otherNamespaces))
603 }
else if (!QmlTypesClassDescription::findType(
604 m_types, {}, foreignClassName, namespaces)) {
605 unresolvedForeign = foreignClassName;
609 if (!unresolvedForeign.isEmpty() && !isPrimitive(unresolvedForeign)) {
612 <<
"is declared as foreign type, but cannot be found.";
615 addSupers(classDef, namespaces);
616 addEnums(classDef, namespaces);
622 std::sort(types.begin(), types.end(), qualifiedClassNameLessThan);
627 if (!m_privateIncludes)
628 return include.toString();
630 if (endsWith(include,
"_p.h"_L1))
631 return QLatin1String(
"private/") + include.toString();
633 if (startsWith(include,
"qplatform"_L1) || startsWith(include,
"qwindowsystem"_L1))
634 return QLatin1String(
"qpa/") + include.toString();
636 return include.toString();
641 const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
642 const QCborArray classes = types[S_CLASSES].toArray();
643 for (
const QCborValue &cls : classes) {
644 const MetaType classDef(cls.toMap(), include);
646 const PreProcessResult preprocessed = preProcess(classDef, PopulateMode::Yes);
647 switch (preprocessed.mode) {
648 case NamespaceRegistration:
649 case GadgetRegistration:
650 case ObjectRegistration: {
651 if (!endsWith(include, QLatin1String(
".h"))
652 && !endsWith(include, QLatin1String(
".hpp"))
653 && !endsWith(include, QLatin1String(
".hxx"))
654 && !endsWith(include, QLatin1String(
".hh"))
655 && !endsWith(include, QLatin1String(
".py"))
656 && contains(include, QLatin1Char(
'.'))) {
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.";
662 m_includes.append(include);
663 m_types.emplaceBack(classDef);
667 m_foreignTypes.emplaceBack(classDef);
671 if (!preprocessed.foreignPrimitive.isEmpty()) {
672 m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
673 m_primitiveTypes.append(preprocessed.primitiveAliases);
676 if (preprocessed.usingDeclaration.isValid())
677 m_usingDeclarations.append(preprocessed.usingDeclaration);
683 const QString include = resolvedInclude(toStringView(types, 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::No);
689 m_foreignTypes.emplaceBack(classDef);
690 if (!preprocessed.foreignPrimitive.isEmpty()) {
691 m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
692 m_primitiveTypes.append(preprocessed.primitiveAliases);
695 if (preprocessed.usingDeclaration.isValid())
696 m_usingDeclarations.append(preprocessed.usingDeclaration);
702 const auto it = cbor.find(S_REVISION);
703 return it == cbor.end()
705 : QTypeRevision::fromEncodedVersion(it->toInteger());
710 const QAnyStringView access = toStringView(cbor, S_ACCESS);
711 if (access == S_PUBLIC)
712 return Access::Public;
713 if (access == S_PROTECTED)
714 return Access::Protected;
715 return Access::Private;
732 if (cbor.isArray()) {
733 QCborArray needlessWrapping = cbor.toArray();
734 className = needlessWrapping.size() > 0
735 ? toStringView(needlessWrapping[0].toMap(), S_CLASS_NAME)
738 className = toStringView(cbor.toMap(), S_CLASS_NAME);
752 ,
index(cbor[S_INDEX].toInteger(-1))
754 ,
isFinal(cbor[S_FINAL].toBool())
772 ,
isCloned(cbor[S_IS_CLONED].toBool())
774 ,
isConstructor(isConstructor || cbor[S_IS_CONSTRUCTOR].toBool())
776 const QCborArray args = cbor[S_ARGUMENTS].toArray();
777 for (
const QCborValue &argument : args)
778 arguments.emplace_back(argument.toMap());
780 if (arguments.size() == 1) {
781 const QAnyStringView type = arguments[0].type;
782 if (type ==
"QQmlV4FunctionPtr"_L1 || type ==
"QQmlV4Function*"_L1) {
793 ,
isFlag(cbor[S_IS_FLAG].toBool())
794 ,
isClass(cbor[S_IS_CLASS].toBool())
796 const QCborArray vals = cbor[S_VALUES].toArray();
797 for (
const QCborValue &value : vals)
798 values.emplace_back(toStringView(value));
805 className = toStringView(cbor, S_CLASS_NAME);
807 qualifiedClassName = toStringView(cbor, S_QUALIFIED_CLASS_NAME);
809 const QCborArray cborSuperClasses = cbor[S_SUPER_CLASSES].toArray();
810 for (
const QCborValue &superClass : cborSuperClasses)
811 superClasses.emplace_back(superClass.toMap());
813 const QCborArray cborClassInfos = cbor[S_CLASS_INFOS].toArray();
814 for (
const QCborValue &classInfo : cborClassInfos)
815 classInfos.emplace_back(classInfo.toMap());
817 const QCborArray cborIfaces = cbor[S_INTERFACES].toArray();
818 for (
const QCborValue &iface : cborIfaces)
819 ifaces.emplace_back(iface);
821 const QCborArray cborProperties = cbor[S_PROPERTIES].toArray();
822 for (
const QCborValue &property : cborProperties)
823 properties.emplace_back(property.toMap());
825 for (
const QCborArray &cborMethods : { cbor[S_SLOTS].toArray(), cbor[S_METHODS].toArray() }) {
826 for (
const QCborValue &method : cborMethods)
827 methods.emplace_back(method.toMap(),
false);
830 const QCborArray cborSigs = cbor[S_SIGNALS].toArray();
831 for (
const QCborValue &sig : cborSigs)
832 sigs.emplace_back(sig.toMap(),
false);
834 const QCborArray cborConstructors = cbor[S_CONSTRUCTORS].toArray();
835 for (
const QCborValue &constructor : cborConstructors)
836 constructors.emplace_back(constructor.toMap(),
true);
838 const QCborArray cborEnums = cbor[S_ENUMS].toArray();
839 for (
const QCborValue &enumerator : cborEnums)
840 enums.emplace_back(enumerator.toMap());
842 if (cbor[S_GADGET].toBool())
844 else if (cbor[S_OBJECT].toBool())
846 else if (cbor[S_NAMESPACE].toBool())
847 kind = Kind::Namespace;
Combined button and popup list for selecting options.
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)
bool isJavaScriptFunction
Method(const QCborMap &cbor, bool isConstructor)
static constexpr int InvalidIndex
Property(const QCborMap &cbor)