diff options
-rw-r--r-- | src/qml/qml/qqml.cpp | 9 | ||||
-rw-r--r-- | src/qml/qml/qqmlprivate.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlproxymetaobject.cpp | 10 | ||||
-rw-r--r-- | src/qml/qml/qqmlproxymetaobject_p.h | 18 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/testtypes.h | 29 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 58 |
6 files changed, 119 insertions, 7 deletions
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 728250dcf2..3c9f930eb9 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -151,6 +151,11 @@ QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc QObject *qmlExtendedObject(QObject *object) { + return QQmlPrivate::qmlExtendedObject(object, 0); +} + +QObject *QQmlPrivate::qmlExtendedObject(QObject *object, int index) +{ if (!object) return nullptr; @@ -161,8 +166,8 @@ QObject *qmlExtendedObject(QObject *object) const int id = d->metaObject->metaCall( object, QMetaObject::CustomCall, - QQmlProxyMetaObject::ExtensionObjectId, &result); - if (id != QQmlProxyMetaObject::ExtensionObjectId) + QQmlProxyMetaObject::extensionObjectId(index), &result); + if (id != QQmlProxyMetaObject::extensionObjectId(index)) return nullptr; return static_cast<QObject *>(result); diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index 817f25c112..9754f9dcf3 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -1087,6 +1087,8 @@ namespace QQmlPrivate }; } + Q_QML_EXPORT QObject *qmlExtendedObject(QObject *, int); + } // namespace QQmlPrivate QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp index e05c63794c..4844ca3720 100644 --- a/src/qml/qml/qqmlproxymetaobject.cpp +++ b/src/qml/qml/qqmlproxymetaobject.cpp @@ -144,11 +144,15 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void break; } - case QMetaObject::CustomCall: - if (id != ExtensionObjectId) + case QMetaObject::CustomCall: { + if ((id & ~MaxExtensionCount) != ExtensionObjectId) break; - a[0] = getProxy(0); + int index = id & MaxExtensionCount; + if (qsizetype(index) >= metaObjects->size()) + break; + a[0] = getProxy(index); return id; + } default: break; } diff --git a/src/qml/qml/qqmlproxymetaobject_p.h b/src/qml/qml/qqmlproxymetaobject_p.h index a58818f22a..965d86c70f 100644 --- a/src/qml/qml/qqmlproxymetaobject_p.h +++ b/src/qml/qml/qqmlproxymetaobject_p.h @@ -65,8 +65,6 @@ QT_BEGIN_NAMESPACE class QQmlProxyMetaObject : public QDynamicMetaObjectData { public: - enum { ExtensionObjectId = std::numeric_limits<int>::min() }; - struct ProxyData { typedef QObject *(*CreateFunc)(QObject *); QMetaObject *metaObject; @@ -78,6 +76,13 @@ public: QQmlProxyMetaObject(QObject *, QList<ProxyData> *); ~QQmlProxyMetaObject(); + static constexpr int extensionObjectId(int id) noexcept + { + Q_ASSERT(id >= 0); + Q_ASSERT(id <= MaxExtensionCount); // MaxExtensionCount is a valid index + return ExtensionObjectId | id; + } + protected: int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) override; QMetaObject *toDynamicMetaObject(QObject *) override; @@ -91,6 +96,15 @@ private: QDynamicMetaObjectData *parent; QMetaObject *metaObject; QObject *object; + + // ExtensionObjectId acts as a flag for whether we should interpret a + // QMetaObject::CustomCall as a call to fetch the extension object (see + // QQmlProxyMetaObject::metaCall()). MaxExtensionCount is a limit on how + // many extensions we can query via such mechanism + enum : int { + MaxExtensionCount = 127, // magic number so that low bits are all '1' + ExtensionObjectId = ~MaxExtensionCount, + }; }; QT_END_NAMESPACE diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 8efccbc0fb..ca42ae02bc 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1851,6 +1851,35 @@ public: ExtendedInParentByIndirect(QObject *parent = nullptr) : ExtendedByIndirect(parent) { } }; +class MultiExtensionThreeExtensions : public MultiExtension +{ + Q_OBJECT + QML_ELEMENT + QML_EXTENDED(Extension) +public: + MultiExtensionThreeExtensions(QObject *parent = nullptr) : MultiExtension(parent) { } +}; + +class MultiExtensionWithoutExtension : public MultiExtension +{ + Q_OBJECT + QML_ANONYMOUS +public: + MultiExtensionWithoutExtension(QObject *parent = nullptr) : MultiExtension(parent) { } +}; + +class MultiExtensionWithExtensionInBaseBase : public MultiExtensionWithoutExtension +{ + Q_OBJECT + QML_ELEMENT + QML_EXTENDED(Extension) +public: + MultiExtensionWithExtensionInBaseBase(QObject *parent = nullptr) + : MultiExtensionWithoutExtension(parent) + { + } +}; + class StringSignaler : public QObject { Q_OBJECT diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 1e3f06a9e9..bfcd146b10 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -365,6 +365,7 @@ private slots: void qtbug_86482(); void multiExtension(); + void multiExtensionExtra(); void multiExtensionIndirect(); void extensionSpecial(); void invalidInlineComponent(); @@ -6395,6 +6396,63 @@ void tst_qqmllanguage::multiExtension() QCOMPARE(o->property("d").toInt(), 22); QCOMPARE(o->property("f").toInt(), 31); QCOMPARE(o->property("g").toInt(), 44); // NB: taken from the type, not from the extension! + + QObject *extension = qmlExtendedObject(o.get()); + QVERIFY(extension != nullptr); + QVERIFY(qobject_cast<ExtensionB *>(extension) != nullptr); + + QCOMPARE(QQmlPrivate::qmlExtendedObject(o.get(), 0), extension); + QObject *baseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 1); + QVERIFY(baseTypeExtension); + QVERIFY(qobject_cast<ExtensionA *>(baseTypeExtension) != nullptr); +} + +void tst_qqmllanguage::multiExtensionExtra() +{ + QQmlEngine engine; + { + QQmlComponent c(&engine); + c.setData("import StaticTest\nMultiExtensionThreeExtensions {}", QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QObject *extension = qmlExtendedObject(o.get()); + QVERIFY(extension != nullptr); + QVERIFY(qobject_cast<Extension *>(extension) != nullptr); + + QCOMPARE(QQmlPrivate::qmlExtendedObject(o.get(), 0), extension); + QObject *baseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 1); + QVERIFY(baseTypeExtension); + QVERIFY(qobject_cast<ExtensionB *>(baseTypeExtension) != nullptr); + QObject *baseBaseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 2); + QVERIFY(baseBaseTypeExtension); + QVERIFY(qobject_cast<ExtensionA *>(baseBaseTypeExtension) != nullptr); + } + + { + QQmlComponent c(&engine); + c.setData("import StaticTest\nMultiExtensionWithExtensionInBaseBase {}", QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QObject *extension = qmlExtendedObject(o.get()); + QVERIFY(extension != nullptr); + QVERIFY(qobject_cast<Extension *>(extension) != nullptr); + + QCOMPARE(QQmlPrivate::qmlExtendedObject(o.get(), 0), extension); + + QObject *pseudoExtension = QQmlPrivate::qmlExtendedObject(o.get(), 1); + QVERIFY(pseudoExtension); + QVERIFY(qobject_cast<ExtensionB *>(pseudoExtension) != nullptr); + + QObject *baseBaseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 2); + QVERIFY(baseBaseTypeExtension); + QVERIFY(qobject_cast<ExtensionB *>(baseBaseTypeExtension) != nullptr); + + QObject *baseBaseBaseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 3); + QVERIFY(baseBaseBaseTypeExtension); + QVERIFY(qobject_cast<ExtensionA *>(baseBaseBaseTypeExtension) != nullptr); + } } void tst_qqmllanguage::multiExtensionIndirect() |