12#include <QtCore/qcborarray.h>
13#include <QtCore/qcbormap.h>
14#include <QtCore/qdir.h>
15#include <QtCore/qfile.h>
16#include <QtCore/qjsondocument.h>
17#include <QtCore/qqueue.h>
21using namespace Qt::StringLiterals;
30std::vector<std::unique_ptr<MetaTypePrivate>> s_pool;
34 const QJsonDocument jsonValue = QJsonDocument::fromJson(json, error);
35 if (jsonValue.isArray())
36 return QCborValue::fromJsonValue(jsonValue.array());
37 if (jsonValue.isObject())
38 return QCborValue::fromJsonValue(jsonValue.object());
44 const QAnyStringView unqualified = classDef.className();
45 const QAnyStringView qualified = classDef.qualifiedClassName();
47 QList<QAnyStringView> namespaces;
48 if (qualified != unqualified) {
49 namespaces = split(qualified,
"::"_L1);
50 Q_ASSERT(namespaces.last() == unqualified);
51 namespaces.pop_back();
59 for (
const QString &source: files) {
60 if (m_seenMetaTypesFiles.hasSeen(QDir::cleanPath(source)))
63 QCborValue metaObjects;
66 if (!f.open(QIODevice::ReadOnly)) {
67 error(source) <<
"Cannot open file for reading";
70 QJsonParseError parseError = {0, QJsonParseError::NoError};
71 metaObjects = fromJson(f.readAll(), &parseError);
72 if (parseError.error != QJsonParseError::NoError) {
74 <<
"Failed to parse JSON:" << parseError.error
75 << parseError.errorString();
80 if (metaObjects.isArray()) {
81 const QCborArray metaObjectsArray = metaObjects.toArray();
82 for (
const QCborValue &metaObject : metaObjectsArray) {
83 if (!metaObject.isMap()) {
84 error(source) <<
"JSON is not an object";
88 processTypes(metaObject.toMap());
90 }
else if (metaObjects.isMap()) {
91 processTypes(metaObjects.toMap());
93 error(source) <<
"JSON is not an object or an array";
103 QFile typesFile(types);
104 if (!typesFile.open(QIODevice::ReadOnly)) {
105 error(types) <<
"Cannot open foreign types file";
109 QJsonParseError parseError = {0, QJsonParseError::NoError};
110 QCborValue foreignMetaObjects = fromJson(typesFile.readAll(), &parseError);
111 if (parseError.error != QJsonParseError::NoError) {
113 <<
"Failed to parse JSON:" << parseError.error
114 << parseError.errorString();
118 const QCborArray foreignObjectsArray = foreignMetaObjects.toArray();
119 for (
const QCborValue &metaObject : foreignObjectsArray) {
120 if (!metaObject.isMap()) {
121 error(types) <<
"JSON is not an object";
125 processForeignTypes(metaObject.toMap());
135 for (
const QString &types : foreignTypesFiles) {
136 if (m_seenMetaTypesFiles.hasSeen(QDir::cleanPath(types)))
139 if (!processForeignTypes(types))
145template<
typename String>
148 std::sort(list->begin(), list->end());
149 const auto newEnd = std::unique(list->begin(), list->end());
150 list->erase(
typename QList<String>::const_iterator(newEnd), list->constEnd());
160 sortTypes(m_foreignTypes);
161 sortStringList(&m_primitiveTypes);
162 sortStringList(&m_usingDeclarations);
164 sortStringList(&m_referencedTypes);
165 sortStringList(&m_includes);
170 QString registrationHelper;
171 for (
const auto &obj: m_types) {
172 const QString className = obj.className().toString();
173 const QString qualifiedClassName = obj.qualifiedClassName().toString();
174 const QString foreignClassName = className + u"Foreign";
175 QStringList qmlElements;
176 QString qmlUncreatable;
178 bool isSingleton =
false;
179 bool isExplicitlyUncreatable =
false;
180 bool isNamespace = obj.kind() == MetaType::Kind::Namespace;
181 for (
const ClassInfo &entry: obj.classInfos()) {
182 const auto name = entry.name;
183 const auto value = entry.value;
184 if (name == S_ELEMENT) {
185 if (value == S_AUTO) {
186 qmlElements.append(u"QML_NAMED_ELEMENT("_s + className + u")"_s);
187 }
else if (value == S_ANONYMOUS) {
188 qmlElements.append(u"QML_ANONYMOUS"_s);
190 qmlElements.append(u"QML_NAMED_ELEMENT("_s + value.toString() + u")");
192 }
else if (name == S_CREATABLE && value == S_FALSE) {
193 isExplicitlyUncreatable =
true;
194 }
else if (name == S_UNCREATABLE_REASON) {
195 qmlUncreatable = u"QML_UNCREATABLE(\""_s + value.toString() + u"\")";
196 }
else if (name == S_ATTACHED) {
197 qmlAttached = u"QML_ATTACHED("_s + value.toString() + u")";
198 }
else if (name == S_SINGLETON) {
202 if (qmlElements.isEmpty())
204 const QString spaces = u" "_s;
206 registrationHelper += u"\nnamespace "_s + foreignClassName + u"{\n Q_NAMESPACE\n"_s;
207 registrationHelper += spaces + u"QML_FOREIGN_NAMESPACE(" + qualifiedClassName + u")\n"_s;
209 registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
210 registrationHelper += spaces + u"QML_FOREIGN(" + qualifiedClassName + u")\n"_s;
212 registrationHelper += spaces + qmlElements.join(u"\n"_s) + u"\n"_s;
214 registrationHelper += spaces + u"QML_SINGLETON\n"_s;
215 if (isExplicitlyUncreatable) {
216 if (qmlUncreatable.isEmpty())
217 registrationHelper += spaces + uR"(QML_UNCREATABLE(""))" + u"n";
219 registrationHelper += spaces + qmlUncreatable + u"\n";
221 if (!qmlAttached.isEmpty())
222 registrationHelper += spaces + qmlAttached + u"\n";
223 registrationHelper += u"}";
225 registrationHelper += u";";
226 registrationHelper += u"\n";
228 return registrationHelper;
232 const MetaType &classDef, PopulateMode populateMode)
238 QList<QAnyStringView> primitiveAliases;
241 RegistrationMode mode = NoRegistration;
242 bool isSelfExtendingValueType =
false;
243 bool hasJavaScriptExtension =
false;
244 bool isRootObject =
false;
245 bool isSequence =
false;
247 for (
const ClassInfo &classInfo : classDef.classInfos()) {
248 if (classInfo.name == S_FOREIGN)
249 usingDeclaration.alias = classInfo.value;
250 else if (classInfo.name == S_PRIMITIVE_ALIAS)
251 primitiveAliases.append(classInfo.value);
252 else if (classInfo.name == S_EXTENSION_IS_JAVA_SCRIPT)
253 hasJavaScriptExtension = (classInfo.value == S_TRUE);
254 else if (classInfo.name == S_EXTENDED && classDef.kind() == MetaType::Kind::Gadget)
255 isSelfExtendingValueType = classInfo.value == classDef.className();
256 else if (classInfo.name == S_ROOT)
257 isRootObject = (classInfo.value == S_TRUE);
258 else if (classInfo.name == S_SEQUENCE)
260 else if (classInfo.name == S_USING)
261 usingDeclaration.original = classInfo.value;
262 else if (populateMode == PopulateMode::Yes && classInfo.name == S_ELEMENT) {
263 switch (classDef.kind()) {
264 case MetaType::Kind::Object:
265 mode = ObjectRegistration;
267 case MetaType::Kind::Gadget:
268 mode = GadgetRegistration;
270 case MetaType::Kind::Namespace:
271 mode = NamespaceRegistration;
275 <<
"Not registering a classInfo which is neither an object,"
276 <<
"nor a gadget, nor a namespace:"
277 << classInfo.name.toString();
283 return PreProcessResult {
284 std::move(primitiveAliases),
286 (!isRootObject && (isSequence || isSelfExtendingValueType || hasJavaScriptExtension))
287 ? usingDeclaration.alias
297 return string.visit([seed](
auto view) {
298 if constexpr (std::is_same_v<
decltype(view), QStringView>)
299 return qHash(view, seed);
300 if constexpr (std::is_same_v<
decltype(view), QLatin1StringView>)
301 return qHash(view, seed);
302 if constexpr (std::is_same_v<
decltype(view), QUtf8StringView>)
303 return qHash(QByteArrayView(view.data(), view.length()), seed);
309 return a.qualifiedClassName() < b.qualifiedClassName();
320 case TypeRelation::Property:
return "property"_L1;
321 case TypeRelation::Argument:
return "argument"_L1;
322 case TypeRelation::Return:
return "return"_L1;
323 case TypeRelation::Enum:
return "enum"_L1;
324 case TypeRelation::Attached:
return "attached"_L1;
325 case TypeRelation::SequenceValue:
return "sequence value"_L1;
326 case TypeRelation::Extension:
return "extension"_L1;
331 Q_UNREACHABLE_RETURN(QLatin1StringView());
336 QSet<QAnyStringView> processedRelatedNativeNames;
337 QSet<QAnyStringView> processedRelatedJavaScriptNames;
338 QSet<QAnyStringView> unresolvedForeignNames;
339 QQueue<MetaType> typeQueue;
340 typeQueue.append(m_types);
342 const auto addRelatedName
343 = [&](QAnyStringView relatedName,
const QList<QAnyStringView> &namespaces) {
344 if (
const FoundType related = QmlTypesClassDescription::findType(
345 m_types, m_foreignTypes, relatedName, namespaces)) {
348 processedRelatedJavaScriptNames.insert(related
.javaScript.qualifiedClassName());
351 processedRelatedNativeNames.insert(related
.native.qualifiedClassName());
359 const auto addRelatedType = [&](
const MetaType &type) {
360 const QAnyStringView qualifiedName = type.qualifiedClassName();
361 if (type.inputFile().isEmpty())
362 processedRelatedJavaScriptNames.insert(qualifiedName);
364 processedRelatedNativeNames.insert(qualifiedName);
368 for (
const MetaType &type : std::as_const(m_types)) {
369 addRelatedType(type);
370 for (
const ClassInfo &obj : type.classInfos()) {
371 if (obj.name == S_FOREIGN) {
372 const QAnyStringView foreign = obj.value;
373 if (!addRelatedName(foreign, namespaces(type)))
374 unresolvedForeignNames.insert(foreign);
382 for (
const MetaType &foreignType : std::as_const(m_foreignTypes)) {
383 bool seenQmlPrefix =
false;
384 for (
const ClassInfo &obj : foreignType.classInfos()) {
385 const QAnyStringView name = obj.name;
386 if (!seenQmlPrefix && startsWith(name,
"QML."_L1)) {
387 addRelatedType(foreignType);
388 seenQmlPrefix =
true;
390 if (name == S_FOREIGN
391 || name == S_EXTENDED
392 || name == S_ATTACHED
393 || name == S_SEQUENCE) {
394 ResolvedTypeAlias foreign(obj.value, m_usingDeclarations);
395 if (!addRelatedName(foreign.type, namespaces(foreignType)))
396 unresolvedForeignNames.insert(foreign.type);
401 const auto addReference
402 = [&](
const MetaType &type, QSet<QAnyStringView> *processedRelatedNames,
406 QAnyStringView qualifiedName = type.qualifiedClassName();
407 m_referencedTypes.append(qualifiedName);
408 const qsizetype size = processedRelatedNames->size();
409 processedRelatedNames->insert(qualifiedName);
411 if (processedRelatedNames->size() == size)
414 typeQueue.enqueue(type);
420 const auto insert = std::lower_bound(
421 m_types.constBegin(), m_types.constEnd(), type,
422 qualifiedClassNameLessThan);
423 m_types.insert(insert, type);
430 const auto remove = std::equal_range(
431 m_foreignTypes.constBegin(), m_foreignTypes.constEnd(), type,
432 qualifiedClassNameLessThan);
433 for (
auto it = remove.first; it != remove.second; ++it) {
435 m_foreignTypes.erase(it);
441 const auto addInterface
442 = [&](QAnyStringView typeName,
const QList<QAnyStringView> &namespaces) {
443 if (
const FoundType other = QmlTypesClassDescription::findType(
444 m_types, m_foreignTypes, typeName, namespaces)) {
452 unresolvedForeignNames.insert(typeName);
455 processedRelatedNativeNames.insert(typeName);
459 const auto doAddReferences = [&](QAnyStringView typeName,
460 const QList<QAnyStringView> &namespaces) {
461 if (
const FoundType other = QmlTypesClassDescription::findType(
462 m_types, m_foreignTypes, typeName, namespaces)) {
472 const auto addType = [&](
const MetaType &context, QAnyStringView typeName,
473 const QList<QAnyStringView> &namespaces,
TypeRelation relation) {
474 if (doAddReferences(typeName, namespaces))
478 const QLatin1StringView separator(
"::");
479 if (
const qsizetype index = lastIndexOf(typeName, separator); index > 0) {
480 if (
const FoundType other = QmlTypesClassDescription::findType(
481 m_types, m_foreignTypes, typeName.left(index), namespaces)) {
483 const QAnyStringView enumName = typeName.mid(index + separator.length());
485 for (
const Enum &enumerator : other.native.enums()) {
486 if (enumerator.name != enumName && enumerator.alias != enumName)
489 addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
491 other.javaScript, &processedRelatedJavaScriptNames,
492 other.javaScriptOrigin);
499 for (
const Enum &enumerator : context.enums()) {
500 if (enumerator.name == typeName || enumerator.alias == typeName)
507 if (!unresolvedForeignNames.contains(typeName) && !isPrimitive(typeName)) {
508 warning(context) << typeName <<
"is used as" << typeRelationString(relation)
509 <<
"type but cannot be found.";
512 processedRelatedNativeNames.insert(typeName);
513 processedRelatedJavaScriptNames.insert(typeName);
519 const auto addSupers = [&](
const MetaType &context,
const QList<QAnyStringView> &namespaces) {
520 for (
const Interface &iface : context.ifaces())
521 addInterface(interfaceName(iface), namespaces);
524 bool warnAboutSupers = context.kind() != MetaType::Kind::Gadget;
526 QList<QAnyStringView> missingSupers;
528 for (
const BaseType &superObject : context.superClasses()) {
529 if (superObject.access != Access::Public)
532 QAnyStringView typeName = superObject.name;
533 if (doAddReferences(typeName, namespaces))
534 warnAboutSupers =
false;
536 missingSupers.append(typeName);
539 for (QAnyStringView typeName : std::as_const(missingSupers)) {
542 && !unresolvedForeignNames.contains(typeName)
543 && !isPrimitive(typeName)) {
544 warning(context) << typeName <<
"is used as base type but cannot be found.";
547 processedRelatedNativeNames.insert(typeName);
548 processedRelatedJavaScriptNames.insert(typeName);
552 const auto addEnums = [&](
const MetaType &context,
553 const QList<QAnyStringView> &namespaces) {
554 for (
const Enum &enumerator : context.enums()) {
555 ResolvedTypeAlias resolved(enumerator.type, m_usingDeclarations);
556 if (!resolved.type.isEmpty())
557 addType(context, resolved.type, namespaces, TypeRelation::Enum);
562 const QList<QAnyStringView> &namespaces) {
563 const QAnyStringView objNameValue = obj.name;
564 if (objNameValue == S_ATTACHED) {
567 }
else if (objNameValue == S_SEQUENCE) {
571 }
else if (objNameValue == S_EXTENDED) {
572 const QAnyStringView value = obj.value;
581 while (!typeQueue.isEmpty()) {
582 QAnyStringView unresolvedForeign;
584 const MetaType classDef = typeQueue.dequeue();
585 const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
587 for (
const ClassInfo &obj : classDef.classInfos()) {
588 if (addRelation(classDef, obj, namespaces))
590 if (obj.name != S_FOREIGN)
593 const QAnyStringView foreignClassName = obj.value;
597 if (
const FoundType found = QmlTypesClassDescription::findType(
598 m_foreignTypes, {}, foreignClassName, namespaces)) {
599 const MetaType other = found.select(classDef,
"Foreign");
600 const QList<QAnyStringView> otherNamespaces
601 = MetaTypesJsonProcessor::namespaces(other);
602 addSupers(other, otherNamespaces);
603 addEnums(other, otherNamespaces);
605 for (
const ClassInfo &obj : other.classInfos()) {
606 if (addRelation(classDef, obj, otherNamespaces))
610 }
else if (!QmlTypesClassDescription::findType(
611 m_types, {}, foreignClassName, namespaces)) {
612 unresolvedForeign = foreignClassName;
616 if (!unresolvedForeign.isEmpty() && !isPrimitive(unresolvedForeign)) {
619 <<
"is declared as foreign type, but cannot be found.";
622 addSupers(classDef, namespaces);
623 addEnums(classDef, namespaces);
629 std::sort(types.begin(), types.end(), qualifiedClassNameLessThan);
634 if (!m_privateIncludes)
635 return include.toString();
637 if (endsWith(include,
"_p.h"_L1))
638 return QLatin1String(
"private/") + include.toString();
640 if (startsWith(include,
"qplatform"_L1) || startsWith(include,
"qwindowsystem"_L1))
641 return QLatin1String(
"qpa/") + include.toString();
643 return include.toString();
648 const QString include = resolvedInclude(types[S_INPUT_FILE].toStringView());
649 const QCborArray classes = types[S_CLASSES].toArray();
650 for (
const QCborValue &cls : classes) {
651 const MetaType classDef(cls.toMap(), include);
653 const PreProcessResult preprocessed = preProcess(classDef, PopulateMode::Yes);
654 switch (preprocessed.mode) {
655 case NamespaceRegistration:
656 case GadgetRegistration:
657 case ObjectRegistration: {
658 if (!endsWith(include, QLatin1String(
".h"))
659 && !endsWith(include, QLatin1String(
".hpp"))
660 && !endsWith(include, QLatin1String(
".hxx"))
661 && !endsWith(include, QLatin1String(
".hh"))
662 && !endsWith(include, QLatin1String(
".py"))
663 && contains(include, QLatin1Char(
'.'))) {
665 <<
"Class" << classDef.qualifiedClassName()
666 <<
"is declared in" << include <<
"which appears not to be a header."
667 <<
"The compilation of its registration to QML may fail.";
669 m_includes.append(include);
670 m_types.emplaceBack(classDef);
674 m_foreignTypes.emplaceBack(classDef);
678 if (!preprocessed.foreignPrimitive.isEmpty()) {
679 m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
680 m_primitiveTypes.append(preprocessed.primitiveAliases);
683 if (preprocessed.usingDeclaration.isValid())
684 m_usingDeclarations.append(preprocessed.usingDeclaration);
690 const QString include = resolvedInclude(types[S_INPUT_FILE].toStringView());
691 const QCborArray classes = types[S_CLASSES].toArray();
692 for (
const QCborValue &cls : classes) {
693 const MetaType classDef(cls.toMap(), include);
694 PreProcessResult preprocessed = preProcess(classDef, PopulateMode::No);
696 m_foreignTypes.emplaceBack(classDef);
697 if (!preprocessed.foreignPrimitive.isEmpty()) {
698 m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
699 m_primitiveTypes.append(preprocessed.primitiveAliases);
702 if (preprocessed.usingDeclaration.isValid())
703 m_usingDeclarations.append(preprocessed.usingDeclaration);
709 const auto it = cbor.find(S_REVISION);
710 return it == cbor.end()
712 : QTypeRevision::fromEncodedVersion(it->toInteger());
717 const QAnyStringView access = cbor[S_ACCESS].toStringView();
718 if (access == S_PUBLIC)
720 if (access == S_PROTECTED)
739 if (cbor.isArray()) {
740 QCborArray needlessWrapping = cbor.toArray();
741 className = needlessWrapping.size() > 0
742 ? needlessWrapping[0].toMap()[S_CLASS_NAME].toStringView()
745 className = cbor.toMap()[S_CLASS_NAME].toStringView();
759 ,
index(cbor[S_INDEX].toInteger(-1))
762 ,
isFinal(cbor[S_FINAL].toBool())
781 ,
isCloned(cbor[S_IS_CLONED].toBool())
783 ,
isConstructor(isConstructor || cbor[S_IS_CONSTRUCTOR].toBool())
784 ,
isConst(cbor[S_IS_CONST].toBool())
786 const QCborArray args = cbor[S_ARGUMENTS].toArray();
787 for (
const QCborValue &argument : args)
788 arguments.emplace_back(argument.toMap());
790 if (arguments.size() == 1) {
791 const QAnyStringView type = arguments[0].type;
792 if (type ==
"QQmlV4FunctionPtr"_L1 || type ==
"QQmlV4Function*"_L1) {
804 ,
isFlag(cbor[S_IS_FLAG].toBool())
805 ,
isClass(cbor[S_IS_CLASS].toBool())
807 const QCborArray vals = cbor[S_VALUES].toArray();
808 for (
const QCborValue &value : vals)
809 values.emplace_back(value.toStringView());
816 className = cbor[S_CLASS_NAME].toStringView();
818 qualifiedClassName = cbor[S_QUALIFIED_CLASS_NAME].toStringView();
820 const QCborArray cborSuperClasses = cbor[S_SUPER_CLASSES].toArray();
821 for (
const QCborValue &superClass : cborSuperClasses)
822 superClasses.emplace_back(superClass.toMap());
824 const QCborArray cborClassInfos = cbor[S_CLASS_INFOS].toArray();
825 for (
const QCborValue &classInfo : cborClassInfos)
826 classInfos.emplace_back(classInfo.toMap());
828 const QCborArray cborIfaces = cbor[S_INTERFACES].toArray();
829 for (
const QCborValue &iface : cborIfaces)
830 ifaces.emplace_back(iface);
832 const QCborArray cborProperties = cbor[S_PROPERTIES].toArray();
833 for (
const QCborValue &property : cborProperties)
834 properties.emplace_back(property.toMap());
836 for (
const QCborArray &cborMethods : { cbor[S_SLOTS].toArray(), cbor[S_METHODS].toArray() }) {
837 for (
const QCborValue &method : cborMethods)
838 methods.emplace_back(method.toMap(),
false);
841 const QCborArray cborSigs = cbor[S_SIGNALS].toArray();
842 for (
const QCborValue &sig : cborSigs)
843 sigs.emplace_back(sig.toMap(),
false);
845 const QCborArray cborConstructors = cbor[S_CONSTRUCTORS].toArray();
846 for (
const QCborValue &constructor : cborConstructors)
847 constructors.emplace_back(constructor.toMap(),
true);
849 const QCborArray cborEnums = cbor[S_ENUMS].toArray();
850 for (
const QCborValue &enumerator : cborEnums)
851 enums.emplace_back(enumerator.toMap());
853 if (cbor[S_GADGET].toBool())
855 else if (cbor[S_OBJECT].toBool())
857 else if (cbor[S_NAMESPACE].toBool())
858 kind = Kind::Namespace;
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)