From 588d624837ae0174139f8db930ad20612463b1dd Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Fri, 20 Nov 2020 13:20:52 +0100 Subject: qmltyperegistrar: Expose interface information Change-Id: Ica3f5c6696542921bc8d399cd46d901ba06f6d83 Reviewed-by: Ulf Hermann --- src/imports/tooling/Component.qml | 1 + src/qmlcompiler/qqmljsscope_p.h | 4 ++ src/qmlcompiler/qqmljstypedescriptionreader.cpp | 47 +++++++++++++++++++--- src/qmlcompiler/qqmljstypedescriptionreader_p.h | 3 ++ src/qmltyperegistrar/qmltypesclassdescription.cpp | 9 +++++ src/qmltyperegistrar/qmltypesclassdescription.h | 1 + src/qmltyperegistrar/qmltypescreator.cpp | 8 ++++ .../qml/qmltyperegistrar/tst_qmltyperegistrar.cpp | 6 +++ .../qml/qmltyperegistrar/tst_qmltyperegistrar.h | 24 +++++++++++ 9 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/imports/tooling/Component.qml b/src/imports/tooling/Component.qml index d87e613907..0045a75a6d 100644 --- a/src/imports/tooling/Component.qml +++ b/src/imports/tooling/Component.qml @@ -47,6 +47,7 @@ QtObject { property string prototype property var exports: [] property var exportMetaObjectRevisions: [] + property var interfaces: [] property string attachedType property string valueType property bool isSingleton: false diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index de6ea58906..0801e6eb7e 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -185,6 +185,9 @@ public: void addExport(const QString &name, const QString &package, const QTypeRevision &version); QList exports() const { return m_exports; } + void setInterfaceNames(const QStringList& interfaces) { m_interfaceNames = interfaces; } + QStringList interfaceNames() { return m_interfaceNames; } + // If isComposite(), this is the QML/JS name of the prototype. Otherwise it's the // relevant base class (in the hierarchy starting from QObject) of a C++ type. void setBaseTypeName(const QString &baseTypeName) { m_baseTypeName = baseTypeName; } @@ -272,6 +275,7 @@ private: ScopeType m_scopeType = QMLScope; QList m_exports; + QStringList m_interfaceNames; QString m_defaultPropertyName; QString m_attachedTypeName; diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp index 8bdfd89370..6f474bbe10 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp +++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp @@ -220,6 +220,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast) scope->setDefaultPropertyName(readStringBinding(script)); } else if (name == QLatin1String("exports")) { readExports(script, scope); + } else if (name == QLatin1String("interfaces")) { + readInterfaces(script, scope); } else if (name == QLatin1String("exportMetaObjectRevisions")) { readMetaObjectRevisions(script, scope); } else if (name == QLatin1String("attachedType")) { @@ -249,7 +251,7 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast) } else { addWarning(script->firstSourceLocation(), tr("Expected only name, prototype, defaultProperty, attachedType, " - "valueType, exports, isSingleton, isCreatable, isComposite and " + "valueType, exports, interfaces, isSingleton, isCreatable, isComposite and " "exportMetaObjectRevisions script bindings, not \"%1\".").arg(name)); } } else { @@ -551,35 +553,47 @@ int QQmlJSTypeDescriptionReader::readIntBinding(UiScriptBinding *ast) return i; } -void QQmlJSTypeDescriptionReader::readExports(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope) +ArrayPattern* QQmlJSTypeDescriptionReader::getArray(UiScriptBinding *ast) { Q_ASSERT(ast); if (!ast->statement) { addError(ast->colonToken, tr("Expected array of strings after colon.")); - return; + return nullptr; } auto *expStmt = cast(ast->statement); if (!expStmt) { addError(ast->statement->firstSourceLocation(), tr("Expected array of strings after colon.")); - return; + return nullptr; } auto *arrayLit = cast(expStmt->expression); if (!arrayLit) { addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon.")); - return; + return nullptr; } + return arrayLit; +} + +void QQmlJSTypeDescriptionReader::readExports(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope) +{ + auto *arrayLit = getArray(ast); + + if (!arrayLit) + return; + for (PatternElementList *it = arrayLit->elements; it; it = it->next) { auto *stringLit = cast(it->element->initializer); + if (!stringLit) { addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only string literal members.")); return; } + QString exp = stringLit->value.toString(); int slashIdx = exp.indexOf(QLatin1Char('/')); int spaceIdx = exp.indexOf(QLatin1Char(' ')); @@ -601,6 +615,29 @@ void QQmlJSTypeDescriptionReader::readExports(UiScriptBinding *ast, const QQmlJS } } +void QQmlJSTypeDescriptionReader::readInterfaces(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope) +{ + auto *arrayLit = getArray(ast); + + if (!arrayLit) + return; + + QStringList list; + + for (PatternElementList *it = arrayLit->elements; it; it = it->next) { + auto *stringLit = cast(it->element->initializer); + if (!stringLit) { + addError(arrayLit->firstSourceLocation(), + tr("Expected array literal with only string literal members.")); + return; + } + + list << stringLit->value.toString(); + } + + scope->setInterfaceNames(list); +} + void QQmlJSTypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope) { diff --git a/src/qmlcompiler/qqmljstypedescriptionreader_p.h b/src/qmlcompiler/qqmljstypedescriptionreader_p.h index 770a53f01d..f99cda0605 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader_p.h +++ b/src/qmlcompiler/qqmljstypedescriptionreader_p.h @@ -80,12 +80,15 @@ private: QTypeRevision readNumericVersionBinding(QQmlJS::AST::UiScriptBinding *ast); int readIntBinding(QQmlJS::AST::UiScriptBinding *ast); void readExports(QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope); + void readInterfaces(QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope); void readMetaObjectRevisions(QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope); void readEnumValues(QQmlJS::AST::UiScriptBinding *ast, QQmlJSMetaEnum *metaEnum); void addError(const QQmlJS::SourceLocation &loc, const QString &message); void addWarning(const QQmlJS::SourceLocation &loc, const QString &message); + QQmlJS::AST::ArrayPattern *getArray(QQmlJS::AST::UiScriptBinding *ast); + QString m_fileName; QString m_source; QString m_errorMessage; diff --git a/src/qmltyperegistrar/qmltypesclassdescription.cpp b/src/qmltyperegistrar/qmltypesclassdescription.cpp index 94cfd2c2a4..add59ddd17 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.cpp +++ b/src/qmltyperegistrar/qmltypesclassdescription.cpp @@ -66,6 +66,15 @@ void QmlTypesClassDescription::collect(const QJsonObject *classDef, const QJsonObject *origClassDef = classDef; // if we find QML.Foreign, classDef changes. if (file.isEmpty() && classDef->value(QLatin1String("registerable")).toBool()) file = classDef->value(QLatin1String("inputFile")).toString(); + + if (classDef->contains(QLatin1String("interfaces"))) { + QJsonArray array = classDef->value(QLatin1String("interfaces")).toArray(); + for (const QJsonValue& value : array) { + auto object = value.toArray()[0].toObject(); + implementsInterfaces << object[QLatin1String("className")].toString(); + } + } + const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray(); for (const QJsonValue classInfo : classInfos) { const QJsonObject obj = classInfo.toObject(); diff --git a/src/qmltyperegistrar/qmltypesclassdescription.h b/src/qmltyperegistrar/qmltypesclassdescription.h index 7e496159ff..fd172d1922 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.h +++ b/src/qmltyperegistrar/qmltypesclassdescription.h @@ -52,6 +52,7 @@ struct QmlTypesClassDescription bool isCreatable = true; bool isSingleton = false; bool isRootClass = false; + QStringList implementsInterfaces; enum CollectMode { TopLevel, diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp index df257d1f53..2a3c10829a 100644 --- a/src/qmltyperegistrar/qmltypescreator.cpp +++ b/src/qmltyperegistrar/qmltypescreator.cpp @@ -96,6 +96,14 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle if (!collector.attachedType.isEmpty()) m_qml.writeScriptBinding(QLatin1String("attachedType"), enquote(collector.attachedType)); + + if (!collector.implementsInterfaces.isEmpty()) { + QStringList interfaces; + for (const QString &interface : collector.implementsInterfaces) + interfaces << enquote(interface); + + m_qml.writeArrayBinding(QLatin1String("interfaces"), interfaces); + } } void QmlTypesCreator::writeType(const QJsonObject &property, const QString &key, bool isReadonly, diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp index 3d3a0e7fbd..f8ffa1166f 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp @@ -111,4 +111,10 @@ void tst_qmltyperegistrar::pastMajorVersions() QVERIFY2(!c.isError(), qPrintable(c.errorString())); } +void tst_qmltyperegistrar::implementsInterfaces() +{ + QVERIFY(qmltypesData.contains("interfaces: [\"Interface\"]")); + QVERIFY(qmltypesData.contains("interfaces: [\"Interface\", \"Interface2\"]")); +} + QTEST_MAIN(tst_qmltyperegistrar) diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h index 3c00e04357..b13debcf1b 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h @@ -34,6 +34,29 @@ #include #include +class Interface {}; +class Interface2 {}; + +QT_BEGIN_NAMESPACE +Q_DECLARE_INTERFACE(Interface, "io.qt.bugreports.Interface"); +Q_DECLARE_INTERFACE(Interface2, "io.qt.bugreports.Interface2"); +QT_END_NAMESPACE + + +class ImplementsInterfaces : public QObject, public Interface +{ + Q_OBJECT + QML_ELEMENT + QML_IMPLEMENTS_INTERFACES(Interface) +}; + +class ImplementsInterfaces2 : public QObject, public Interface, public Interface2 +{ + Q_OBJECT + QML_ELEMENT + QML_IMPLEMENTS_INTERFACES(Interface Interface2) +}; + class ExcessiveVersion : public QObject { Q_OBJECT @@ -124,6 +147,7 @@ private slots: void isBindable(); void restrictToImportVersion(); void pastMajorVersions(); + void implementsInterfaces(); private: QByteArray qmltypesData; -- cgit v1.2.3