summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrett Stottlemyer <bstottle@ford.com>2018-04-05 10:57:24 -0400
committerBrett Stottlemyer <bstottle@ford.com>2018-04-05 16:05:08 +0000
commit67ac9631dbb26394e6b9981494a0aaf6124284a9 (patch)
treefb24803268fea99892927a8ba9824d451740f614
parent80941e2ddabba1df8f90c68465b75929f7816fee (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.h2
-rw-r--r--src/remoteobjects/qremoteobjectnode.cpp182
-rw-r--r--src/remoteobjects/qremoteobjectnode_p.h16
-rw-r--r--src/remoteobjects/qremoteobjectpacket.cpp226
-rw-r--r--src/remoteobjects/qremoteobjectpacket_p.h50
-rw-r--r--src/remoteobjects/qremoteobjectreplica.cpp39
-rw-r--r--src/remoteobjects/qremoteobjectreplica_p.h12
-rw-r--r--src/remoteobjects/qremoteobjectsource.cpp9
-rw-r--r--src/remoteobjects/qremoteobjectsource.h2
-rw-r--r--src/remoteobjects/qremoteobjectsource_p.h6
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;