diff options
author | Brett Stottlemyer <bstottle@ford.com> | 2018-04-05 10:57:24 -0400 |
---|---|---|
committer | Brett Stottlemyer <bstottle@ford.com> | 2018-04-05 16:05:08 +0000 |
commit | 67ac9631dbb26394e6b9981494a0aaf6124284a9 (patch) | |
tree | fb24803268fea99892927a8ba9824d451740f614 | |
parent | 80941e2ddabba1df8f90c68465b75929f7816fee (diff) |
Allow recursive packetizing of types
We create a new type (QRO_) to allow storing a subclass as a variant. Thus
when a QObject* property is marshalled, all of the pointed to properties
can be sent as a single variant. This can be decoded into the proper type
on the replica side.
This required giving Node's private a new QRemoteObjectMetaObjectManager,
a class to handle the building of dynamic metatypes.
Change-Id: Ib83b1edb2fd37dd12defb066d4c2b69509985eaa
Reviewed-by: Michael Brasser <michael.brasser@live.com>
-rw-r--r-- | src/remoteobjects/qconnectionfactories_p.h | 2 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectnode.cpp | 182 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectnode_p.h | 16 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectpacket.cpp | 226 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectpacket_p.h | 50 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectreplica.cpp | 39 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectreplica_p.h | 12 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectsource.cpp | 9 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectsource.h | 2 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectsource_p.h | 6 |
10 files changed, 373 insertions, 171 deletions
diff --git a/src/remoteobjects/qconnectionfactories_p.h b/src/remoteobjects/qconnectionfactories_p.h index a211727..b71d006 100644 --- a/src/remoteobjects/qconnectionfactories_p.h +++ b/src/remoteobjects/qconnectionfactories_p.h @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE namespace QtRemoteObjects { static const int dataStreamVersion = QDataStream::Qt_5_6; -static const QLatin1String protocolVersion("QtRO 1.0"); +static const QLatin1String protocolVersion("QtRO 1.1"); } diff --git a/src/remoteobjects/qremoteobjectnode.cpp b/src/remoteobjects/qremoteobjectnode.cpp index b65d920..ae08c93 100644 --- a/src/remoteobjects/qremoteobjectnode.cpp +++ b/src/remoteobjects/qremoteobjectnode.cpp @@ -309,6 +309,107 @@ QRemoteObjectAbstractPersistedStorePrivate::~QRemoteObjectAbstractPersistedStore { } +QRemoteObjectMetaObjectManager::~QRemoteObjectMetaObjectManager() +{ + for (QMetaObject *mo : dynamicTypes) + free(mo); //QMetaObjectBuilder uses malloc, not new +} + +QMetaObject *QRemoteObjectMetaObjectManager::metaObjectForType(const QString &type) +{ + return dynamicTypes.value(type); +} + +QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(QDataStream &in) +{ + QMetaObjectBuilder builder; + builder.setClassName("QRemoteObjectDynamicReplica"); + builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + QString type; + quint32 numEnums = 0; + quint32 numSignals = 0; + quint32 numMethods = 0; + quint32 numProperties = 0; + + in >> type; + + in >> numEnums; + for (quint32 i = 0; i < numEnums; ++i) { + QByteArray name; + in >> name; + auto enumBuilder = builder.addEnumerator(name); + bool isFlag; + in >> isFlag; + enumBuilder.setIsFlag(isFlag); + + QByteArray scopeName; + in >> scopeName; // scope + // TODO uncomment this line after https://bugreports.qt.io/browse/QTBUG-64081 is implemented + //enumBuilder.setScope(scopeName); + + int keyCount; + in >> keyCount; + for (int k = 0; k < keyCount; ++k) { + QByteArray key; + int value; + in >> key; + in >> value; + enumBuilder.addKey(key, value); + } + } + + int curIndex = 0; + + in >> numSignals; + for (quint32 i = 0; i < numSignals; ++i) { + QByteArray signature; + QList<QByteArray> paramNames; + in >> signature; + in >> paramNames; + ++curIndex; + auto mmb = builder.addSignal(signature); + mmb.setParameterNames(paramNames); + } + + in >> numMethods; + for (quint32 i = 0; i < numMethods; ++i) { + QByteArray signature, returnType; + QList<QByteArray> paramNames; + in >> signature; + in >> returnType; + in >> paramNames; + ++curIndex; + const bool isVoid = returnType.isEmpty() || returnType == QByteArrayLiteral("void"); + QMetaMethodBuilder mmb; + if (isVoid) + mmb = builder.addMethod(signature); + else + mmb = builder.addMethod(signature, QByteArrayLiteral("QRemoteObjectPendingCall")); + mmb.setParameterNames(paramNames); + } + + in >> numProperties; + + for (quint32 i = 0; i < numProperties; ++i) { + QByteArray name; + QByteArray typeName; + QByteArray signalName; + in >> name; + in >> typeName; + in >> signalName; + if (signalName.isEmpty()) + builder.addProperty(name, typeName); + else + builder.addProperty(name, typeName, builder.indexOfSignal(signalName)); + } + + auto meta = builder.toMetaObject(); + dynamicTypes.insert(type, meta); + return meta; +} + void QRemoteObjectNodePrivate::connectReplica(QObject *object, QRemoteObjectReplica *instance) { int nConnections = 0; @@ -623,12 +724,13 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj) } case InitPacket: { - qROPrivDebug() << "InitObject-->" << rxName << this; + qROPrivDebug() << "InitPacket-->" << rxName << this; QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef()); //Use m_rxArgs (a QVariantList to hold the properties QVariantList) deserializeInitPacket(connection->stream(), rxArgs); if (rep) { + handlePointerToQObjectProperties(rep.data(), rxArgs); rep->initialize(rxArgs); } else { //replica has been deleted, remove from list replicas.remove(rxName); @@ -637,16 +739,15 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj) } case InitDynamicPacket: { - qROPrivDebug() << "InitObject-->" << rxName << this; - QMetaObjectBuilder builder; - builder.setClassName("QRemoteObjectDynamicReplica"); - builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject); - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - deserializeInitDynamicPacket(connection->stream(), builder, rxArgs); + qROPrivDebug() << "InitDynamicPacket-->" << rxName << this; + const QMetaObject *meta = dynamicTypeManager.addDynamicType(connection->stream()); + deserializeInitPacket(connection->stream(), rxArgs); QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef()); if (rep) { - rep->initializeMetaObject(builder, rxArgs); + rep->setDynamicMetaObject(meta); + handlePointerToQObjectProperties(rep.data(), rxArgs); + rep->setDynamicProperties(rxArgs); } else { //replica has been deleted, remove from list replicas.remove(rxName); } @@ -674,8 +775,18 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj) deserializePropertyChangePacket(connection->stream(), propertyIndex, rxValue); QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef()); if (rep) { - const QMetaProperty property = rep->m_metaObject->property(propertyIndex + rep->m_metaObject->propertyOffset()); - rep->setProperty(propertyIndex, deserializedProperty(rxValue, property)); + QConnectedReplicaImplementation *connectedRep = nullptr; + if (!rep->isShortCircuit()) { + connectedRep = static_cast<QConnectedReplicaImplementation *>(rep.data()); + if (!connectedRep->childIndices().contains(propertyIndex)) + connectedRep = nullptr; //connectedRep will be a valid pointer only if propertyIndex is a child index + } + if (connectedRep) + rep->setProperty(propertyIndex, handlePointerToQObjectProperty(connectedRep, propertyIndex, rxValue)); + else { + const QMetaProperty property = rep->m_metaObject->property(propertyIndex + rep->m_metaObject->propertyOffset()); + rep->setProperty(propertyIndex, deserializedProperty(rxValue, property)); + } } else { //replica has been deleted, remove from list replicas.remove(rxName); } @@ -854,6 +965,7 @@ void QRemoteObjectNodePrivate::initialize() qRegisterMetaType<QRemoteObjectNode::ErrorCode>(); qRegisterMetaType<QAbstractSocket::SocketError>(); //For queued qnx error() qRegisterMetaTypeStreamOperators<QVector<int> >(); + qRegisterMetaTypeStreamOperators<QRemoteObjectPackets::QRO_>(); } bool QRemoteObjectNodePrivate::checkSignatures(const QByteArray &a, const QByteArray &b) @@ -1207,6 +1319,56 @@ void QRemoteObjectNodePrivate::setRegistry(QRemoteObjectRegistry *reg) }); } +QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedReplicaImplementation *rep, int index, const QVariant &property) +{ + Q_Q(QRemoteObjectNode); + using namespace QRemoteObjectPackets; + + QVariant retval; + + Q_ASSERT(property.canConvert<QRO_>()); + QRO_ childInfo = property.value<QRO_>(); + qROPrivDebug() << "QRO_:" << childInfo.name << replicas.contains(childInfo.name) << replicas.keys(); + if (replicas.contains(childInfo.name) && (childInfo.isNull || rep->isInitialized())) { + // Either the source has changed the pointer and we need to update it, or the source pointer is a nullptr + replicas.remove(childInfo.name); + if (childInfo.type == ObjectType::CLASS) + retval = QVariant::fromValue<QRemoteObjectDynamicReplica*>(nullptr); + else + retval = QVariant::fromValue<QAbstractItemModelReplica*>(nullptr); + } else { + if (!replicas.contains(childInfo.name)) { + if (childInfo.type == ObjectType::CLASS) + retval = QVariant::fromValue(q->acquireDynamic(childInfo.name)); + else + retval = QVariant::fromValue(q->acquireModel(childInfo.name)); + } else //We are receiving the initial data for the QObject + retval = rep->getProperty(index); //Use existing value so changed signal isn't emitted + + QSharedPointer<QConnectedReplicaImplementation> childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(childInfo.name).toStrongRef()); + if (childRep->needsDynamicInitialization()) { + if (childInfo.classDefinition.isEmpty()) + childRep->setDynamicMetaObject(dynamicTypeManager.metaObjectForType(childInfo.typeName)); + else { + QDataStream in(childInfo.classDefinition); + childRep->setDynamicMetaObject(dynamicTypeManager.addDynamicType(in)); + } + } + handlePointerToQObjectProperties(childRep.data(), childInfo.parameters); + childRep->setDynamicProperties(childInfo.parameters); + } + + return retval; +} + +void QRemoteObjectNodePrivate::handlePointerToQObjectProperties(QConnectedReplicaImplementation *rep, QVariantList &properties) +{ + using namespace QRemoteObjectPackets; + QVector<int> childIndices = rep->childIndices(); + for (const int index : childIndices) + properties[index] = handlePointerToQObjectProperty(rep, index, properties.at(index)); +} + /*! Blocks until this Node's \l Registry is initialized or \a timeout (in milliseconds) expires. Returns \c true if the \l Registry is successfully diff --git a/src/remoteobjects/qremoteobjectnode_p.h b/src/remoteobjects/qremoteobjectnode_p.h index 839dbb3..6b20117 100644 --- a/src/remoteobjects/qremoteobjectnode_p.h +++ b/src/remoteobjects/qremoteobjectnode_p.h @@ -83,6 +83,19 @@ public: Q_DECLARE_PUBLIC(QRemoteObjectAbstractPersistedStore) }; +class QRemoteObjectMetaObjectManager +{ +public: + QRemoteObjectMetaObjectManager() {} + ~QRemoteObjectMetaObjectManager(); + + QMetaObject *metaObjectForType(const QString &type); + QMetaObject *addDynamicType(QDataStream &in); + +private: + QHash<QString, QMetaObject*> dynamicTypes; +}; + class QRemoteObjectNodePrivate : public QObjectPrivate { public: @@ -101,6 +114,8 @@ public: bool initConnection(const QUrl &address); bool hasInstance(const QString &name); void setRegistry(QRemoteObjectRegistry *); + QVariant handlePointerToQObjectProperty(QConnectedReplicaImplementation *rep, int index, const QVariant &property); + void handlePointerToQObjectProperties(QConnectedReplicaImplementation *rep, QVariantList &properties); void onClientRead(QObject *obj); void onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry); @@ -141,6 +156,7 @@ public: QRemoteObjectAbstractPersistedStore *persistedStore; bool m_handshakeReceived = false; int m_heartbeatInterval = 0; + QRemoteObjectMetaObjectManager dynamicTypeManager; Q_DECLARE_PUBLIC(QRemoteObjectNode) }; diff --git a/src/remoteobjects/qremoteobjectpacket.cpp b/src/remoteobjects/qremoteobjectpacket.cpp index 71330ab..73ef5a0 100644 --- a/src/remoteobjects/qremoteobjectpacket.cpp +++ b/src/remoteobjects/qremoteobjectpacket.cpp @@ -51,13 +51,32 @@ using namespace QtRemoteObjects; namespace QRemoteObjectPackets { -QVariant serializedProperty(const QMetaProperty &property, const QObject *object) +void serializeProperty(DataStreamPacket &ds, const QRemoteObjectSourceBase *source, int internalIndex) { - const QVariant value = property.read(object); + const int propertyIndex = source->m_api->sourcePropertyIndex(internalIndex); + Q_ASSERT (propertyIndex >= 0); + const auto target = source->m_api->isAdapterProperty(internalIndex) ? source->m_adapter : source->m_object; + const auto property = target->metaObject()->property(propertyIndex); + const QVariant value = property.read(target); if (property.isEnumType()) { - return QVariant::fromValue<qint32>(value.toInt()); + ds << QVariant::fromValue<qint32>(value.toInt()); + } else if (QMetaType::typeFlags(property.userType()).testFlag(QMetaType::PointerToQObject)) { + auto const childSource = source->m_children.value(internalIndex); + QRO_ qro(childSource); + if (source->d->isDynamic && qro.type == ObjectType::CLASS && !source->d->sentTypes.contains(qro.typeName)) { + QDataStream classDef(&qro.classDefinition, QIODevice::WriteOnly); + serializeDefinition(classDef, childSource); + source->d->sentTypes.insert(qro.typeName); + } + ds << QVariant::fromValue<QRO_>(qro); + if (qro.isNull) + return; + const int propertyCount = childSource->m_api->propertyCount(); + ds << propertyCount; + for (int internalIndex = 0; internalIndex < propertyCount; ++internalIndex) + serializeProperty(ds, childSource, internalIndex); } else { - return value; // return original + ds << value; // return original } } @@ -78,30 +97,24 @@ void serializeHandshakePacket(DataStreamPacket &ds) ds.finishPacket(); } -void serializeInitPacket(DataStreamPacket &ds, const QRemoteObjectSourceBase *object) +void serializeInitPacket(DataStreamPacket &ds, const QRemoteObjectRootSource *source) { - const SourceApiMap *api = object->m_api; - ds.setId(InitPacket); - ds << api->name(); + ds << source->name(); + serializeProperties(ds, source); + ds.finishPacket(); +} + +void serializeProperties(DataStreamPacket &ds, const QRemoteObjectSourceBase *source) +{ + const SourceApiMap *api = source->m_api; //Now copy the property data const int numProperties = api->propertyCount(); ds << quint32(numProperties); //Number of properties - for (int i = 0; i < numProperties; ++i) { - const int index = api->sourcePropertyIndex(i); - if (index < 0) { - qCWarning(QT_REMOTEOBJECT) << "QInitPacketEncoder - Found invalid property. Index not found:" << i << "Dropping invalid packet."; - ds.size = 0; - return; - } - - const auto target = api->isAdapterProperty(i) ? object->m_adapter : object->m_object; - const auto metaProperty = target->metaObject()->property(index); - ds << serializedProperty(metaProperty, target); - } - ds.finishPacket(); + for (int internalIndex = 0; internalIndex < numProperties; ++internalIndex) + serializeProperty(ds, source, internalIndex); } bool deserializeQVariantList(QDataStream &s, QList<QVariant> &l) @@ -142,16 +155,24 @@ void deserializeInitPacket(QDataStream &in, QVariantList &values) Q_UNUSED(success); } -void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectSourceBase *object) +void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectRootSource *source) { - const SourceApiMap *api = object->m_api; - ds.setId(InitDynamicPacket); - ds << api->name(); + ds << source->name(); + serializeDefinition(ds, source); + serializeProperties(ds, source); + ds.finishPacket(); +} + +void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source) +{ + const SourceApiMap *api = source->m_api; + + ds << source->m_api->typeName(); //Now copy the property data const int numEnums = api->enumCount(); - const auto metaObject = object->m_object->metaObject(); + const auto metaObject = source->m_object->metaObject(); ds << quint32(numEnums); //Number of Enums for (int i = 0; i < numEnums; ++i) { auto enumerator = metaObject->enumerator(api->sourceEnumIndex(i)); @@ -171,11 +192,7 @@ void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectSourceB ds << quint32(numSignals); //Number of signals for (int i = 0; i < numSignals; ++i) { const int index = api->sourceSignalIndex(i); - if (index < 0) { - qCWarning(QT_REMOTEOBJECT) << "QInitDynamicPacketEncoder - Found invalid signal. Index not found:" << i << "Dropping invalid packet."; - ds.size = 0; - return; - } + Q_ASSERT(index >= 0); ds << api->signalSignature(i); ds << api->signalParameterNames(i); } @@ -184,11 +201,7 @@ void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectSourceB ds << quint32(numMethods); //Number of methods for (int i = 0; i < numMethods; ++i) { const int index = api->sourceMethodIndex(i); - if (index < 0) { - qCWarning(QT_REMOTEOBJECT) << "QInitDynamicPacketEncoder - Found invalid method. Index not found:" << i << "Dropping invalid packet."; - ds.size = 0; - return; - } + Q_ASSERT(index >= 0); ds << api->methodSignature(i); ds << api->typeName(i); ds << api->methodParameterNames(i); @@ -198,13 +211,9 @@ void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectSourceB ds << quint32(numProperties); //Number of properties for (int i = 0; i < numProperties; ++i) { const int index = api->sourcePropertyIndex(i); - if (index < 0) { - qCWarning(QT_REMOTEOBJECT) << "QInitDynamicPacketEncoder - Found invalid method. Index not found:" << i << "Dropping invalid packet."; - ds.size = 0; - return; - } + Q_ASSERT(index >= 0); - const auto target = api->isAdapterProperty(i) ? object->m_adapter : object->m_object; + const auto target = api->isAdapterProperty(i) ? source->m_adapter : source->m_object; const auto metaProperty = target->metaObject()->property(index); ds << metaProperty.name(); ds << metaProperty.typeName(); @@ -212,98 +221,6 @@ void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectSourceB ds << QByteArray(); else ds << metaProperty.notifySignal().methodSignature(); - ds << metaProperty.read(target); - } - ds.finishPacket(); -} - -void deserializeInitDynamicPacket(QDataStream &in, QMetaObjectBuilder &builder, QVariantList &values) -{ - quint32 numEnums = 0; - quint32 numSignals = 0; - quint32 numMethods = 0; - quint32 numProperties = 0; - - in >> numEnums; - for (quint32 i = 0; i < numEnums; ++i) { - QByteArray name; - in >> name; - auto enumBuilder = builder.addEnumerator(name); - bool isFlag; - in >> isFlag; - enumBuilder.setIsFlag(isFlag); - - QByteArray scopeName; - in >> scopeName; // scope - // TODO uncomment this line after https://bugreports.qt.io/browse/QTBUG-64081 is implemented - //enumBuilder.setScope(scopeName); - - int keyCount; - in >> keyCount; - for (int k = 0; k < keyCount; ++k) { - QByteArray key; - int value; - in >> key; - in >> value; - enumBuilder.addKey(key, value); - } - } - - int curIndex = 0; - - in >> numSignals; - for (quint32 i = 0; i < numSignals; ++i) { - QByteArray signature; - QList<QByteArray> paramNames; - in >> signature; - in >> paramNames; - ++curIndex; - auto mmb = builder.addSignal(signature); - mmb.setParameterNames(paramNames); - } - - in >> numMethods; - for (quint32 i = 0; i < numMethods; ++i) { - QByteArray signature, returnType; - QList<QByteArray> paramNames; - in >> signature; - in >> returnType; - in >> paramNames; - ++curIndex; - const bool isVoid = returnType.isEmpty() || returnType == QByteArrayLiteral("void"); - QMetaMethodBuilder mmb; - if (isVoid) - mmb = builder.addMethod(signature); - else - mmb = builder.addMethod(signature, QByteArrayLiteral("QRemoteObjectPendingCall")); - mmb.setParameterNames(paramNames); - } - - in >> numProperties; - const quint32 initialListSize = values.size(); - if (static_cast<quint32>(values.size()) < numProperties) - values.reserve(numProperties); - else if (static_cast<quint32>(values.size()) > numProperties) - for (quint32 i = numProperties; i < initialListSize; ++i) - values.removeLast(); - - for (quint32 i = 0; i < numProperties; ++i) { - QByteArray name; - QByteArray typeName; - QByteArray signalName; - in >> name; - in >> typeName; - in >> signalName; - if (signalName.isEmpty()) - builder.addProperty(name, typeName); - else - builder.addProperty(name, typeName, builder.indexOfSignal(signalName)); - QVariant value; - in >> value; - if (i < initialListSize) - values[i] = value; - else - values.append(value); } } @@ -373,12 +290,14 @@ void deserializeInvokeReplyPacket(QDataStream& in, int &ackedSerialId, QVariant in >> value; } -void serializePropertyChangePacket(DataStreamPacket &ds, const QString &name, int index, const QVariant &value) +void serializePropertyChangePacket(QRemoteObjectSourceBase *source, int signalIndex) { + int internalIndex = source->m_api->propertyRawIndexFromSignal(signalIndex); + auto &ds = source->d->m_packet; ds.setId(PropertyChangePacket); - ds << name; - ds << index; - ds << value; + ds << source->name(); + ds << internalIndex; + serializeProperty(ds, source, internalIndex); ds.finishPacket(); } @@ -414,6 +333,37 @@ void serializePongPacket(DataStreamPacket &ds, const QString &name) ds.finishPacket(); } +QRO_::QRO_(QRemoteObjectSourceBase *source) + : name(source->name()) + , typeName(source->m_api->typeName()) + , type(source->m_adapter ? ObjectType::MODEL : ObjectType::CLASS) + , isNull(source->m_object == nullptr) + , classDefinition() +{} + +QDataStream &operator<<(QDataStream &stream, const QRO_ &info) +{ + stream << info.name << info.typeName << (quint8)(info.type) << info.classDefinition << info.isNull; + qCDebug(QT_REMOTEOBJECT) << "Serializing QRO_" << info.name << info.typeName << (info.type == ObjectType::CLASS ? "Class" : "Model") + << (info.isNull ? "nullptr" : "valid pointer"); + //We expect info.parameters to be empty (parameters are pushed to stream individually in serializeProperty) + if (!info.isNull && !info.parameters.isEmpty()) + stream << info.parameters; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QRO_ &info) +{ + quint8 tmpType; + stream >> info.name >> info.typeName >> tmpType >> info.classDefinition >> info.isNull; + info.type = static_cast<ObjectType>(tmpType); + qCDebug(QT_REMOTEOBJECT) << "Deserializing QRO_" << info.name << info.typeName << (info.isNull ? "nullptr" : "valid pointer"); + if (!info.isNull) + stream >> info.parameters; + return stream; +} + } // namespace QRemoteObjectPackets QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectpacket_p.h b/src/remoteobjects/qremoteobjectpacket_p.h index 56d85d6..d8857db 100644 --- a/src/remoteobjects/qremoteobjectpacket_p.h +++ b/src/remoteobjects/qremoteobjectpacket_p.h @@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE class QMetaObjectBuilder; class QRemoteObjectSourceBase; +class QRemoteObjectRootSource; namespace QRemoteObjectPackets { @@ -98,6 +99,34 @@ inline QDataStream& operator>>(QDataStream &stream, ObjectInfo &info) typedef QVector<ObjectInfo> ObjectInfoList; +enum class ObjectType : quint8 { CLASS, MODEL }; + +// Use a short name, as QVariant::save writes the name every time a qvariant of +// this type is serialized +class QRO_ +{ +public: + QRO_() {} + explicit QRO_(QRemoteObjectSourceBase *source); + QString name, typeName; + ObjectType type; + bool isNull; + QByteArray classDefinition; + QVariantList parameters; +}; + +inline QDebug operator<<(QDebug dbg, const QRO_ &info) +{ + dbg.nospace() << "QRO_(name: " << info.name << "typeName: " << info.typeName << "type: " << (info.type == ObjectType::CLASS ? "Class" : "Model") + << ", valid: " << (info.isNull ? "true" : "false") + << ", paremeters: {" << info.parameters <<")"; + return dbg.space(); +} + +QDataStream& operator<<(QDataStream &stream, const QRO_ &info); + +QDataStream& operator>>(QDataStream &stream, QRO_ &info); + void serializeObjectListPacket(DataStreamPacket&, const ObjectInfoList&); void deserializeObjectListPacket(QDataStream&, ObjectInfoList&); @@ -135,18 +164,19 @@ private: Q_DISABLE_COPY(DataStreamPacket) }; -QVariant serializedProperty(const QMetaProperty &property, const QObject *object); +void serializeProperty(DataStreamPacket &, const QRemoteObjectSourceBase *source, int internalIndex); QVariant deserializedProperty(const QVariant &in, const QMetaProperty &property); -void serializeHandshakePacket(DataStreamPacket &ds); -void serializeInitPacket(DataStreamPacket&, const QRemoteObjectSourceBase*); -void deserializeInitPacket(QDataStream&, QVariantList&); +void serializeHandshakePacket(DataStreamPacket &); +void serializeInitPacket(DataStreamPacket &, const QRemoteObjectRootSource*); +void serializeProperties(DataStreamPacket &, const QRemoteObjectSourceBase*); +void deserializeInitPacket(QDataStream &, QVariantList&); -void serializeInitDynamicPacket(DataStreamPacket&, const QRemoteObjectSourceBase*); -void deserializeInitDynamicPacket(QDataStream&, QMetaObjectBuilder&, QVariantList&); +void serializeInitDynamicPacket(DataStreamPacket &, const QRemoteObjectRootSource*); +void serializeDefinition(QDataStream &, const QRemoteObjectSourceBase*); -void serializeAddObjectPacket(DataStreamPacket&, const QString &name, bool isDynamic); -void deserializeAddObjectPacket(QDataStream &ds, bool &isDynamic); +void serializeAddObjectPacket(DataStreamPacket &, const QString &name, bool isDynamic); +void deserializeAddObjectPacket(QDataStream &, bool &isDynamic); void serializeRemoveObjectPacket(DataStreamPacket&, const QString &name); //There is no deserializeRemoveObjectPacket - no parameters other than id and name @@ -158,7 +188,7 @@ void serializeInvokeReplyPacket(DataStreamPacket&, const QString &name, int acke void deserializeInvokeReplyPacket(QDataStream& in, int &ackedSerialId, QVariant &value); //TODO do we need the object name or could we go with an id in backend code, this could be a costly allocation -void serializePropertyChangePacket(DataStreamPacket&, const QString &name, int index, const QVariant &value); +void serializePropertyChangePacket(QRemoteObjectSourceBase *source, int signalIndex); void deserializePropertyChangePacket(QDataStream& in, int &index, QVariant &value); // Heartbeat packets @@ -170,4 +200,6 @@ void serializePongPacket(DataStreamPacket &ds, const QString &name); QT_END_NAMESPACE +Q_DECLARE_METATYPE(QRemoteObjectPackets::QRO_) + #endif diff --git a/src/remoteobjects/qremoteobjectreplica.cpp b/src/remoteobjects/qremoteobjectreplica.cpp index 3d15502..3ec539e 100644 --- a/src/remoteobjects/qremoteobjectreplica.cpp +++ b/src/remoteobjects/qremoteobjectreplica.cpp @@ -89,8 +89,6 @@ QRemoteObjectReplicaImplementation::QRemoteObjectReplicaImplementation(const QSt QRemoteObjectReplicaImplementation::~QRemoteObjectReplicaImplementation() { - if (m_metaObject && qstrcmp(m_metaObject->className(), "QRemoteObjectDynamicReplica") == 0) - free(const_cast<QMetaObject*>(m_metaObject)); } QConnectedReplicaImplementation::QConnectedReplicaImplementation(const QString &name, const QMetaObject *meta, QRemoteObjectNode *node) @@ -120,6 +118,15 @@ QConnectedReplicaImplementation::QConnectedReplicaImplementation(const QString & } } }); + + if (!meta) + return; + + for (int index = m_metaObject->propertyOffset(); index < m_metaObject->propertyCount(); ++index) { + const QMetaProperty property = m_metaObject->property(index); + if (QMetaType::typeFlags(property.userType()).testFlag(QMetaType::PointerToQObject)) + m_childIndices << index - m_metaObject->propertyOffset(); + } } QConnectedReplicaImplementation::~QConnectedReplicaImplementation() @@ -175,6 +182,11 @@ bool QConnectedReplicaImplementation::sendCommand() return true; } +QVector<int> QConnectedReplicaImplementation::childIndices() const +{ + return m_childIndices; +} + void QConnectedReplicaImplementation::initialize(const QVariantList &values) { qCDebug(QT_REMOTEOBJECT) << "initialize()" << m_propertyStorage.size(); @@ -244,18 +256,33 @@ QVariantList QRemoteObjectReplica::retrieveProperties(const QString &repName, co return node()->retrieveProperties(repName, repSig); } -void QRemoteObjectReplicaImplementation::initializeMetaObject(const QMetaObjectBuilder &builder, const QVariantList &values) +void QRemoteObjectReplicaImplementation::setDynamicMetaObject(const QMetaObject *meta) { Q_ASSERT(!m_metaObject); - m_metaObject = builder.toMetaObject(); + m_metaObject = meta; +} + +void QConnectedReplicaImplementation::setDynamicMetaObject(const QMetaObject *meta) +{ + QRemoteObjectReplicaImplementation::setDynamicMetaObject(meta); + + for (int index = m_metaObject->propertyOffset(); index < m_metaObject->propertyCount(); ++index) { + const QMetaProperty property = m_metaObject->property(index); + if (QMetaType::typeFlags(property.userType()).testFlag(QMetaType::PointerToQObject)) + m_childIndices << index - m_metaObject->propertyOffset(); + } +} + +void QRemoteObjectReplicaImplementation::setDynamicProperties(const QVariantList &values) +{ //rely on order of properties; setProperties(values); } -void QConnectedReplicaImplementation::initializeMetaObject(const QMetaObjectBuilder &builder, const QVariantList &values) +void QConnectedReplicaImplementation::setDynamicProperties(const QVariantList &values) { - QRemoteObjectReplicaImplementation::initializeMetaObject(builder, values); + QRemoteObjectReplicaImplementation::setDynamicProperties(values); foreach (QRemoteObjectReplica *obj, m_parentsNeedingConnect) configurePrivate(obj); m_parentsNeedingConnect.clear(); diff --git a/src/remoteobjects/qremoteobjectreplica_p.h b/src/remoteobjects/qremoteobjectreplica_p.h index 31e539e..f1ebcbf 100644 --- a/src/remoteobjects/qremoteobjectreplica_p.h +++ b/src/remoteobjects/qremoteobjectreplica_p.h @@ -137,7 +137,8 @@ public: QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) override = 0; //Dynamic replica functions - virtual void initializeMetaObject(const QMetaObjectBuilder &builder, const QVariantList &values); + virtual void setDynamicMetaObject(const QMetaObject *meta); + virtual void setDynamicProperties(const QVariantList &values); QString m_objectName; const QMetaObject *m_metaObject; @@ -160,9 +161,10 @@ public: const QVariant getProperty(int i) const override; void setProperties(const QVariantList &) override; void setProperty(int i, const QVariant &) override; - bool isShortCircuit() const override { return false; } + bool isShortCircuit() const final { return false; } bool isInitialized() const override; bool waitForSource(int timeout) override; + QVector<int> childIndices() const; void initialize(const QVariantList &values); void configurePrivate(QRemoteObjectReplica *) override; void requestRemoteObjectSource(); @@ -176,9 +178,11 @@ public: void _q_send(QMetaObject::Call call, int index, const QVariantList &args) override; QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList& args) override; - void initializeMetaObject(const QMetaObjectBuilder&, const QVariantList&) override; + void setDynamicMetaObject(const QMetaObject *meta) override; + void setDynamicProperties(const QVariantList&) override; QVector<QRemoteObjectReplica *> m_parentsNeedingConnect; QVariantList m_propertyStorage; + QVector<int> m_childIndices; QPointer<ClientIoDevice> connectionToSource; // pending call data @@ -197,7 +201,7 @@ public: const QVariant getProperty(int i) const override; void setProperties(const QVariantList &) override; void setProperty(int i, const QVariant &) override; - bool isShortCircuit() const override { return true; } + bool isShortCircuit() const final { return true; } void _q_send(QMetaObject::Call call, int index, const QVariantList &args) override; QRemoteObjectPendingCall _q_sendWithReply(QMetaObject::Call call, int index, const QVariantList& args) override; diff --git a/src/remoteobjects/qremoteobjectsource.cpp b/src/remoteobjects/qremoteobjectsource.cpp index aea3365..37a8044 100644 --- a/src/remoteobjects/qremoteobjectsource.cpp +++ b/src/remoteobjects/qremoteobjectsource.cpp @@ -272,7 +272,7 @@ void QRemoteObjectSourceBase::handleMetaCall(int index, QMetaObject::Call call, const auto target = m_api->isAdapterProperty(index) ? m_adapter : m_object; const QMetaProperty mp = target->metaObject()->property(propertyIndex); qCDebug(QT_REMOTEOBJECT) << "Sending Invoke Property" << (m_api->isAdapterSignal(index) ? "via adapter" : "") << rawIndex << propertyIndex << mp.name() << mp.read(target); - serializePropertyChangePacket(d->m_packet, m_api->name(), rawIndex, serializedProperty(mp, target)); + serializePropertyChangePacket(this, index); d->m_packet.baseAddress = d->m_packet.size; propertyIndex = rawIndex; } @@ -290,14 +290,21 @@ void QRemoteObjectSourceBase::handleMetaCall(int index, QMetaObject::Call call, void QRemoteObjectRootSource::addListener(ServerIoDevice *io, bool dynamic) { d->m_listeners.append(io); + d->isDynamic = dynamic; if (dynamic) { + d->sentTypes.clear(); serializeInitDynamicPacket(d->m_packet, this); io->write(d->m_packet.array, d->m_packet.size); } else { serializeInitPacket(d->m_packet, this); io->write(d->m_packet.array, d->m_packet.size); } + + //Setting isDynamic == false will prevent class definitions from being sent if the + //QObject pointer is changed (setting a new subclass pointer). I.e., class definitions + //are only sent by serializeInitDynamicPacket, not propertyChangePackets. + d->isDynamic = false; } int QRemoteObjectRootSource::removeListener(ServerIoDevice *io, bool shouldSendRemove) diff --git a/src/remoteobjects/qremoteobjectsource.h b/src/remoteobjects/qremoteobjectsource.h index eaece69..73fc1ae 100644 --- a/src/remoteobjects/qremoteobjectsource.h +++ b/src/remoteobjects/qremoteobjectsource.h @@ -121,7 +121,7 @@ QByteArray qtro_classinfo_signature(const QMetaObject *metaObject); } -class QRemoteObjectHostBase; +// TODO ModelInfo just needs roles, and no need for SubclassInfo class QAbstractItemModel; struct ModelInfo diff --git a/src/remoteobjects/qremoteobjectsource_p.h b/src/remoteobjects/qremoteobjectsource_p.h index 654a4a2..9aa7070 100644 --- a/src/remoteobjects/qremoteobjectsource_p.h +++ b/src/remoteobjects/qremoteobjectsource_p.h @@ -83,10 +83,14 @@ public: QByteArray m_objectChecksum; QMap<int, QPointer<QRemoteObjectSourceBase>> m_children; struct Private { - Private(QRemoteObjectSourceIo *io) : m_sourceIo(io) {} + Private(QRemoteObjectSourceIo *io) : m_sourceIo(io), isDynamic(false) {} QRemoteObjectSourceIo *m_sourceIo; QVector<ServerIoDevice*> m_listeners; QRemoteObjectPackets::DataStreamPacket m_packet; + + // Types needed during recursively sending a root to a new listener + QSet<QString> sentTypes; + bool isDynamic; }; Private *d; static const int qobjectPropertyOffset; |