diff options
11 files changed, 339 insertions, 79 deletions
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index e34f7a407f..ea4a69f05d 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1080,6 +1080,13 @@ bool QQmlType::isCreatable() const return d && d->regType == CppType && d->extraData.cd->newFunc; } +QQmlType::ExtensionFunc QQmlType::extensionFunction() const +{ + if (!d || d->regType != CppType) + return nullptr; + return d->extraData.cd->extFunc; +} + bool QQmlType::isExtendedType() const { if (!d) diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 4a5e4ba266..b3ca6acd64 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -201,6 +201,8 @@ public: QQmlCustomParser *customParser() const; bool isCreatable() const; + typedef QObject *(*ExtensionFunc)(QObject *); + ExtensionFunc extensionFunction() const; bool isExtendedType() const; QString noCreationReason() const; diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/extendedtype.pro b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/extendedtype.pro new file mode 100644 index 0000000000..24243aa622 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/extendedtype.pro @@ -0,0 +1,22 @@ +TEMPLATE = lib +TARGET = ExtendedType +QT += qml +CONFIG += qt plugin + +CONFIG -= debug_and_release_target +!build_pass:qtConfig(debug_and_release): CONFIG += release + +TARGET = $$qtLibraryTarget($$TARGET) + +SOURCES += \ + plugin.cpp + +HEADERS += \ + plugin.h \ + types.h + +!equals(_PRO_FILE_PWD_, $$OUT_PWD) { + cp.files = qmldir plugins.qmltypes + cp.path = $$OUT_PWD + COPIES += cp +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp new file mode 100644 index 0000000000..423fbc1f4c --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "plugin.h" +#include "types.h" + +#include <qqml.h> + +void Plugin::registerTypes(const char *uri) +{ + // @uri dumper.ExtendedType + qmlRegisterType<Type>(uri, 1, 0, "Type"); + qmlRegisterExtendedType<Type, ExtendedType>(uri, 1, 1, "Type"); + qmlRegisterType<DerivedType2>(uri, 1, 1, "DerivedType"); +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h new file mode 100644 index 0000000000..b677fe2940 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLUGIN_H +#define PLUGIN_H + +#include <QQmlExtensionPlugin> + +class Plugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri); +}; + +#endif // PLUGIN_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugins.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugins.qmltypes new file mode 100644 index 0000000000..d84eb0011a --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugins.qmltypes @@ -0,0 +1,35 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick dumper.ExtendedType 1.1 .' + +Module { + dependencies: [] + Component { + name: "DerivedType1" + prototype: "Type" + Property { name: "m_exendedProperty2"; type: "int" } + } + Component { + name: "DerivedType2" + prototype: "DerivedType1" + exports: ["dumper.ExtendedType/DerivedType 1.1"] + exportMetaObjectRevisions: [0] + } + Component { + name: "Type" + defaultProperty: "data" + prototype: "QObject" + exports: [ + "dumper.ExtendedType/Type 1.0", + "dumper.ExtendedType/Type 1.1" + ] + exportMetaObjectRevisions: [0, 101] + Property { name: "baseProperty"; type: "int" } + Property { name: "extendedProperty"; revision: 101; type: "int" } + Property { name: "data"; revision: 101; type: "QObject"; isList: true; isReadonly: true } + } +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/qmldir new file mode 100644 index 0000000000..6693f403d9 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/qmldir @@ -0,0 +1,3 @@ +module dumper.ExtendedType +plugin ExtendedType + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h new file mode 100644 index 0000000000..dfba55a094 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TYPES_H +#define TYPES_H + +#include <QObject> +#include <QQmlListProperty> + +class Type : public QObject +{ + Q_OBJECT + Q_PROPERTY(int baseProperty MEMBER m_baseProperty) + +public: + Type(QObject *parent = nullptr) + : QObject(parent) {} + +private: + int m_baseProperty; +}; + +class ExtendedType : public QObject +{ + Q_OBJECT + Q_PROPERTY(int extendedProperty MEMBER m_extendedProperty) + Q_PROPERTY(QQmlListProperty<QObject> data READ data) + Q_CLASSINFO("DefaultProperty", "data") + +public: + ExtendedType(QObject *parent = nullptr) + : QObject(parent) {} + QQmlListProperty<QObject> data() { return QQmlListProperty<QObject>(this, m_data); } + +private: + QList<QObject *> m_data; + int m_extendedProperty; +}; + +class DerivedType1 : public Type +{ + Q_OBJECT + Q_PROPERTY(int m_exendedProperty2 MEMBER m_extendedProperty2) + +public: + DerivedType1(QObject *parent = nullptr) + : Type(parent) {} + +private: + int m_extendedProperty2; +}; + +class DerivedType2 : public DerivedType1 +{ + Q_OBJECT +public: + DerivedType2(QObject *parent = nullptr) + : DerivedType1(parent) {} +}; + +#endif // TYPES_H diff --git a/tests/auto/qml/qmlplugindump/qmlplugindump.pro b/tests/auto/qml/qmlplugindump/qmlplugindump.pro index cd33e5606e..34eb58c981 100644 --- a/tests/auto/qml/qmlplugindump/qmlplugindump.pro +++ b/tests/auto/qml/qmlplugindump/qmlplugindump.pro @@ -4,4 +4,5 @@ SUBDIRS += \ tst_qmlplugindump.pro \ data/dumper/Dummy/dummy.pro \ data/dumper/Imports/imports.pro \ - data/dumper/Versions/versions.pro + data/dumper/Versions/versions.pro \ + data/dumper/ExtendedType/extendedtype.pro diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp index a326ff9702..f673fca1d7 100644 --- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp +++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp @@ -127,6 +127,8 @@ void tst_qmlplugindump::plugin_data() QTest::newRow("dumper.Dummy") << "dumper.Dummy" << "1.0" << testFile("dumper/Dummy/plugins.qmltypes"); QTest::newRow("dumper.Imports") << "dumper.Imports" << "1.0" << testFile("dumper/Imports/plugins.qmltypes"); QTest::newRow("dumper.Versions") << "dumper.Versions" << "1.1" << testFile("dumper/Versions/plugins.qmltypes"); + QTest::newRow("dumper.ExtendedType") << "dumper.ExtendedType" + << "1.1" << testFile("dumper/ExtendedType/plugins.qmltypes"); } void tst_qmlplugindump::plugin() diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 750065e9a0..8f9ac2d1da 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -146,7 +146,7 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas) { - collectReachableMetaObjects(ty.metaObject(), metas, ty.isExtendedType()); + collectReachableMetaObjects(ty.baseMetaObject(), metas, ty.isExtendedType()); if (ty.attachedPropertiesType(engine)) collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas); } @@ -214,7 +214,7 @@ QByteArray convertToId(const QMetaObject *mo) void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<const QMetaObject *>& metas ) { const auto qmlAllTypes = QQmlMetaType::qmlAllTypes(); for (const QQmlType &ty : qmlAllTypes) { - if ( ! metas.contains(ty.metaObject()) ) { + if (!metas.contains(ty.baseMetaObject())) { if (!ty.isComposite()) { collectReachableMetaObjects(engine, ty, &metas); } else { @@ -232,55 +232,20 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, QSet<const QMetaObject *> metas; metas.insert(FriendlyQObject::qtMeta()); - QHash<QByteArray, QSet<QByteArray> > extensions; const auto qmlTypes = QQmlMetaType::qmlTypes(); for (const QQmlType &ty : qmlTypes) { if (!ty.isCreatable()) - noncreatables.insert(ty.metaObject()); + noncreatables.insert(ty.baseMetaObject()); if (ty.isSingleton()) - singletons.insert(ty.metaObject()); + singletons.insert(ty.baseMetaObject()); if (!ty.isComposite()) { - qmlTypesByCppName[ty.metaObject()->className()].insert(ty); - if (ty.isExtendedType()) - extensions[ty.typeName()].insert(ty.metaObject()->className()); + qmlTypesByCppName[ty.baseMetaObject()->className()].insert(ty); collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas); } else { qmlTypesByCompositeName[ty.elementName()].insert(ty); } } - // Adjust exports of the base object if there are extensions. - // For each export of a base object there can be a single extension object overriding it. - // Example: QDeclarativeGraphicsWidget overrides the QtQuick/QGraphicsWidget export - // of QGraphicsWidget. - for (auto it = extensions.cbegin(), end = extensions.cend(); it != end; ++it) { - QSet<QQmlType> baseExports = qmlTypesByCppName.value(it.key()); - - const QSet<QByteArray> extensionCppNames = it.value(); - for (const QByteArray &extensionCppName : extensionCppNames) { - const QSet<QQmlType> extensionExports = qmlTypesByCppName.value(extensionCppName); - - // remove extension exports from base imports - // unfortunately the QQmlType pointers don't match, so can't use QSet::subtract - QSet<QQmlType> newBaseExports; - for (const QQmlType &baseExport : qAsConst(baseExports)) { - bool match = false; - for (const QQmlType &extensionExport : extensionExports) { - if (baseExport.qmlTypeName() == extensionExport.qmlTypeName() - && baseExport.majorVersion() == extensionExport.majorVersion() - && baseExport.minorVersion() == extensionExport.minorVersion()) { - match = true; - break; - } - } - if (!match) - newBaseExports.insert(baseExport); - } - baseExports = newBaseExports; - } - qmlTypesByCppName[it.key()] = baseExports; - } - if (creatable) { // find even more QMetaObjects by instantiating QML types and running // over the instances @@ -405,15 +370,7 @@ public: void writeMetaContent(const QMetaObject *meta, KnownAttributes *knownAttributes = nullptr) { - QSet<QString> implicitSignals; - for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) { - const QMetaProperty &property = meta->property(index); - dump(property, knownAttributes); - if (knownAttributes) - knownAttributes->knownMethod(QByteArray(property.name()).append("Changed"), - 0, property.revision()); - implicitSignals.insert(QString("%1Changed").arg(QString::fromUtf8(property.name()))); - } + QSet<QString> implicitSignals = dumpMetaProperties(meta, 0, knownAttributes); if (meta == &QObject::staticMetaObject) { // for QObject, hide deleteLater() and onDestroyed @@ -533,6 +490,27 @@ public: qml->writeEndObject(); } + QString getDefaultProperty(const QMetaObject *meta) + { + for (int index = meta->classInfoCount() - 1; index >= 0; --index) { + QMetaClassInfo classInfo = meta->classInfo(index); + if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) { + return QLatin1String(classInfo.value()); + } + } + return QString(); + } + + struct QmlTypeInfo { + QmlTypeInfo() {} + QmlTypeInfo(const QString &exportString, int revision, const QMetaObject *extendedObject, QByteArray attachedTypeId) + : exportString(exportString), revision(revision), extendedObject(extendedObject), attachedTypeId(attachedTypeId) {} + QString exportString; + int revision = 0; + const QMetaObject *extendedObject = nullptr; + QByteArray attachedTypeId; + }; + void dump(QQmlEnginePrivate *engine, const QMetaObject *meta, bool isUncreatable, bool isSingleton) { qml->writeStartObject("Component"); @@ -540,29 +518,58 @@ public: QByteArray id = convertToId(meta); qml->writeScriptBinding(QLatin1String("name"), enquote(id)); - for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) { - QMetaClassInfo classInfo = meta->classInfo(index); - if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) { - qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value()))); - break; + // collect type information + QVector<QmlTypeInfo> typeInfo; + for (QQmlType type : qmlTypesByCppName.value(meta->className())) { + const QMetaObject *extendedObject = type.extensionFunction() ? type.metaObject() : nullptr; + QByteArray attachedTypeId; + if (const QMetaObject *attachedType = type.attachedPropertiesType(engine)) { + // Can happen when a type is registered that returns itself as attachedPropertiesType() + // because there is no creatable type to attach to. + if (attachedType != meta) + attachedTypeId = convertToId(attachedType); + } + const QString exportString = getExportString(type.qmlTypeName(), type.majorVersion(), type.minorVersion()); + int metaObjectRevision = type.metaObjectRevision(); + if (extendedObject) { + // emulate custom metaobjectrevision out of import + metaObjectRevision = type.majorVersion() * 100 + type.minorVersion(); + } + + QmlTypeInfo info = { exportString, metaObjectRevision, extendedObject, attachedTypeId }; + typeInfo.append(info); + } + + // sort to ensure stable output + std::sort(typeInfo.begin(), typeInfo.end(), [](const QmlTypeInfo &i1, const QmlTypeInfo &i2) { + return i1.revision < i2.revision; + }); + + // determine default property + // TODO: support revisioning of default property + QString defaultProperty = getDefaultProperty(meta); + if (defaultProperty.isEmpty()) { + for (const QmlTypeInfo &iter : typeInfo) { + if (iter.extendedObject) { + defaultProperty = getDefaultProperty(iter.extendedObject); + if (!defaultProperty.isEmpty()) + break; + } } } + if (!defaultProperty.isEmpty()) + qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(defaultProperty)); if (meta->superClass()) qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass()))); - const QSet<QQmlType> qmlTypes = qmlTypesByCppName.value(meta->className()); - if (!qmlTypes.isEmpty()) { - QHash<QString, QQmlType> exports; - - for (const QQmlType &qmlTy : qmlTypes) { - const QString exportString = getExportString(qmlTy.qmlTypeName(), qmlTy.majorVersion(), qmlTy.minorVersion()); - exports.insert(exportString, qmlTy); - } + if (!typeInfo.isEmpty()) { + QMap<QString, QString> exports; // sort exports + for (const QmlTypeInfo &iter : typeInfo) + exports.insert(iter.exportString, QString::number(iter.revision)); - // ensure exports are sorted and don't change order when the plugin is dumped again QStringList exportStrings = exports.keys(); - std::sort(exportStrings.begin(), exportStrings.end()); + QStringList metaObjectRevisions = exports.values(); qml->writeArrayBinding(QLatin1String("exports"), exportStrings); if (isUncreatable) @@ -571,20 +578,12 @@ public: if (isSingleton) qml->writeBooleanBinding(QLatin1String("isSingleton"), true); - // write meta object revisions - QStringList metaObjectRevisions; - for (const QString &exportString : qAsConst(exportStrings)) { - int metaObjectRevision = exports[exportString].metaObjectRevision(); - metaObjectRevisions += QString::number(metaObjectRevision); - } qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions); - if (const QMetaObject *attachedType = (*qmlTypes.begin()).attachedPropertiesType(engine)) { - // Can happen when a type is registered that returns itself as attachedPropertiesType() - // because there is no creatable type to attach to. - if (attachedType != meta) { - qml->writeScriptBinding(QLatin1String("attachedType"), enquote( - convertToId(attachedType))); + for (const QmlTypeInfo &iter : typeInfo) { + if (!iter.attachedTypeId.isEmpty()) { + qml->writeScriptBinding(QLatin1String("attachedType"), enquote(iter.attachedTypeId)); + break; } } } @@ -594,6 +593,12 @@ public: writeMetaContent(meta); + // dump properties from extended metaobjects last + for (auto iter : typeInfo) { + if (iter.extendedObject) + dumpMetaProperties(iter.extendedObject, iter.revision); + } + qml->writeEndObject(); } @@ -642,9 +647,9 @@ private: qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true")); } - void dump(const QMetaProperty &prop, KnownAttributes *knownAttributes = nullptr) + void dump(const QMetaProperty &prop, int metaRevision = -1, KnownAttributes *knownAttributes = nullptr) { - int revision = prop.revision(); + int revision = metaRevision ? metaRevision : prop.revision(); QByteArray propName = prop.name(); if (knownAttributes && knownAttributes->knownProperty(propName, revision)) return; @@ -657,6 +662,20 @@ private: qml->writeEndObject(); } + QSet<QString> dumpMetaProperties(const QMetaObject *meta, int metaRevision = -1, KnownAttributes *knownAttributes = nullptr) + { + QSet<QString> implicitSignals; + for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) { + const QMetaProperty &property = meta->property(index); + dump(property, metaRevision, knownAttributes); + if (knownAttributes) + knownAttributes->knownMethod(QByteArray(property.name()).append("Changed"), + 0, property.revision()); + implicitSignals.insert(QString("%1Changed").arg(QString::fromUtf8(property.name()))); + } + return implicitSignals; + } + void dump(const QMetaMethod &meth, const QSet<QString> &implicitSignals, KnownAttributes *knownAttributes = nullptr) { |