aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmltyperegistrar/qmetatypesjsonprocessor.cpp')
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor.cpp847
1 files changed, 847 insertions, 0 deletions
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
new file mode 100644
index 0000000000..480d4ba187
--- /dev/null
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
@@ -0,0 +1,847 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmetatypesjsonprocessor_p.h"
+
+#include "qanystringviewutils_p.h"
+#include "qqmltyperegistrarconstants_p.h"
+#include "qqmltyperegistrarutils_p.h"
+#include "qqmltypesclassdescription_p.h"
+#include "qqmltyperegistrarutils_p.h"
+
+#include <QtCore/qcborarray.h>
+#include <QtCore/qcbormap.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qqueue.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+using namespace Constants;
+using namespace Constants::MetatypesDotJson;
+using namespace Constants::MetatypesDotJson::Qml;
+using namespace QAnyStringViewUtils;
+
+const MetaTypePrivate MetaType::s_empty;
+
+// TODO: This could be optimized to store the objects in a more compact way.
+std::vector<std::unique_ptr<MetaTypePrivate>> s_pool;
+
+static QCborValue fromJson(const QByteArray &json, QJsonParseError *error)
+{
+ const QJsonDocument jsonValue = QJsonDocument::fromJson(json, error);
+ if (jsonValue.isArray())
+ return QCborValue::fromJsonValue(jsonValue.array());
+ if (jsonValue.isObject())
+ return QCborValue::fromJsonValue(jsonValue.object());
+ return QCborValue();
+}
+
+QList<QAnyStringView> MetaTypesJsonProcessor::namespaces(const MetaType &classDef)
+{
+ const QAnyStringView unqualified = classDef.className();
+ const QAnyStringView qualified = classDef.qualifiedClassName();
+
+ QList<QAnyStringView> namespaces;
+ if (qualified != unqualified) {
+ namespaces = split(qualified, "::"_L1);
+ Q_ASSERT(namespaces.last() == unqualified);
+ namespaces.pop_back();
+ }
+
+ return namespaces;
+}
+
+bool MetaTypesJsonProcessor::processTypes(const QStringList &files)
+{
+ for (const QString &source: files) {
+ QCborValue metaObjects;
+ {
+ QFile f(source);
+ if (!f.open(QIODevice::ReadOnly)) {
+ error(source) << "Cannot open file for reading";
+ return false;
+ }
+ QJsonParseError parseError = {0, QJsonParseError::NoError};
+ metaObjects = fromJson(f.readAll(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ error(source)
+ << "Failed to parse JSON:" << parseError.error
+ << parseError.errorString();
+ return false;
+ }
+ }
+
+ if (metaObjects.isArray()) {
+ const QCborArray metaObjectsArray = metaObjects.toArray();
+ for (const QCborValue &metaObject : metaObjectsArray) {
+ if (!metaObject.isMap()) {
+ error(source) << "JSON is not an object";
+ return false;
+ }
+
+ processTypes(metaObject.toMap());
+ }
+ } else if (metaObjects.isMap()) {
+ processTypes(metaObjects.toMap());
+ } else {
+ error(source) << "JSON is not an object or an array";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool MetaTypesJsonProcessor::processForeignTypes(const QString &types)
+{
+ QFile typesFile(types);
+ if (!typesFile.open(QIODevice::ReadOnly)) {
+ error(types) << "Cannot open foreign types file";
+ return false;
+ }
+
+ QJsonParseError parseError = {0, QJsonParseError::NoError};
+ QCborValue foreignMetaObjects = fromJson(typesFile.readAll(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ error(types)
+ << "Failed to parse JSON:" << parseError.error
+ << parseError.errorString();
+ return false;
+ }
+
+ const QCborArray foreignObjectsArray = foreignMetaObjects.toArray();
+ for (const QCborValue &metaObject : foreignObjectsArray) {
+ if (!metaObject.isMap()) {
+ error(types) << "JSON is not an object";
+ return false;
+ }
+
+ processForeignTypes(metaObject.toMap());
+ }
+
+ return true;
+}
+
+bool MetaTypesJsonProcessor::processForeignTypes(const QStringList &foreignTypesFiles)
+{
+ bool success = true;
+
+ for (const QString &types : foreignTypesFiles) {
+ if (!processForeignTypes(types))
+ success = false;
+ }
+ return success;
+}
+
+template<typename String>
+static void sortStringList(QList<String> *list)
+{
+ std::sort(list->begin(), list->end());
+ const auto newEnd = std::unique(list->begin(), list->end());
+ list->erase(typename QList<String>::const_iterator(newEnd), list->constEnd());
+}
+
+void MetaTypesJsonProcessor::postProcessTypes()
+{
+ sortTypes(m_types);
+}
+
+void MetaTypesJsonProcessor::postProcessForeignTypes()
+{
+ sortTypes(m_foreignTypes);
+ sortStringList(&m_primitiveTypes);
+ addRelatedTypes();
+ sortStringList(&m_referencedTypes);
+ sortStringList(&m_includes);
+}
+
+QString MetaTypesJsonProcessor::extractRegisteredTypes() const
+{
+ QString registrationHelper;
+ for (const auto &obj: m_types) {
+ const QString className = obj.className().toString();
+ const QString qualifiedClassName = obj.qualifiedClassName().toString();
+ const QString foreignClassName = className + u"Foreign";
+ QStringList qmlElements;
+ QString qmlUncreatable;
+ QString qmlAttached;
+ bool isSingleton = false;
+ bool isExplicitlyUncreatable = false;
+ bool isNamespace = obj.kind() == MetaType::Kind::Namespace;
+ for (const ClassInfo &entry: obj.classInfos()) {
+ const auto name = entry.name;
+ const auto value = entry.value;
+ if (name == S_ELEMENT) {
+ if (value == S_AUTO) {
+ qmlElements.append(u"QML_NAMED_ELEMENT("_s + className + u")"_s);
+ } else if (value == S_ANONYMOUS) {
+ qmlElements.append(u"QML_ANONYMOUS"_s);
+ } else {
+ qmlElements.append(u"QML_NAMED_ELEMENT("_s + value.toString() + u")");
+ }
+ } else if (name == S_CREATABLE && value == S_FALSE) {
+ isExplicitlyUncreatable = true;
+ } else if (name == S_UNCREATABLE_REASON) {
+ qmlUncreatable = u"QML_UNCREATABLE(\""_s + value.toString() + u"\")";
+ } else if (name == S_ATTACHED) {
+ qmlAttached = u"QML_ATTACHED("_s + value.toString() + u")";
+ } else if (name == S_SINGLETON) {
+ isSingleton = true;
+ }
+ }
+ if (qmlElements.isEmpty())
+ continue; // no relevant entries found
+ const QString spaces = u" "_s;
+ if (isNamespace) {
+ registrationHelper += u"\nnamespace "_s + foreignClassName + u"{\n Q_NAMESPACE\n"_s;
+ registrationHelper += spaces + u"QML_FOREIGN_NAMESPACE(" + qualifiedClassName + u")\n"_s;
+ } else {
+ registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
+ registrationHelper += spaces + u"QML_FOREIGN(" + qualifiedClassName + u")\n"_s;
+ }
+ registrationHelper += spaces + qmlElements.join(u"\n"_s) + u"\n"_s;
+ if (isSingleton)
+ registrationHelper += spaces + u"QML_SINGLETON\n"_s;
+ if (isExplicitlyUncreatable) {
+ if (qmlUncreatable.isEmpty())
+ registrationHelper += spaces + uR"(QML_UNCREATABLE(""))" + u"n";
+ else
+ registrationHelper += spaces + qmlUncreatable + u"\n";
+ }
+ if (!qmlAttached.isEmpty())
+ registrationHelper += spaces + qmlAttached + u"\n";
+ registrationHelper += u"}";
+ if (!isNamespace)
+ registrationHelper += u";";
+ registrationHelper += u"\n";
+ }
+ return registrationHelper;
+}
+
+MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess(
+ const MetaType &classDef, PopulateMode populateMode)
+{
+ // If this type is a self-extending value type or a sequence type or has a JavaScript extension
+ // and is not the root object, then it's foreign type has no entry of its own.
+ // In that case we need to generate a "primitive" entry.
+
+ QList<QAnyStringView> aliases;
+ QAnyStringView foreign;
+ RegistrationMode mode = NoRegistration;
+ bool isSelfExtendingValueType = false;
+ bool hasJavaScriptExtension = false;
+ bool isRootObject = false;
+ bool isSequence = false;
+
+ for (const ClassInfo &classInfo : classDef.classInfos()) {
+ if (classInfo.name == S_FOREIGN)
+ foreign = classInfo.value;
+ else if (classInfo.name == S_PRIMITIVE_ALIAS)
+ aliases.append(classInfo.value);
+ else if (classInfo.name == S_EXTENSION_IS_JAVA_SCRIPT)
+ hasJavaScriptExtension = (classInfo.value == S_TRUE);
+ else if (classInfo.name == S_EXTENDED && classDef.kind() == MetaType::Kind::Gadget)
+ isSelfExtendingValueType = classInfo.value == classDef.className();
+ else if (classInfo.name == S_ROOT)
+ isRootObject = (classInfo.value == S_TRUE);
+ else if (classInfo.name == S_SEQUENCE)
+ isSequence = true;
+ else if (populateMode == PopulateMode::Yes && classInfo.name == S_ELEMENT) {
+ switch (classDef.kind()) {
+ case MetaType::Kind::Object:
+ mode = ObjectRegistration;
+ break;
+ case MetaType::Kind::Gadget:
+ mode = GadgetRegistration;
+ break;
+ case MetaType::Kind::Namespace:
+ mode = NamespaceRegistration;
+ break;
+ default:
+ warning(classDef)
+ << "Not registering a classInfo which is neither an object,"
+ << "nor a gadget, nor a namespace:"
+ << classInfo.name.toString();
+ break;
+ }
+ }
+ }
+
+ return PreProcessResult {
+ aliases,
+ (!isRootObject && (isSequence || isSelfExtendingValueType || hasJavaScriptExtension))
+ ? foreign
+ : QAnyStringView(),
+ mode
+ };
+
+}
+
+// TODO: Remove this when QAnyStringView gets a proper qHash()
+static size_t qHash(QAnyStringView string, size_t seed = 0)
+{
+ return string.visit([seed](auto view) {
+ if constexpr (std::is_same_v<decltype(view), QStringView>)
+ return qHash(view, seed);
+ if constexpr (std::is_same_v<decltype(view), QLatin1StringView>)
+ return qHash(view, seed);
+ if constexpr (std::is_same_v<decltype(view), QUtf8StringView>)
+ return qHash(QByteArrayView(view.data(), view.length()), seed);
+ });
+}
+
+static bool qualifiedClassNameLessThan(const MetaType &a, const MetaType &b)
+{
+ return a.qualifiedClassName() < b.qualifiedClassName();
+}
+
+void MetaTypesJsonProcessor::addRelatedTypes()
+{
+ QSet<QAnyStringView> processedRelatedNativeNames;
+ QSet<QAnyStringView> processedRelatedJavaScriptNames;
+ QSet<QAnyStringView> unresolvedForeignNames;
+ QQueue<MetaType> typeQueue;
+ typeQueue.append(m_types);
+
+ const auto addRelatedName
+ = [&](QAnyStringView relatedName, const QList<QAnyStringView> &namespaces) {
+ if (const FoundType related = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, relatedName, namespaces)) {
+
+ if (!related.javaScript.isEmpty())
+ processedRelatedJavaScriptNames.insert(related.javaScript.qualifiedClassName());
+
+ if (!related.native.isEmpty())
+ processedRelatedNativeNames.insert(related.native.qualifiedClassName());
+
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ const auto addRelatedType = [&](const MetaType &type) {
+ const QAnyStringView qualifiedName = type.qualifiedClassName();
+ if (type.inputFile().isEmpty())
+ processedRelatedJavaScriptNames.insert(qualifiedName);
+ else
+ processedRelatedNativeNames.insert(qualifiedName);
+ };
+
+ // First mark all classes registered from this module as already processed.
+ for (const MetaType &type : std::as_const(m_types)) {
+ addRelatedType(type);
+ for (const ClassInfo &obj : type.classInfos()) {
+ if (obj.name == S_FOREIGN) {
+ const QAnyStringView foreign = obj.value;
+ if (!addRelatedName(foreign, namespaces(type)))
+ unresolvedForeignNames.insert(foreign);
+ break;
+ }
+ }
+ }
+
+ // Then mark all classes registered from other modules as already processed.
+ // We don't want to generate them again for this module.
+ for (const MetaType &foreignType : std::as_const(m_foreignTypes)) {
+ bool seenQmlPrefix = false;
+ for (const ClassInfo &obj : foreignType.classInfos()) {
+ const QAnyStringView name = obj.name;
+ if (!seenQmlPrefix && startsWith(name, "QML."_L1)) {
+ addRelatedType(foreignType);
+ seenQmlPrefix = true;
+ }
+ if (name == S_FOREIGN) {
+ const QAnyStringView foreign = obj.value;
+ if (!addRelatedName(foreign, namespaces(foreignType)))
+ unresolvedForeignNames.insert(foreign);
+ break;
+ }
+ }
+ }
+
+ const auto addReference = [&](const MetaType &type, QSet<QAnyStringView> *processedRelatedNames,
+ FoundType::Origin origin) {
+ if (type.isEmpty())
+ return;
+ QAnyStringView qualifiedName = type.qualifiedClassName();
+ m_referencedTypes.append(qualifiedName);
+ const qsizetype size = processedRelatedNames->size();
+ processedRelatedNames->insert(qualifiedName);
+
+ if (processedRelatedNames->size() == size)
+ return;
+
+ typeQueue.enqueue(type);
+
+ if (origin == FoundType::OwnTypes)
+ return;
+
+ // Add to own types since we need it for our registrations.
+ const auto insert = std::lower_bound(
+ m_types.constBegin(), m_types.constEnd(), type,
+ qualifiedClassNameLessThan);
+ m_types.insert(insert, type);
+
+ // Also add its include. We don't want to rely on transitive inclues.
+ const QString inputFile = type.inputFile();
+ if (!inputFile.isEmpty())
+ m_includes.append(inputFile);
+
+ // Remove from the foreign types to avoid the ODR warning.
+ const auto remove = std::equal_range(
+ m_foreignTypes.constBegin(), m_foreignTypes.constEnd(), type,
+ qualifiedClassNameLessThan);
+ for (auto it = remove.first; it != remove.second; ++it) {
+ if (*it == type) {
+ m_foreignTypes.erase(it);
+ break;
+ }
+ }
+ };
+
+ const auto addInterface
+ = [&](QAnyStringView typeName, const QList<QAnyStringView> &namespaces) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName, namespaces)) {
+ if (!other.native.isEmpty()) {
+ addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ return true;
+ }
+ } else {
+ // Do not warn about unresolved interfaces.
+ // They don't have to have Q_OBJECT or Q_GADGET.
+ unresolvedForeignNames.insert(typeName);
+ }
+
+ processedRelatedNativeNames.insert(typeName);
+ return false;
+ };
+
+ const auto addType = [&](const MetaType &context, QAnyStringView typeName,
+ const QList<QAnyStringView> &namespaces, QAnyStringView relation) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName, namespaces)) {
+ addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ addReference(other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin);
+ return true;
+ }
+
+ // If it's an enum, add the surrounding type.
+ const QLatin1StringView separator("::");
+ if (const qsizetype index = lastIndexOf(typeName, separator); index > 0) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName.left(index), namespaces)) {
+
+ const QAnyStringView enumName = typeName.mid(index + separator.length());
+
+ for (const Enum &enumerator : other.native.enums()) {
+ if (enumerator.name != enumName && enumerator.alias != enumName)
+ continue;
+
+ addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ addReference(
+ other.javaScript, &processedRelatedJavaScriptNames,
+ other.javaScriptOrigin);
+ return true;
+ }
+ }
+ }
+
+ // If it's an enum of the context type itself, we don't have to do anything.
+ for (const Enum &enumerator : context.enums()) {
+ if (enumerator.name == typeName || enumerator.alias == typeName)
+ return true;
+ }
+
+ // If we've detected this type as unresolved foreign and it actually belongs to this module,
+ // we'll get to it again when we process it as foreign type. In that case we'll look at the
+ // special cases for sequences and extensions.
+ if (!unresolvedForeignNames.contains(typeName) && !isPrimitive(typeName))
+ warning(context) << typeName << "is used as" << relation << "type but cannot be found.";
+
+ processedRelatedNativeNames.insert(typeName);
+ processedRelatedJavaScriptNames.insert(typeName);
+ return false;
+ };
+
+ const auto doAddReferences = [&](QAnyStringView typeName,
+ const QList<QAnyStringView> &namespaces) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName, namespaces)) {
+ addReference(
+ other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ addReference(
+ other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin);
+ return true;
+ }
+
+ return false;
+ };
+
+ const auto addSupers = [&](const MetaType &context, const QList<QAnyStringView> &namespaces) {
+ for (const Interface &iface : context.ifaces())
+ addInterface(interfaceName(iface), namespaces);
+
+ // We don't warn about missing bases for value types. They don't have to be registered.
+ bool warnAboutSupers = context.kind() != MetaType::Kind::Gadget;
+
+ QList<QAnyStringView> missingSupers;
+
+ for (const BaseType &superObject : context.superClasses()) {
+ if (superObject.access != Access::Public)
+ continue;
+
+ QAnyStringView typeName = superObject.name;
+ if (doAddReferences(typeName, namespaces))
+ warnAboutSupers = false;
+ else
+ missingSupers.append(typeName);
+ }
+
+ for (QAnyStringView typeName : std::as_const(missingSupers)) {
+ // If we've found one valid base type, don't complain about the others.
+ if (warnAboutSupers
+ && !unresolvedForeignNames.contains(typeName)
+ && !isPrimitive(typeName)) {
+ warning(context) << typeName << "is used as base type but cannot be found.";
+ }
+
+ processedRelatedNativeNames.insert(typeName);
+ processedRelatedJavaScriptNames.insert(typeName);
+ }
+ };
+
+ const auto addProperties = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Property &property : context.properties()) {
+ ResolvedTypeAlias resolved(property.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "property");
+ }
+ };
+
+ const auto addMethods = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Method::Container &methods
+ : {context.methods(), context.constructors(), context.sigs() }) {
+ for (const Method &methodObject : methods) {
+ for (const Argument &argument : std::as_const(methodObject.arguments)) {
+ ResolvedTypeAlias resolved(argument.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "argument");
+ }
+
+ ResolvedTypeAlias resolved(methodObject.returnType);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "return");
+ }
+ }
+ };
+
+ const auto addEnums = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Enum &enumerator : context.enums()) {
+ ResolvedTypeAlias resolved(enumerator.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "enum");
+ }
+ };
+
+ const auto addRelation = [&](const MetaType &classDef, const ClassInfo &obj,
+ const QList<QAnyStringView> &namespaces) {
+ const QAnyStringView objNameValue = obj.name;
+ if (objNameValue == S_ATTACHED) {
+ addType(classDef, obj.value, namespaces, "attached");
+ return true;
+ } else if (objNameValue == S_SEQUENCE) {
+ ResolvedTypeAlias value(obj.value);
+ addType(classDef, value.type, namespaces, "sequence value");
+ return true;
+ } else if (objNameValue == S_EXTENDED) {
+ const QAnyStringView value = obj.value;
+ addType(classDef, value, namespaces, "extension");
+ return true;
+ }
+ return false;
+ };
+
+ // Then recursively iterate the super types and attached types, marking the
+ // ones we are interested in as related.
+ while (!typeQueue.isEmpty()) {
+ QAnyStringView unresolvedForeign;
+
+ const MetaType classDef = typeQueue.dequeue();
+ const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
+
+ for (const ClassInfo &obj : classDef.classInfos()) {
+ if (addRelation(classDef, obj, namespaces))
+ continue;
+ if (obj.name != S_FOREIGN)
+ continue;
+
+ const QAnyStringView foreignClassName = obj.value;
+
+ // A type declared as QML_FOREIGN will usually be a foreign type, but it can
+ // actually be an additional registration of a local type, too.
+ if (const FoundType found = QmlTypesClassDescription::findType(
+ m_foreignTypes, {}, foreignClassName, namespaces)) {
+ const MetaType other = found.select(classDef, "Foreign");
+ const QList<QAnyStringView> otherNamespaces
+ = MetaTypesJsonProcessor::namespaces(other);
+ addSupers(other, otherNamespaces);
+ addProperties(other, otherNamespaces);
+ addMethods(other, otherNamespaces);
+ addEnums(other, otherNamespaces);
+
+ for (const ClassInfo &obj : other.classInfos()) {
+ if (addRelation(classDef, obj, otherNamespaces))
+ break;
+ // No, you cannot chain S_FOREIGN declarations. Sorry.
+ }
+ } else if (!QmlTypesClassDescription::findType(
+ m_types, {}, foreignClassName, namespaces)) {
+ unresolvedForeign = foreignClassName;
+ }
+ }
+
+ if (!unresolvedForeign.isEmpty() && !isPrimitive(unresolvedForeign)) {
+ warning(classDef)
+ << unresolvedForeign
+ << "is declared as foreign type, but cannot be found.";
+ }
+
+ addSupers(classDef, namespaces);
+ addProperties(classDef, namespaces);
+ addMethods(classDef, namespaces);
+ addEnums(classDef, namespaces);
+ }
+}
+
+void MetaTypesJsonProcessor::sortTypes(QVector<MetaType> &types)
+{
+ std::sort(types.begin(), types.end(), qualifiedClassNameLessThan);
+}
+
+QString MetaTypesJsonProcessor::resolvedInclude(QAnyStringView include)
+{
+ if (!m_privateIncludes)
+ return include.toString();
+
+ if (endsWith(include, "_p.h"_L1))
+ return QLatin1String("private/") + include.toString();
+
+ if (startsWith(include, "qplatform"_L1) || startsWith(include, "qwindowsystem"_L1))
+ return QLatin1String("qpa/") + include.toString();
+
+ return include.toString();
+}
+
+void MetaTypesJsonProcessor::processTypes(const QCborMap &types)
+{
+ const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
+ const QCborArray classes = types[S_CLASSES].toArray();
+ for (const QCborValue &cls : classes) {
+ const MetaType classDef(cls.toMap(), include);
+
+ const PreProcessResult preprocessed = preProcess(classDef, PopulateMode::Yes);
+ switch (preprocessed.mode) {
+ case NamespaceRegistration:
+ case GadgetRegistration:
+ case ObjectRegistration: {
+ if (!endsWith(include, QLatin1String(".h"))
+ && !endsWith(include, QLatin1String(".hpp"))
+ && !endsWith(include, QLatin1String(".hxx"))
+ && !endsWith(include, QLatin1String(".hh"))
+ && !endsWith(include, QLatin1String(".py"))
+ && contains(include, QLatin1Char('.'))) {
+ warning(include)
+ << "Class" << classDef.qualifiedClassName()
+ << "is declared in" << include << "which appears not to be a header."
+ << "The compilation of its registration to QML may fail.";
+ }
+ m_includes.append(include);
+ m_types.emplaceBack(classDef);
+ break;
+ }
+ case NoRegistration:
+ m_foreignTypes.emplaceBack(classDef);
+ break;
+ }
+
+ if (!preprocessed.foreignPrimitive.isEmpty()) {
+ m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
+ m_primitiveTypes.append(preprocessed.primitiveAliases);
+ }
+ }
+}
+
+void MetaTypesJsonProcessor::processForeignTypes(const QCborMap &types)
+{
+ const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
+ const QCborArray classes = types[S_CLASSES].toArray();
+ for (const QCborValue &cls : classes) {
+ const MetaType classDef(cls.toMap(), include);
+ PreProcessResult preprocessed = preProcess(classDef, PopulateMode::No);
+
+ m_foreignTypes.emplaceBack(classDef);
+ if (!preprocessed.foreignPrimitive.isEmpty()) {
+ m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
+ m_primitiveTypes.append(preprocessed.primitiveAliases);
+ }
+ }
+}
+
+static QTypeRevision getRevision(const QCborMap &cbor)
+{
+ const auto it = cbor.find(S_REVISION);
+ return it == cbor.end()
+ ? QTypeRevision()
+ : QTypeRevision::fromEncodedVersion(it->toInteger());
+}
+
+static Access getAccess(const QCborMap &cbor)
+{
+ const QAnyStringView access = toStringView(cbor, S_ACCESS);
+ if (access == S_PUBLIC)
+ return Access::Public;
+ if (access == S_PROTECTED)
+ return Access::Protected;
+ return Access::Private;
+}
+
+BaseType::BaseType(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , access(getAccess(cbor))
+{
+}
+
+ClassInfo::ClassInfo(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , value(toStringView(cbor, S_VALUE))
+{
+}
+
+Interface::Interface(const QCborValue &cbor)
+{
+ if (cbor.isArray()) {
+ QCborArray needlessWrapping = cbor.toArray();
+ className = needlessWrapping.size() > 0
+ ? toStringView(needlessWrapping[0].toMap(), S_CLASS_NAME)
+ : QAnyStringView();
+ } else {
+ className = toStringView(cbor.toMap(), S_CLASS_NAME);
+ }
+}
+
+Property::Property(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , type(toStringView(cbor, S_TYPE))
+ , member(toStringView(cbor, S_MEMBER))
+ , read(toStringView(cbor, S_READ))
+ , write(toStringView(cbor, S_WRITE))
+ , reset(toStringView(cbor, S_RESET))
+ , notify(toStringView(cbor, S_NOTIFY))
+ , bindable(toStringView(cbor, S_BINDABLE))
+ , privateClass(toStringView(cbor, S_PRIVATE_CLASS))
+ , index(cbor[S_INDEX].toInteger(-1))
+ , revision(getRevision(cbor))
+ , isFinal(cbor[S_FINAL].toBool())
+ , isConstant(cbor[S_CONSTANT].toBool())
+ , isRequired(cbor[S_REQUIRED].toBool())
+{
+}
+
+Argument::Argument(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , type(toStringView(cbor, S_TYPE))
+{
+}
+
+Method::Method(const QCborMap &cbor, bool isConstructor)
+ : name(toStringView(cbor, S_NAME))
+ , returnType(toStringView(cbor, S_RETURN_TYPE))
+ , revision(getRevision(cbor))
+ , access(getAccess(cbor))
+ , isCloned(cbor[S_IS_CLONED].toBool())
+ , isJavaScriptFunction(cbor[S_IS_JAVASCRIPT_FUNCTION].toBool())
+ , isConstructor(isConstructor || cbor[S_IS_CONSTRUCTOR].toBool())
+{
+ const QCborArray args = cbor[S_ARGUMENTS].toArray();
+ for (const QCborValue &argument : args)
+ arguments.emplace_back(argument.toMap());
+
+ if (arguments.size() == 1) {
+ const QAnyStringView type = arguments[0].type;
+ if (type == "QQmlV4FunctionPtr"_L1 || type == "QQmlV4Function*"_L1)
+ isJavaScriptFunction = true;
+ }
+}
+
+Enum::Enum(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , alias(toStringView(cbor, S_ALIAS))
+ , type(toStringView(cbor, S_TYPE))
+ , isFlag(cbor[S_IS_FLAG].toBool())
+ , isClass(cbor[S_IS_CLASS].toBool())
+{
+ const QCborArray vals = cbor[S_VALUES].toArray();
+ for (const QCborValue &value : vals)
+ values.emplace_back(toStringView(value));
+}
+
+MetaTypePrivate::MetaTypePrivate(const QCborMap &cbor, const QString &inputFile)
+ : cbor(cbor)
+ , inputFile(inputFile)
+{
+ className = toStringView(cbor, S_CLASS_NAME);
+ qualifiedClassName = toStringView(cbor, S_QUALIFIED_CLASS_NAME);
+
+ const QCborArray cborSuperClasses = cbor[S_SUPER_CLASSES].toArray();
+ for (const QCborValue &superClass : cborSuperClasses)
+ superClasses.emplace_back(superClass.toMap());
+
+ const QCborArray cborClassInfos = cbor[S_CLASS_INFOS].toArray();
+ for (const QCborValue &classInfo : cborClassInfos)
+ classInfos.emplace_back(classInfo.toMap());
+
+ const QCborArray cborIfaces = cbor[S_INTERFACES].toArray();
+ for (const QCborValue &iface : cborIfaces)
+ ifaces.emplace_back(iface);
+
+ const QCborArray cborProperties = cbor[S_PROPERTIES].toArray();
+ for (const QCborValue &property : cborProperties)
+ properties.emplace_back(property.toMap());
+
+ for (const QCborArray &cborMethods : { cbor[S_METHODS].toArray(), cbor[S_SLOTS].toArray() }) {
+ for (const QCborValue &method : cborMethods)
+ methods.emplace_back(method.toMap(), false);
+ }
+
+ const QCborArray cborSigs = cbor[S_SIGNALS].toArray();
+ for (const QCborValue &sig : cborSigs)
+ sigs.emplace_back(sig.toMap(), false);
+
+ const QCborArray cborConstructors = cbor[S_CONSTRUCTORS].toArray();
+ for (const QCborValue &constructor : cborConstructors)
+ constructors.emplace_back(constructor.toMap(), true);
+
+ const QCborArray cborEnums = cbor[S_ENUMS].toArray();
+ for (const QCborValue &enumerator : cborEnums)
+ enums.emplace_back(enumerator.toMap());
+
+ if (cbor[S_GADGET].toBool())
+ kind = Kind::Gadget;
+ else if (cbor[S_OBJECT].toBool())
+ kind = Kind::Object;
+ else if (cbor[S_NAMESPACE].toBool())
+ kind = Kind::Namespace;
+}
+
+MetaType::MetaType(const QCborMap &cbor, const QString &inputFile)
+ : d(s_pool.emplace_back(std::make_unique<MetaTypePrivate>(cbor, inputFile)).get())
+{}
+
+QT_END_NAMESPACE