From 2a624649afa3c214f6952ef98361ca541b1ce1ae Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 23 Oct 2012 14:39:18 +0200 Subject: [PATCH] Support for QQmlVMEMetaObject and QmlPropertyCache We have to derive NodeInstanceMetaObject from QQmlVMEMetaObject to support e. g. alias properties. The QmlEngine uses QQmlVMEMetaObject to resolve alias properties. Also we have to invalidate/recreate the QmlPropertyCache if we add dynamic properties. Change-Id: Id52dd8b380eadeb6f74b5171168f2f70c16be118 Reviewed-by: Thomas Hartmann --- .../instances/nodeinstancemetaobject.cpp | 277 ++++++++++++++++-- .../instances/nodeinstancemetaobject.h | 52 +++- .../instances/objectnodeinstance.cpp | 9 +- 3 files changed, 301 insertions(+), 37 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.cpp index a3d7483fa45..2cd639b700e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.cpp @@ -36,23 +36,167 @@ #include #include +#include +#include + namespace QmlDesigner { namespace Internal { -NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstance::Pointer &nodeInstance, QQmlEngine *engine) - : QQmlOpenMetaObject(nodeInstance->object(), new QQmlOpenMetaObjectType(nodeInstance->object()->metaObject(), engine), true), - m_nodeInstance(nodeInstance), - m_context(nodeInstance->isRootNodeInstance() ? nodeInstance->context() : 0) +static QHash nodeInstanceMetaObjectList; + +struct MetaPropertyData { + inline QPair &getDataRef(int idx) { + while (m_data.count() <= idx) + m_data << QPair(QVariant(), false); + return m_data[idx]; + } + + inline QVariant &getData(int idx) { + QPair &prop = getDataRef(idx); + if (!prop.second) { + prop.first = QVariant(); + prop.second = true; + } + return prop.first; + } + + inline bool hasData(int idx) const { + if (idx >= m_data.count()) + return false; + return m_data[idx].second; + } + + inline int count() { return m_data.count(); } + + QList > m_data; +}; + +static bool constructedMetaData(const QQmlVMEMetaData* data) { - setCached(false); + return data->varPropertyCount == 0 + && data->propertyCount == 0 + && data->aliasCount == 0 + && data->signalCount == 0 + && data->methodCount == 0; +} + +static QQmlVMEMetaData* fakeMetaData() +{ + QQmlVMEMetaData* data = new QQmlVMEMetaData; + data->varPropertyCount = 0; + data->propertyCount = 0; + data->aliasCount = 0; + data->signalCount = 0; + data->methodCount = 0; + + return data; +} + +static const QQmlVMEMetaData* vMEMetaDataForObject(QObject *object) +{ + QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object); + if (metaObject) + return metaObject->metaData; + + return fakeMetaData(); +} + +static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine) +{ + QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object); + if (metaObject) + return metaObject->cache; + + return QQmlEnginePrivate::get(engine)->cache(object); +} + +static QAbstractDynamicMetaObject *abstractDynamicMetaObject(QObject *object) +{ + QObjectPrivate *op = QObjectPrivate::get(object); + if (op->metaObject) + return static_cast(op->metaObject); + return const_cast(static_cast(object->metaObject())); +} + + +NodeInstanceMetaObject *NodeInstanceMetaObject::createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine) +{ + //Avoid setting up multiple NodeInstanceMetaObjects on the same QObject + QObjectPrivate *op = QObjectPrivate::get(nodeInstance->object()); + QDynamicMetaObjectData *parent = op->metaObject; + if (nodeInstanceMetaObjectList.contains(parent)) + return static_cast(parent); + + return new NodeInstanceMetaObject(nodeInstance, engine); +} + +NodeInstanceMetaObject *NodeInstanceMetaObject::createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine) +{ + //Avoid setting up multiple NodeInstanceMetaObjects on the same QObject + QObjectPrivate *op = QObjectPrivate::get(nodeInstance->object()); + QDynamicMetaObjectData *parent = op->metaObject; + if (nodeInstanceMetaObjectList.contains(parent)) + return static_cast(parent); + + return new NodeInstanceMetaObject(nodeInstance, object, prefix, engine); +} + +void NodeInstanceMetaObject::init(QObject *object, QQmlEngine *engine) +{ + //Creating QQmlOpenMetaObjectType + m_type = new QQmlOpenMetaObjectType(metaObjectParent(), engine); + m_type->addref(); + //Assigning type to this + copyTypeMetaObject(); + + //Assign this to object + QObjectPrivate *op = QObjectPrivate::get(object); + op->metaObject = this; + + //create cache + cache = m_cache = QQmlEnginePrivate::get(engine)->cache(this); + + //If our parent is not a VMEMetaObject we just se the flag to false again + if (constructedMetaData(metaData)) + QQmlData::get(object)->hasVMEMetaObject = false; + + nodeInstanceMetaObjectList.insert(this, true); +} + +NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstance::Pointer &nodeInstance, QQmlEngine *engine) + : QQmlVMEMetaObject(nodeInstance->object(), cacheForObject(nodeInstance->object(), engine), vMEMetaDataForObject(nodeInstance->object())), + m_nodeInstance(nodeInstance), + m_context(engine->contextForObject(nodeInstance->object())), + m_data(new MetaPropertyData), + m_cache(0) +{ + init(nodeInstance->object(), engine); + + QQmlData *ddata = QQmlData::get(nodeInstance->object(), false); + + //Assign cache to object + if (ddata && ddata->propertyCache) { + ddata->propertyCache = m_cache; + } } NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine) - : QQmlOpenMetaObject(object, new QQmlOpenMetaObjectType(object->metaObject(), engine), true), - m_nodeInstance(nodeInstance), - m_prefix(prefix) + : QQmlVMEMetaObject(object, cacheForObject(object, engine), vMEMetaDataForObject(object)), + m_nodeInstance(nodeInstance), + m_context(engine->contextForObject(object)), + m_prefix(prefix), + + m_data(new MetaPropertyData), + m_cache(0) { - setCached(false); + init(object, engine); +} + +NodeInstanceMetaObject::~NodeInstanceMetaObject() +{ + m_type->release(); + + nodeInstanceMetaObjectList.remove(this); } void NodeInstanceMetaObject::createNewProperty(const QString &name) @@ -60,55 +204,114 @@ void NodeInstanceMetaObject::createNewProperty(const QString &name) int id = createProperty(name.toLatin1(), 0); setValue(id, QVariant()); Q_ASSERT(id >= 0); - Q_UNUSED(id) + Q_UNUSED(id); + + + //Updating cache + QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this); + + QQmlProperty property(myObject(), name, m_context); + Q_ASSERT(property.isValid()); +} + +int NodeInstanceMetaObject::createProperty(const char *name, const char *) +{ + int id = m_type->createProperty(name); + copyTypeMetaObject(); + return id; +} + +void NodeInstanceMetaObject::setValue(int id, const QVariant &value) +{ + QPair &prop = m_data->getDataRef(id); + prop.first = propertyWriteValue(id, value); + prop.second = true; + QMetaObject::activate(myObject(), id + m_type->signalOffset(), 0); +} + +QVariant NodeInstanceMetaObject::propertyWriteValue(int, const QVariant &value) +{ + return value; +} + +int NodeInstanceMetaObject::openMetaCall(QMetaObject::Call call, int id, void **a) +{ + if ((call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty) + && id >= m_type->propertyOffset()) { + int propId = id - m_type->propertyOffset(); + if (call == QMetaObject::ReadProperty) { + //propertyRead(propId); + *reinterpret_cast(a[0]) = m_data->getData(propId); + } else if (call == QMetaObject::WriteProperty) { + if (propId <= m_data->count() || m_data->m_data[propId].first != *reinterpret_cast(a[0])) { + //propertyWrite(propId); + QPair &prop = m_data->getDataRef(propId); + prop.first = propertyWriteValue(propId, *reinterpret_cast(a[0])); + prop.second = true; + //propertyWritten(propId); + activate(myObject(), m_type->signalOffset() + propId, 0); + } + } + return -1; + } else { + QAbstractDynamicMetaObject *directParent = parent(); + if (directParent) + return directParent->metaCall(call, id, a); + else + return myObject()->qt_metacall(call, id, a); + } } int NodeInstanceMetaObject::metaCall(QMetaObject::Call call, int id, void **a) { int metaCallReturnValue = -1; + const QMetaProperty propertyById = QQmlVMEMetaObject::property(id); + if (call == QMetaObject::WriteProperty - && property(id).userType() == QMetaType::QVariant - && reinterpret_cast(a[0])->type() == QVariant::Double - && qIsNaN(reinterpret_cast(a[0])->toDouble())) { + && propertyById.userType() == QMetaType::QVariant + && reinterpret_cast(a[0])->type() == QVariant::Double + && qIsNaN(reinterpret_cast(a[0])->toDouble())) { return -1; } if (call == QMetaObject::WriteProperty - && property(id).userType() == QMetaType::Double - && qIsNaN(*reinterpret_cast(a[0]))) { + && propertyById.userType() == QMetaType::Double + && qIsNaN(*reinterpret_cast(a[0]))) { return -1; } if (call == QMetaObject::WriteProperty - && property(id).userType() == QMetaType::Float - && qIsNaN(*reinterpret_cast(a[0]))) { + && propertyById.userType() == QMetaType::Float + && qIsNaN(*reinterpret_cast(a[0]))) { return -1; } QVariant oldValue; - if (call == QMetaObject::WriteProperty && !property(id).hasNotifySignal()) + if (call == QMetaObject::WriteProperty && !propertyById.hasNotifySignal()) { - oldValue = property(id).read(object()); + oldValue = propertyById.read(myObject()); } ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef(); - if (parent() && id < parent()->propertyOffset()) { - metaCallReturnValue = parent()->metaCall(call, id, a); + QAbstractDynamicMetaObject *directParent = parent(); + if (directParent && id < directParent->propertyOffset()) { + metaCallReturnValue = directParent->metaCall(call, id, a); } else { - metaCallReturnValue = QQmlOpenMetaObject::metaCall(call, id, a); + openMetaCall(call, id, a); } if ((call == QMetaObject::WriteProperty || call == QMetaObject::ReadProperty) && metaCallReturnValue < 0) { if (objectNodeInstance && objectNodeInstance->nodeInstanceServer() && objectNodeInstance->nodeInstanceServer()->dummyContextObject() - && !(objectNodeInstance && !objectNodeInstance->isRootNodeInstance() && property(id).name() == QLatin1String("parent"))) { + && !(objectNodeInstance && !objectNodeInstance->isRootNodeInstance() + && property(id).name() == QLatin1String("parent"))) { QObject *contextDummyObject = objectNodeInstance->nodeInstanceServer()->dummyContextObject(); - int properyIndex = contextDummyObject->metaObject()->indexOfProperty(property(id).name()); + int properyIndex = contextDummyObject->metaObject()->indexOfProperty(propertyById.name()); if (properyIndex >= 0) metaCallReturnValue = contextDummyObject->qt_metacall(call, properyIndex, a); } @@ -116,8 +319,8 @@ int NodeInstanceMetaObject::metaCall(QMetaObject::Call call, int id, void **a) if (metaCallReturnValue >= 0 && call == QMetaObject::WriteProperty - && !property(id).hasNotifySignal() - && oldValue != property(id).read(object())) + && !propertyById.hasNotifySignal() + && oldValue != propertyById.read(myObject())) notifyPropertyChange(id); return metaCallReturnValue; @@ -126,15 +329,31 @@ int NodeInstanceMetaObject::metaCall(QMetaObject::Call call, int id, void **a) void NodeInstanceMetaObject::notifyPropertyChange(int id) { ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef(); + const QMetaProperty propertyById = property(id); if (objectNodeInstance && objectNodeInstance->nodeInstanceServer()) { - if (id < type()->propertyOffset()) { - objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + property(id).name()); + if (id < propertyOffset()) { + objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + propertyById.name()); } else { - objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + name(id - type()->propertyOffset())); + objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + name(id - propertyOffset())); } } } +int NodeInstanceMetaObject::count() const +{ + return m_type->propertyCount(); +} + +QByteArray NodeInstanceMetaObject::name(int idx) const +{ + return m_type->propertyName(idx); +} + +void NodeInstanceMetaObject::copyTypeMetaObject() +{ + *static_cast(this) = *m_type->metaObject(); +} + } // namespace Internal } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.h index db9a92fd355..5fa795b4553 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.h @@ -32,7 +32,9 @@ #define NODEINSTANCEMETAOBJECT_H #include +#include #include +#include namespace QmlDesigner { namespace Internal { @@ -41,21 +43,63 @@ class ObjectNodeInstance; typedef QSharedPointer ObjectNodeInstancePointer; typedef QWeakPointer ObjectNodeInstanceWeakPointer; -class NodeInstanceMetaObject : public QQmlOpenMetaObject +struct MetaPropertyData; + +class NodeInstanceMetaObject : public QQmlVMEMetaObject { public: - NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine); - NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine); + static NodeInstanceMetaObject *createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine); + static NodeInstanceMetaObject *createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine); + ~NodeInstanceMetaObject(); void createNewProperty(const QString &name); protected: + NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine); + NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine); + + int openMetaCall(QMetaObject::Call _c, int _id, void **_a); int metaCall(QMetaObject::Call _c, int _id, void **_a); void notifyPropertyChange(int id); + void setValue(int id, const QVariant &value); + int createProperty(const char *, const char *); + QVariant propertyWriteValue(int, const QVariant &); + + QObject *myObject() const { return QQmlVMEMetaObject::object; } + QAbstractDynamicMetaObject *parent() const { return const_cast(dynamicMetaObjectParent()); } + + const QAbstractDynamicMetaObject *dynamicMetaObjectParent() const + { + if (QQmlVMEMetaObject::parent.isT1()) + return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object); + else + return 0; + } + + const QMetaObject *metaObjectParent() const + { + if (QQmlVMEMetaObject::parent.isT1()) + return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object); + + return QQmlVMEMetaObject::parent.asT2(); + } + + int propertyOffset() const { return cache->propertyOffset(); } + + int count() const; + QByteArray name(int) const; + + void copyTypeMetaObject(); private: + void init(QObject *, QQmlEngine *engine); + ObjectNodeInstanceWeakPointer m_nodeInstance; QString m_prefix; - QPointer m_context; + QPointer m_context; + QQmlOpenMetaObjectType *m_type; + QScopedPointer m_data; + //QAbstractDynamicMetaObject *m_parent; + QQmlPropertyCache *m_cache; }; } // namespace Internal diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp index 1cfe4be8119..bb1ff8c0524 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp @@ -130,14 +130,15 @@ static bool hasPropertiesWitoutNotifications(const QMetaObject *metaObject) void ObjectNodeInstance::initializePropertyWatcher(const ObjectNodeInstance::Pointer &objectNodeInstance) { const QMetaObject *metaObject = objectNodeInstance->object()->metaObject(); - m_metaObject = new NodeInstanceMetaObject(objectNodeInstance, nodeInstanceServer()->engine()); - QQmlEnginePrivate::get(engine())->cache(m_metaObject); + m_metaObject = NodeInstanceMetaObject::createNodeInstanceMetaObject(objectNodeInstance, nodeInstanceServer()->engine()); for (int propertyIndex = QObject::staticMetaObject.propertyCount(); propertyIndex < metaObject->propertyCount(); propertyIndex++) { if (QQmlMetaType::isQObject(metaObject->property(propertyIndex).userType())) { QObject *propertyObject = QQmlMetaType::toQObject(metaObject->property(propertyIndex).read(objectNodeInstance->object())); if (propertyObject && hasPropertiesWitoutNotifications(propertyObject->metaObject())) { - QMetaObject *childMetaObject = new NodeInstanceMetaObject(objectNodeInstance, propertyObject, metaObject->property(propertyIndex).name(), nodeInstanceServer()->engine()); - QQmlEnginePrivate::get(engine())->cache(childMetaObject); + NodeInstanceMetaObject::createNodeInstanceMetaObject(objectNodeInstance, + propertyObject, + metaObject->property(propertyIndex).name(), + nodeInstanceServer()->engine()); } } }