diff options
| author | Ulf Hermann <[email protected]> | 2024-05-17 20:21:34 +0200 |
|---|---|---|
| committer | Ulf Hermann <[email protected]> | 2024-05-23 16:29:16 +0200 |
| commit | 5430aeadd88b35c81c08ec96e91206e07346866c (patch) | |
| tree | 0f0d9c50ddbca869a132b2990fbfe6ace6c7a4fc | |
| parent | a59f727ae58ed57ae52a1162e8e46755475a9269 (diff) | |
QtQml: Allow "using" declarations in QML type registrations
This makes qmltyperegistrar lexically replace occurrences of the alias
with the original type. It therefore assumes that the types are actually
the same. It cannot check whether this assumption holds, and you can
actually provide "using" declarations for types that are not the same if
you have the respective conversion operators in place.
Obviously this is quite dangerous. Therefore it remains internal for
now.
Task-number: QTBUG-125240
Change-Id: Idb54cb42fd1289cbd9438272ae3e9e01a30869eb
Reviewed-by: Fabian Kosmale <[email protected]>
| -rw-r--r-- | src/qmlintegration/qqmlintegration.h | 4 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qmetatypesjsonprocessor.cpp | 32 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qmetatypesjsonprocessor_p.h | 15 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qqmltyperegistrar.cpp | 6 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qqmltyperegistrar_p.h | 2 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qqmltyperegistrarconstants_p.h | 1 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qqmltypesclassdescription.cpp | 97 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qqmltypesclassdescription_p.h | 8 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qqmltypescreator.cpp | 2 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qqmltypescreator_p.h | 2 | ||||
| -rw-r--r-- | tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp | 14 | ||||
| -rw-r--r-- | tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h | 20 | ||||
| -rw-r--r-- | tools/qmltyperegistrar/qmltyperegistrar.cpp | 1 |
13 files changed, 163 insertions, 41 deletions
diff --git a/src/qmlintegration/qqmlintegration.h b/src/qmlintegration/qqmlintegration.h index f1a990a79c..0cea1bab30 100644 --- a/src/qmlintegration/qqmlintegration.h +++ b/src/qmlintegration/qqmlintegration.h @@ -192,4 +192,8 @@ QT_END_NAMESPACE #define QML_CUSTOMPARSER \ Q_CLASSINFO("QML.HasCustomParser", "true") +#define QML_USING(ORIGINAL) \ + using QmlUsing ## ORIGINAL = ORIGINAL; \ + Q_CLASSINFO("QML.Using", #ORIGINAL) + #endif diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp index 480d4ba187..5d5d694937 100644 --- a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp +++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp @@ -227,8 +227,9 @@ MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess( // 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; + QList<QAnyStringView> primitiveAliases; + UsingDeclaration usingDeclaration; + RegistrationMode mode = NoRegistration; bool isSelfExtendingValueType = false; bool hasJavaScriptExtension = false; @@ -237,9 +238,9 @@ MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess( for (const ClassInfo &classInfo : classDef.classInfos()) { if (classInfo.name == S_FOREIGN) - foreign = classInfo.value; + usingDeclaration.alias = classInfo.value; else if (classInfo.name == S_PRIMITIVE_ALIAS) - aliases.append(classInfo.value); + primitiveAliases.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) @@ -248,6 +249,8 @@ MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess( isRootObject = (classInfo.value == S_TRUE); else if (classInfo.name == S_SEQUENCE) isSequence = true; + else if (classInfo.name == S_USING) + usingDeclaration.original = classInfo.value; else if (populateMode == PopulateMode::Yes && classInfo.name == S_ELEMENT) { switch (classDef.kind()) { case MetaType::Kind::Object: @@ -270,9 +273,10 @@ MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess( } return PreProcessResult { - aliases, + primitiveAliases, + usingDeclaration, (!isRootObject && (isSequence || isSelfExtendingValueType || hasJavaScriptExtension)) - ? foreign + ? usingDeclaration.alias : QAnyStringView(), mode }; @@ -517,7 +521,7 @@ void MetaTypesJsonProcessor::addRelatedTypes() const auto addProperties = [&](const MetaType &context, const QList<QAnyStringView> &namespaces) { for (const Property &property : context.properties()) { - ResolvedTypeAlias resolved(property.type); + ResolvedTypeAlias resolved(property.type, m_usingDeclarations); if (!resolved.type.isEmpty()) addType(context, resolved.type, namespaces, "property"); } @@ -529,12 +533,12 @@ void MetaTypesJsonProcessor::addRelatedTypes() : {context.methods(), context.constructors(), context.sigs() }) { for (const Method &methodObject : methods) { for (const Argument &argument : std::as_const(methodObject.arguments)) { - ResolvedTypeAlias resolved(argument.type); + ResolvedTypeAlias resolved(argument.type, m_usingDeclarations); if (!resolved.type.isEmpty()) addType(context, resolved.type, namespaces, "argument"); } - ResolvedTypeAlias resolved(methodObject.returnType); + ResolvedTypeAlias resolved(methodObject.returnType, m_usingDeclarations); if (!resolved.type.isEmpty()) addType(context, resolved.type, namespaces, "return"); } @@ -544,7 +548,7 @@ void MetaTypesJsonProcessor::addRelatedTypes() const auto addEnums = [&](const MetaType &context, const QList<QAnyStringView> &namespaces) { for (const Enum &enumerator : context.enums()) { - ResolvedTypeAlias resolved(enumerator.type); + ResolvedTypeAlias resolved(enumerator.type, m_usingDeclarations); if (!resolved.type.isEmpty()) addType(context, resolved.type, namespaces, "enum"); } @@ -557,7 +561,7 @@ void MetaTypesJsonProcessor::addRelatedTypes() addType(classDef, obj.value, namespaces, "attached"); return true; } else if (objNameValue == S_SEQUENCE) { - ResolvedTypeAlias value(obj.value); + ResolvedTypeAlias value(obj.value, m_usingDeclarations); addType(classDef, value.type, namespaces, "sequence value"); return true; } else if (objNameValue == S_EXTENDED) { @@ -675,6 +679,9 @@ void MetaTypesJsonProcessor::processTypes(const QCborMap &types) m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive); m_primitiveTypes.append(preprocessed.primitiveAliases); } + + if (preprocessed.usingDeclaration.isValid()) + m_usingDeclarations.append(preprocessed.usingDeclaration); } } @@ -691,6 +698,9 @@ void MetaTypesJsonProcessor::processForeignTypes(const QCborMap &types) m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive); m_primitiveTypes.append(preprocessed.primitiveAliases); } + + if (preprocessed.usingDeclaration.isValid()) + m_usingDeclarations.append(preprocessed.usingDeclaration); } } diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h index 9d86665c52..ba9e32591e 100644 --- a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h +++ b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h @@ -209,6 +209,13 @@ private: const MetaTypePrivate *d = &s_empty; }; +struct UsingDeclaration { + QAnyStringView alias; + QAnyStringView original; + + bool isValid() const { return !alias.isEmpty() && !original.isEmpty(); } +}; + class MetaTypesJsonProcessor { public: @@ -227,6 +234,7 @@ public: QVector<MetaType> types() const { return m_types; } QVector<MetaType> foreignTypes() const { return m_foreignTypes; } QList<QAnyStringView> referencedTypes() const { return m_referencedTypes; } + QList<UsingDeclaration> usingDeclarations() const { return m_usingDeclarations; } QList<QString> includes() const { return m_includes; } QString extractRegisteredTypes() const; @@ -241,15 +249,11 @@ private: struct PreProcessResult { QList<QAnyStringView> primitiveAliases; + UsingDeclaration usingDeclaration; QAnyStringView foreignPrimitive; RegistrationMode mode; }; - struct PotentialPrimitiveType { - QAnyStringView name; - QString file; - }; - enum class PopulateMode { No, Yes }; static PreProcessResult preProcess(const MetaType &classDef, PopulateMode populateMode); void addRelatedTypes(); @@ -267,6 +271,7 @@ private: QList<QString> m_includes; QList<QAnyStringView> m_referencedTypes; QList<QAnyStringView> m_primitiveTypes; + QList<UsingDeclaration> m_usingDeclarations; QVector<MetaType> m_types; QVector<MetaType> m_foreignTypes; bool m_privateIncludes = false; diff --git a/src/qmltyperegistrar/qqmltyperegistrar.cpp b/src/qmltyperegistrar/qqmltyperegistrar.cpp index 058a3180b0..3841c5e442 100644 --- a/src/qmltyperegistrar/qqmltyperegistrar.cpp +++ b/src/qmltyperegistrar/qqmltyperegistrar.cpp @@ -504,6 +504,7 @@ bool QmlTypeRegistrar::generatePluginTypes(const QString &pluginTypesFile) creator.setOwnTypes(m_types); creator.setForeignTypes(m_foreignTypes); creator.setReferencedTypes(m_referencedTypes); + creator.setUsingDeclarations(m_usingDeclarations); creator.setModule(m_module.toUtf8()); creator.setVersion(QTypeRevision::fromVersion(m_moduleVersion.majorVersion(), 0)); @@ -539,4 +540,9 @@ void QmlTypeRegistrar::setReferencedTypes(const QList<QAnyStringView> &reference m_referencedTypes = referencedTypes; } +void QmlTypeRegistrar::setUsingDeclarations(const QList<UsingDeclaration> &usingDeclarations) +{ + m_usingDeclarations = usingDeclarations; +} + QT_END_NAMESPACE diff --git a/src/qmltyperegistrar/qqmltyperegistrar_p.h b/src/qmltyperegistrar/qqmltyperegistrar_p.h index e0811f2ade..fdfbe15ab7 100644 --- a/src/qmltyperegistrar/qqmltyperegistrar_p.h +++ b/src/qmltyperegistrar/qqmltyperegistrar_p.h @@ -35,6 +35,7 @@ class QmlTypeRegistrar QVector<MetaType> m_types; QVector<MetaType> m_foreignTypes; QList<QAnyStringView> m_referencedTypes; + QList<UsingDeclaration> m_usingDeclarations; MetaType findType(QAnyStringView name) const; MetaType findTypeForeign(QAnyStringView name) const; @@ -48,6 +49,7 @@ public: void setIncludes(const QList<QString> &includes); void setTypes(const QVector<MetaType> &types, const QVector<MetaType> &foreignTypes); void setReferencedTypes(const QList<QAnyStringView> &referencedTypes); + void setUsingDeclarations(const QList<UsingDeclaration> &usingDeclarations); static bool argumentsFromCommandLineAndFile(QStringList &allArguments, const QStringList &arguments); diff --git a/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h b/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h index 465cc23532..47e3d14cfa 100644 --- a/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h +++ b/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h @@ -163,6 +163,7 @@ static constexpr QLatin1StringView S_ROOT { "QML.Root" } static constexpr QLatin1StringView S_SEQUENCE { "QML.Sequence" }; static constexpr QLatin1StringView S_SINGLETON { "QML.Singleton" }; static constexpr QLatin1StringView S_UNCREATABLE_REASON { "QML.UncreatableReason" }; +static constexpr QLatin1StringView S_USING { "QML.Using" }; } // namespace Qml } // namespace MetatypesJson diff --git a/src/qmltyperegistrar/qqmltypesclassdescription.cpp b/src/qmltyperegistrar/qqmltypesclassdescription.cpp index 7c42052a70..bbb1b25f9b 100644 --- a/src/qmltyperegistrar/qqmltypesclassdescription.cpp +++ b/src/qmltyperegistrar/qqmltypesclassdescription.cpp @@ -457,48 +457,99 @@ FoundType QmlTypesClassDescription::collectRelated( return FoundType(); } +struct UsingCompare { + bool operator()(const UsingDeclaration &a, QAnyStringView b) const + { + return a.alias < b; + } -ResolvedTypeAlias::ResolvedTypeAlias(QAnyStringView alias) + bool operator()(QAnyStringView a, const UsingDeclaration &b) const + { + return a < b.alias; + } +}; + +ResolvedTypeAlias::ResolvedTypeAlias( + QAnyStringView alias, const QList<UsingDeclaration> &usingDeclarations) : type(alias) { + handleVoid(); if (type.isEmpty()) return; - if (type == "void") { - type = ""; - return; + handleList(); + + if (!isList) { + handlePointer(); + handleConst(); + } + + while (true) { + const auto usingDeclaration = std::equal_range( + usingDeclarations.begin(), usingDeclarations.end(), type, UsingCompare()); + if (usingDeclaration.first == usingDeclaration.second) + break; + + type = usingDeclaration.first->original; + handleVoid(); + if (type.isEmpty()) + return; + + if (isPointer) { + handleConst(); + continue; + } + + if (!isList) { + handleList(); + if (!isList) { + handlePointer(); + handleConst(); + } + } } +} - // This is a best effort approach and will not return correct results in the - // presence of typedefs. +void ResolvedTypeAlias::handleVoid() +{ + if (type == "void") + type = ""; +} - auto handleList = [&](QLatin1StringView list) { +void ResolvedTypeAlias::handleList() +{ + for (QLatin1StringView list : {"QQmlListProperty<"_L1, "QList<"_L1}) { if (!startsWith(type, list) || type.back() != '>'_L1) - return false; + continue; const int listSize = list.size(); const QAnyStringView elementType = trimmed(type.mid(listSize, type.size() - listSize - 1)); - // QQmlListProperty internally constructs the pointer. Passing an explicit '*' will - // produce double pointers. QList is only for value types. We can't handle QLists - // of pointers (unless specially registered, but then they're not isList). + // QQmlListProperty internally constructs the pointer. Passing an explicit '*' will + // produce double pointers. QList is only for value types. We can't handle QLists + // of pointers (unless specially registered, but then they're not isList). if (elementType.back() == '*'_L1) - return false; + continue; isList = true; type = elementType; - return true; - }; + return; + } +} - if (!handleList("QQmlListProperty<"_L1) && !handleList("QList<"_L1)) { - if (type.back() == '*'_L1) { - isPointer = true; - type = type.chopped(1); - } - if (startsWith(type, "const "_L1)) { - isConstant = true; - type = type.sliced(strlen("const ")); - } +void ResolvedTypeAlias::handlePointer() +{ + if (type.back() == '*'_L1) { + isPointer = true; + type = type.chopped(1); + } +} + +void ResolvedTypeAlias::handleConst() +{ + if (startsWith(type, "const "_L1)) { + isConstant = true; + type = type.sliced(strlen("const ")); } } diff --git a/src/qmltyperegistrar/qqmltypesclassdescription_p.h b/src/qmltyperegistrar/qqmltypesclassdescription_p.h index 60aafb948f..b48a0670da 100644 --- a/src/qmltyperegistrar/qqmltypesclassdescription_p.h +++ b/src/qmltyperegistrar/qqmltypesclassdescription_p.h @@ -113,12 +113,18 @@ private: struct ResolvedTypeAlias { - ResolvedTypeAlias(QAnyStringView alias); + ResolvedTypeAlias(QAnyStringView alias, const QList<UsingDeclaration> &usingDeclarations); QAnyStringView type; bool isList = false; bool isPointer = false; bool isConstant = false; + +private: + void handleVoid(); + void handleList(); + void handlePointer(); + void handleConst(); }; QT_END_NAMESPACE diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp index 297a23679a..f523c69bea 100644 --- a/src/qmltyperegistrar/qqmltypescreator.cpp +++ b/src/qmltyperegistrar/qqmltypescreator.cpp @@ -163,7 +163,7 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle void QmlTypesCreator::writeType(QAnyStringView type) { - ResolvedTypeAlias resolved(type); + ResolvedTypeAlias resolved(type, m_usingDeclarations); if (resolved.type.isEmpty()) return; diff --git a/src/qmltyperegistrar/qqmltypescreator_p.h b/src/qmltyperegistrar/qqmltypescreator_p.h index 10c5a3d35d..c30c00c889 100644 --- a/src/qmltyperegistrar/qqmltypescreator_p.h +++ b/src/qmltyperegistrar/qqmltypescreator_p.h @@ -35,6 +35,7 @@ public: void setReferencedTypes(QList<QAnyStringView> referencedTypes) { m_referencedTypes = std::move(referencedTypes); } void setModule(QByteArray module) { m_module = std::move(module); } void setVersion(QTypeRevision version) { m_version = version; } + void setUsingDeclarations(QList<UsingDeclaration> usingDeclarations) { m_usingDeclarations = std::move(usingDeclarations);} private: void writeComponent(const QmlTypesClassDescription &collector); @@ -54,6 +55,7 @@ private: QVector<MetaType> m_ownTypes; QVector<MetaType> m_foreignTypes; QList<QAnyStringView> m_referencedTypes; + QList<UsingDeclaration> m_usingDeclarations; QByteArray m_module; QTypeRevision m_version = QTypeRevision::zero(); }; diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp index 822caea0d0..7ea5dcb51c 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp @@ -1023,4 +1023,18 @@ void tst_qmltyperegistrar::constReturnType() })")); } +void tst_qmltyperegistrar::usingDeclaration() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "WithMyInt" + accessSemantics: "reference" + prototype: "QObject" + exports: ["QmlTypeRegistrarTest/WithMyInt 1.0"] + isCreatable: true + exportMetaObjectRevisions: [256] + Property { name: "a"; type: "int"; read: "a"; index: 0; isReadonly: true; isConstant: true } + })")); +} + QTEST_MAIN(tst_qmltyperegistrar) diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h index 371fb840d1..efb40fd426 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h @@ -792,6 +792,24 @@ public: Q_INVOKABLE const QObject *getObject() { return nullptr; } }; +using myint = int; + +struct IntAlias +{ + Q_GADGET + QML_FOREIGN(myint) + QML_USING(int); +}; + +class WithMyInt : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(myint a READ a CONSTANT) +public: + myint a() const { return 10; } +}; + class tst_qmltyperegistrar : public QObject { Q_OBJECT @@ -865,6 +883,8 @@ private slots: void enumList(); void constReturnType(); + void usingDeclaration(); + private: QByteArray qmltypesData; }; diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp index 248926fb33..937c8a356f 100644 --- a/tools/qmltyperegistrar/qmltyperegistrar.cpp +++ b/tools/qmltyperegistrar/qmltyperegistrar.cpp @@ -198,6 +198,7 @@ int main(int argc, char **argv) return EXIT_SUCCESS; typeRegistrar.setReferencedTypes(processor.referencedTypes()); + typeRegistrar.setUsingDeclarations(processor.usingDeclarations()); const QString qmltypes = parser.value(pluginTypesOption); if (!typeRegistrar.generatePluginTypes(qmltypes)) { error(qmltypes) << "Cannot generate qmltypes file"; |
