From 05fd01c14a7a4b26f366704fb0b0a4eddaceccf4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Jan 2015 11:51:37 +0100 Subject: [PATCH] Fix property access and method invocation on value types that use inheritance For gadgets/value types we use moc's static_metacall, which doesn't call the parent class implementation. Therefore before placing a static metacall we must resolve the indicies and find the right meta-object. Change-Id: I258e3d9ecfc704498c68772dc42b16134a3bfd83 Reviewed-by: Olivier Goffart Reviewed-by: Alex Blasche --- src/qml/qml/qqmlpropertycache.cpp | 40 +++++++++++++++- src/qml/qml/qqmlpropertycache_p.h | 4 ++ src/qml/qml/qqmlvaluetype.cpp | 4 +- src/qml/qml/qqmlvaluetypewrapper.cpp | 10 ++-- .../qml/qqmlvaluetypes/data/customvaluetype.qml | 3 ++ .../auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp | 54 ++++++++++++++++++++++ 6 files changed, 109 insertions(+), 6 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index b5bc29a..182e6a7 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1510,6 +1510,39 @@ bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject return false; } +void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index) +{ + int offset; + + switch (type) { + case QMetaObject::ReadProperty: + case QMetaObject::WriteProperty: + case QMetaObject::ResetProperty: + case QMetaObject::QueryPropertyDesignable: + case QMetaObject::QueryPropertyEditable: + case QMetaObject::QueryPropertyScriptable: + case QMetaObject::QueryPropertyStored: + case QMetaObject::QueryPropertyUser: + offset = (*metaObject)->propertyOffset(); + while (*index < offset) { + *metaObject = (*metaObject)->superClass(); + offset = (*metaObject)->propertyOffset(); + } + break; + case QMetaObject::InvokeMetaMethod: + offset = (*metaObject)->methodOffset(); + while (*index < offset) { + *metaObject = (*metaObject)->superClass(); + offset = (*metaObject)->methodOffset(); + } + break; + default: + Q_UNIMPLEMENTED(); + } + + *index -= offset; +} + QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const { if (_m.isNull()) return 0; @@ -1652,8 +1685,11 @@ void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv { if (ptr.isT1()) QMetaObject::metacall(ptr.asT1(), type, index, argv); - else - _m.asT1()->metaObject()->d.static_metacall(reinterpret_cast(ptr.asT2()), type, index, argv); + else { + const QMetaObject *metaObject = _m.asT1()->metaObject(); + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); + metaObject->d.static_metacall(reinterpret_cast(ptr.asT2()), type, index, argv); + } } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 77bbd3d..033ff1d 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -432,6 +432,10 @@ public: static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); + // static_metacall (on Gadgets) doesn't call the base implementation and therefore + // we need a helper to find the correct meta object and property/method index. + static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index); + protected: QBiPointer _m; }; diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index e901a9c..7f0e3a3 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -231,7 +231,9 @@ void QQmlValueType::objectDestroyed(QObject *) int QQmlValueType::metaCall(QObject *, QMetaObject::Call type, int _id, void **argv) { - d.static_metacall(reinterpret_cast(gadgetPtr), type, _id, argv); + const QMetaObject *mo = _metaObject; + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &mo, &_id); + mo->d.static_metacall(reinterpret_cast(gadgetPtr), type, _id, argv); return _id; } diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 332cfef..85a391f 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -307,11 +307,15 @@ ReturnedValue QQmlValueTypeWrapper::get(Managed *m, String *name, bool *hasPrope if (result->propType == metatype) { \ cpptype v; \ void *args[] = { &v, 0 }; \ - metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, result->coreIndex, args); \ + metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, index, args); \ return QV4::Encode(constructor(v)); \ } const QMetaObject *metaObject = r->d()->propertyCache->metaObject(); + + int index = result->coreIndex; + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); + void *gadget = r->d()->gadget(); // These four types are the most common used by the value type wrappers @@ -322,9 +326,9 @@ ReturnedValue QQmlValueTypeWrapper::get(Managed *m, String *name, bool *hasPrope QVariant v(result->propType, (void *)0); void *args[] = { v.data(), 0 }; - metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, result->coreIndex, args); + metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, index, args); return QV8Engine::fromVariant(v4, v); -#undef VALUE_TYPE_ACCESSOR +#undef VALUE_TYPE_LOAD } void QQmlValueTypeWrapper::put(Managed *m, String *name, const ValueRef value) diff --git a/tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml b/tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml index 7ae7347..380eb55 100644 --- a/tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml +++ b/tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml @@ -5,4 +5,7 @@ TypeWithCustomValueType { desk { monitorCount: 3 } + derivedGadget { + baseProperty: 42 + } } diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index f07a34b..45cbbeb 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -90,6 +90,7 @@ private slots: void groupedInterceptors_data(); void customValueType(); void customValueTypeInQml(); + void gadgetInheritance(); private: QQmlEngine engine; @@ -1484,13 +1485,48 @@ void tst_qqmlvaluetypes::customValueType() QCOMPARE(cppOffice.desk().monitorCount, 2); } +struct BaseGadget +{ + Q_GADGET + Q_PROPERTY(int baseProperty READ baseProperty WRITE setBaseProperty) +public: + BaseGadget() : m_baseProperty(0) {} + + int baseProperty() const { return m_baseProperty; } + void setBaseProperty(int value) { m_baseProperty = value; } + int m_baseProperty; + + Q_INVOKABLE void functionInBaseGadget(int value) { m_baseProperty = value; } +}; + +Q_DECLARE_METATYPE(BaseGadget) + +struct DerivedGadget : public BaseGadget +{ + Q_GADGET + Q_PROPERTY(int derivedProperty READ derivedProperty WRITE setDerivedProperty) +public: + DerivedGadget() : m_derivedProperty(0) {} + + int derivedProperty() const { return m_derivedProperty; } + void setDerivedProperty(int value) { m_derivedProperty = value; } + int m_derivedProperty; + + Q_INVOKABLE void functionInDerivedGadget(int value) { m_derivedProperty = value; } +}; + class TypeWithCustomValueType : public QObject { Q_OBJECT Q_PROPERTY(MyDesk desk MEMBER m_desk) + Q_PROPERTY(DerivedGadget derivedGadget READ derivedGadget WRITE setDerivedGadget) public: MyDesk m_desk; + + DerivedGadget derivedGadget() const { return m_derivedGadget; } + void setDerivedGadget(const DerivedGadget &value) { m_derivedGadget = value; } + DerivedGadget m_derivedGadget; }; void tst_qqmlvaluetypes::customValueTypeInQml() @@ -1503,6 +1539,24 @@ void tst_qqmlvaluetypes::customValueTypeInQml() TypeWithCustomValueType *t = qobject_cast(object.data()); Q_ASSERT(t); QCOMPARE(t->m_desk.monitorCount, 3); + QCOMPARE(t->m_derivedGadget.baseProperty(), 42); +} + +Q_DECLARE_METATYPE(DerivedGadget) + +void tst_qqmlvaluetypes::gadgetInheritance() +{ + QJSEngine engine; + + QJSValue value = engine.toScriptValue(DerivedGadget()); + + QCOMPARE(value.property("baseProperty").toInt(), 0); + value.setProperty("baseProperty", 10); + QCOMPARE(value.property("baseProperty").toInt(), 10); + + QJSValue method = value.property("functionInBaseGadget"); + method.call(QJSValueList() << QJSValue(42)); + QCOMPARE(value.property("baseProperty").toInt(), 42); } QTEST_MAIN(tst_qqmlvaluetypes) -- 2.7.4