1/****************************************************************************
2**
3** Copyright (C) 2017 Ford Motor Company
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtRemoteObjects module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qremoteobjectpacket_p.h"
41
42#include <QtCore/qabstractitemmodel.h>
43
44#include "qremoteobjectpendingcall.h"
45#include "qremoteobjectsource.h"
46#include "qremoteobjectsource_p.h"
47#include <cstring>
48
49//#define QTRO_VERBOSE_PROTOCOL
50QT_BEGIN_NAMESPACE
51
52
53// Add methods so we can use QMetaEnum in a set
54// Note for both functions we are skipping string comparisons/hashes. Since the
55// metaObjects are the same, we can just use the address of the string.
56inline bool operator==(const QMetaEnum e1, const QMetaEnum e2)
57{
58 return e1.enclosingMetaObject() == e2.enclosingMetaObject()
59 && e1.name() == e2.name()
60 && e1.enumName() == e2.enumName()
61 && e1.scope() == e2.scope();
62}
63
64inline uint qHash(const QMetaEnum &key, uint seed=0) Q_DECL_NOTHROW
65{
66 return qHash(key: key.enclosingMetaObject(), seed) ^ qHash(key: static_cast<const void *>(key.name()), seed)
67 ^ qHash(key: static_cast<const void *>(key.enumName()), seed) ^ qHash(key: static_cast<const void *>(key.scope()), seed);
68}
69
70using namespace QtRemoteObjects;
71
72namespace QRemoteObjectPackets {
73
74// QDataStream sends QVariants of custom types by sending their typename, allowing decode
75// on the receiving side. For QtRO and enums, this won't work, as the enums have different
76// scopes. E.g., the examples have ParentClassSource::MyEnum and ParentClassReplica::MyEnum.
77// Dynamic types will be created as ParentClass::MyEnum. So instead, we change the variants
78// to integers (encodeVariant) when sending them. On the receive side, the we know the
79// types of properties and the signatures for methods, so we can use that information to
80// decode the integer variant into an enum variant (via decodeVariant).
81const QVariant encodeVariant(const QVariant &value)
82{
83 if (QMetaType::typeFlags(type: value.userType()).testFlag(flag: QMetaType::IsEnumeration)) {
84 auto converted = QVariant(value);
85 const auto size = QMetaType(value.userType()).sizeOf();
86 switch (size) {
87 case 1: converted.convert(targetTypeId: QMetaType::Char); break;
88 case 2: converted.convert(targetTypeId: QMetaType::Short); break;
89 case 4: converted.convert(targetTypeId: QMetaType::Int); break;
90 // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
91// case 8: converted.convert(QMetaType::Long); break; // typeId for long from qmetatype.h
92 default:
93 qWarning() << "Invalid enum detected" << QMetaType::typeName(type: value.userType()) << "with size" << size;
94 converted.convert(targetTypeId: QMetaType::Int);
95 }
96#ifdef QTRO_VERBOSE_PROTOCOL
97 qDebug() << "Converting from enum to integer type" << size << converted << value;
98#endif
99 return converted;
100 }
101 return value;
102}
103
104QVariant &decodeVariant(QVariant &value, int type)
105{
106 if (QMetaType::typeFlags(type).testFlag(flag: QMetaType::IsEnumeration)) {
107#ifdef QTRO_VERBOSE_PROTOCOL
108 QVariant encoded(value);
109#endif
110 value.convert(targetTypeId: type);
111#ifdef QTRO_VERBOSE_PROTOCOL
112 qDebug() << "Converting to enum from integer type" << value << encoded;
113#endif
114 }
115 return value;
116}
117
118void serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, int internalIndex)
119{
120 const int propertyIndex = source->m_api->sourcePropertyIndex(index: internalIndex);
121 Q_ASSERT (propertyIndex >= 0);
122 const auto target = source->m_api->isAdapterProperty(internalIndex) ? source->m_adapter : source->m_object;
123 const auto property = target->metaObject()->property(index: propertyIndex);
124 const QVariant value = property.read(obj: target);
125 if (QMetaType::typeFlags(type: property.userType()).testFlag(flag: QMetaType::PointerToQObject)) {
126 auto const childSource = source->m_children.value(akey: internalIndex);
127 auto valueAsPointerToQObject = qvariant_cast<QObject *>(v: value);
128 if (childSource->m_object != valueAsPointerToQObject)
129 childSource->resetObject(newObject: valueAsPointerToQObject);
130 QRO_ qro(childSource);
131 if (source->d->isDynamic && qro.type == ObjectType::CLASS && childSource->m_object && !source->d->sentTypes.contains(value: qro.typeName)) {
132 QDataStream classDef(&qro.classDefinition, QIODevice::WriteOnly);
133 serializeDefinition(classDef, childSource);
134 source->d->sentTypes.insert(value: qro.typeName);
135 }
136 ds << QVariant::fromValue<QRO_>(value: qro);
137 if (qro.isNull)
138 return;
139 const int propertyCount = childSource->m_api->propertyCount();
140 // Put the properties in a buffer, the receiver may not know how to
141 // interpret the types until it registers new ones.
142 QDataStream params(&qro.parameters, QIODevice::WriteOnly);
143 params << propertyCount;
144 for (int internalIndex = 0; internalIndex < propertyCount; ++internalIndex)
145 serializeProperty(ds&: params, source: childSource, internalIndex);
146 ds << qro.parameters;
147 return;
148 }
149 if (source->d->isDynamic && property.userType() == QMetaType::QVariant &&
150 QMetaType::typeFlags(type: value.userType()).testFlag(flag: QMetaType::IsGadget)) {
151 const auto typeName = QString::fromLatin1(str: QMetaType::typeName(type: value.userType()));
152 if (!source->d->sentTypes.contains(value: typeName)) {
153 QRO_ qro(value);
154 ds << QVariant::fromValue<QRO_>(value: qro);
155 ds << qro.parameters;
156 source->d->sentTypes.insert(value: typeName);
157 return;
158 }
159 }
160 ds << encodeVariant(value);
161}
162
163void serializeHandshakePacket(DataStreamPacket &ds)
164{
165 ds.setId(Handshake);
166 ds << QString(protocolVersion);
167 ds.finishPacket();
168}
169
170void serializeInitPacket(DataStreamPacket &ds, const QRemoteObjectRootSource *source)
171{
172 ds.setId(InitPacket);
173 ds << source->name();
174 serializeProperties(ds, source);
175 ds.finishPacket();
176}
177
178void serializeProperties(DataStreamPacket &ds, const QRemoteObjectSourceBase *source)
179{
180 const SourceApiMap *api = source->m_api;
181
182 //Now copy the property data
183 const int numProperties = api->propertyCount();
184 ds << quint32(numProperties); //Number of properties
185
186 for (int internalIndex = 0; internalIndex < numProperties; ++internalIndex)
187 serializeProperty(ds, source, internalIndex);
188}
189
190bool deserializeQVariantList(QDataStream &s, QList<QVariant> &l)
191{
192 // note: optimized version of: QDataStream operator>>(QDataStream& s, QList<T>& l)
193 quint32 c;
194 s >> c;
195 const int initialListSize = l.size();
196 if (static_cast<quint32>(l.size()) < c)
197 l.reserve(alloc: c);
198 else if (static_cast<quint32>(l.size()) > c)
199 for (int i = c; i < initialListSize; ++i)
200 l.removeLast();
201
202 for (int i = 0; i < l.size(); ++i)
203 {
204 if (s.atEnd())
205 return false;
206 QVariant t;
207 s >> t;
208 l[i] = t;
209 }
210 for (quint32 i = l.size(); i < c; ++i)
211 {
212 if (s.atEnd())
213 return false;
214 QVariant t;
215 s >> t;
216 l.append(t);
217 }
218 return true;
219}
220
221void deserializeInitPacket(QDataStream &in, QVariantList &values)
222{
223 const bool success = deserializeQVariantList(s&: in, l&: values);
224 Q_ASSERT(success);
225 Q_UNUSED(success);
226}
227
228void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectRootSource *source)
229{
230 ds.setId(InitDynamicPacket);
231 ds << source->name();
232 serializeDefinition(ds, source);
233 serializeProperties(ds, source);
234 ds.finishPacket();
235}
236
237static ObjectType getObjectType(const QString &typeName)
238{
239 if (typeName == QLatin1String("QAbstractItemModelAdapter"))
240 return ObjectType::MODEL;
241 auto tid = QMetaType::type(typeName: typeName.toUtf8());
242 if (tid == QMetaType::UnknownType)
243 return ObjectType::CLASS;
244 QMetaType type(tid);
245 auto mo = type.metaObject();
246 if (mo && mo->inherits(metaObject: &QAbstractItemModel::staticMetaObject))
247 return ObjectType::MODEL;
248 return ObjectType::CLASS;
249}
250
251// Same method as in QVariant.cpp, as it isn't publicly exposed...
252static QMetaEnum metaEnumFromType(int type)
253{
254 QMetaType t(type);
255 if (t.flags() & QMetaType::IsEnumeration) {
256 if (const QMetaObject *metaObject = t.metaObject()) {
257 const char *enumName = QMetaType::typeName(type);
258 const char *lastColon = std::strrchr(s: enumName, c: ':');
259 if (lastColon)
260 enumName = lastColon + 1;
261 return metaObject->enumerator(index: metaObject->indexOfEnumerator(name: enumName));
262 }
263 }
264 return QMetaEnum();
265}
266
267static bool checkEnum(int type, QSet<QMetaEnum> &enums)
268{
269 if (QMetaType::typeFlags(type).testFlag(flag: QMetaType::IsEnumeration)) {
270 QMetaEnum meta = metaEnumFromType(type);
271 enums.insert(value: meta);
272 return true;
273 }
274 return false;
275}
276
277static void recurseMetaobject(const QMetaObject *mo, QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums)
278{
279 if (!mo || gadgets.contains(value: mo))
280 return;
281 gadgets.insert(value: mo);
282 const int numProperties = mo->propertyCount();
283 for (int i = 0; i < numProperties; ++i) {
284 const auto property = mo->property(index: i);
285 if (checkEnum(type: property.userType(), enums))
286 continue;
287 if (QMetaType::typeFlags(type: property.userType()).testFlag(flag: QMetaType::IsGadget))
288 recurseMetaobject(mo: QMetaType::metaObjectForType(type: property.userType()), gadgets, enums);
289 }
290}
291
292// A Source may only use a subset of the metaobjects properties/signals/slots, so we only search
293// the ones in the API. For nested pointer types, we will have another api to limit the search.
294// For nested PODs/enums, we search the entire qobject (using the recurseMetaobject call()).
295void recurseForGadgets(QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source)
296{
297 const SourceApiMap *api = source->m_api;
298
299 const int numSignals = api->signalCount();
300 const int numMethods = api->methodCount();
301 const int numProperties = api->propertyCount();
302
303 for (int si = 0; si < numSignals; ++si) {
304 const int params = api->signalParameterCount(index: si);
305 for (int pi = 0; pi < params; ++pi) {
306 const int type = api->signalParameterType(sigIndex: si, paramIndex: pi);
307 if (checkEnum(type, enums))
308 continue;
309 if (!QMetaType::typeFlags(type).testFlag(flag: QMetaType::IsGadget))
310 continue;
311 const auto mo = QMetaType::metaObjectForType(type);
312 if (source->d->sentTypes.contains(value: QLatin1String(mo->className())))
313 continue;
314 recurseMetaobject(mo, gadgets, enums);
315 source->d->sentTypes.insert(value: QLatin1String(mo->className()));
316 }
317 }
318
319 for (int mi = 0; mi < numMethods; ++mi) {
320 const int params = api->methodParameterCount(index: mi);
321 for (int pi = 0; pi < params; ++pi) {
322 const int type = api->methodParameterType(methodIndex: mi, paramIndex: pi);
323 if (checkEnum(type, enums))
324 continue;
325 if (!QMetaType::typeFlags(type).testFlag(flag: QMetaType::IsGadget))
326 continue;
327 const auto mo = QMetaType::metaObjectForType(type);
328 if (source->d->sentTypes.contains(value: QLatin1String(mo->className())))
329 continue;
330 recurseMetaobject(mo, gadgets, enums);
331 source->d->sentTypes.insert(value: QLatin1String(mo->className()));
332 }
333 }
334 for (int pi = 0; pi < numProperties; ++pi) {
335 const int index = api->sourcePropertyIndex(index: pi);
336 Q_ASSERT(index >= 0);
337 const auto target = api->isAdapterProperty(pi) ? source->m_adapter : source->m_object;
338 const auto metaProperty = target->metaObject()->property(index);
339 const int type = metaProperty.userType();
340 if (checkEnum(type, enums))
341 continue;
342 if (QMetaType::typeFlags(type).testFlag(flag: QMetaType::PointerToQObject)) {
343 auto const objectType = getObjectType(typeName: QString::fromLatin1(str: metaProperty.typeName()));
344 if (objectType == ObjectType::CLASS) {
345 auto const childSource = source->m_children.value(akey: pi);
346 if (childSource->m_object)
347 recurseForGadgets(gadgets, enums, source: childSource);
348 }
349 }
350 if (!QMetaType::typeFlags(type).testFlag(flag: QMetaType::IsGadget))
351 continue;
352 const auto mo = QMetaType::metaObjectForType(type);
353 if (source->d->sentTypes.contains(value: QLatin1String(mo->className())))
354 continue;
355 recurseMetaobject(mo, gadgets, enums);
356 source->d->sentTypes.insert(value: QLatin1String(mo->className()));
357 }
358}
359
360static bool checkForEnumsInSource(const QMetaObject *meta, const QRemoteObjectSourceBase *source)
361{
362 if (source->m_object->inherits(classname: meta->className()))
363 return true;
364 for (const auto child : source->m_children) {
365 if (child->m_object && checkForEnumsInSource(meta, source: child))
366 return true;
367 }
368 return false;
369}
370
371static void serializeEnum(QDataStream &ds, const QMetaEnum &enumerator)
372{
373 ds << QByteArray::fromRawData(enumerator.name(), size: qstrlen(str: enumerator.name()));
374 ds << enumerator.isFlag();
375 ds << enumerator.isScoped();
376 const auto typeName = QByteArray(enumerator.scope()).append(s: "::").append(s: enumerator.name());
377 quint32 size = QMetaType(QMetaType::type(typeName: typeName.constData())).sizeOf();
378 ds << size;
379#ifdef QTRO_VERBOSE_PROTOCOL
380 qDebug(" Enum (name = %s, size = %d, isFlag = %s, isScoped = %s):", enumerator.name(), size, enumerator.isFlag() ? "true" : "false", enumerator.isScoped() ? "true" : "false");
381#endif
382 const int keyCount = enumerator.keyCount();
383 ds << keyCount;
384 for (int k = 0; k < keyCount; ++k) {
385 ds << QByteArray::fromRawData(enumerator.key(index: k), size: qstrlen(str: enumerator.key(index: k)));
386 ds << enumerator.value(index: k);
387#ifdef QTRO_VERBOSE_PROTOCOL
388 qDebug(" Key %d (name = %s, value = %d):", k, enumerator.key(k), enumerator.value(k));
389#endif
390 }
391}
392
393static void serializeGadgets(QDataStream &ds, const QSet<const QMetaObject *> &gadgets, const QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source=nullptr)
394{
395 // Determine how to handle the enums found
396 QSet<QMetaEnum> qtEnums;
397 QSet<const QMetaObject *> dynamicEnumMetaObjects;
398 for (const auto metaEnum : enums) {
399 auto const metaObject = metaEnum.enclosingMetaObject();
400 if (gadgets.contains(value: metaObject)) // Part of a gadget will we serialize
401 continue;
402 // This checks if the enum is defined in our object heirarchy, in which case it will
403 // already have been serialized.
404 if (source && checkForEnumsInSource(meta: metaObject, source: source->d->root))
405 continue;
406 // qtEnums are enumerations already known by Qt, so we only need register them.
407 // We don't need to send all of the key/value data.
408 if (metaObject == qt_getQtMetaObject()) // Are the other Qt metaclasses for enums?
409 qtEnums.insert(value: metaEnum);
410 else
411 dynamicEnumMetaObjects.insert(value: metaEnum.enclosingMetaObject());
412 }
413 ds << quint32(qtEnums.size());
414 for (const auto metaEnum : qtEnums) {
415 QByteArray enumName(metaEnum.scope());
416 enumName.append(s: "::", len: 2).append(s: metaEnum.name());
417 ds << enumName;
418 }
419 const auto allMetaObjects = gadgets + dynamicEnumMetaObjects;
420 ds << quint32(allMetaObjects.size());
421#ifdef QTRO_VERBOSE_PROTOCOL
422 qDebug() << " Found" << gadgets.size() << "gadget/pod and" << (allMetaObjects.size() - gadgets.size()) << "enum types";
423 int i = 0;
424#endif
425 // There isn't an easy way to update a metaobject incrementally, so we
426 // send all of the metaobject's enums, but no properties, when an external
427 // enum is requested.
428 for (auto const meta : allMetaObjects) {
429 ds << QByteArray::fromRawData(meta->className(), size: qstrlen(str: meta->className()));
430 int propertyCount = gadgets.contains(value: meta) ? meta->propertyCount() : 0;
431 ds << quint32(propertyCount);
432#ifdef QTRO_VERBOSE_PROTOCOL
433 qDebug(" Gadget %d (name = %s, # properties = %d, # enums = %d):", i++, meta->className(), propertyCount, meta->enumeratorCount());
434#endif
435 for (int j = 0; j < propertyCount; j++) {
436 auto prop = meta->property(index: j);
437#ifdef QTRO_VERBOSE_PROTOCOL
438 qDebug(" Data member %d (name = %s, type = %s):", j, prop.name(), prop.typeName());
439#endif
440 ds << QByteArray::fromRawData(prop.name(), size: qstrlen(str: prop.name()));
441 ds << QByteArray::fromRawData(prop.typeName(), size: qstrlen(str: prop.typeName()));
442 }
443 int enumCount = meta->enumeratorCount();
444 ds << enumCount;
445 for (int j = 0; j < enumCount; j++) {
446 auto const enumMeta = meta->enumerator(index: j);
447 serializeEnum(ds, enumerator: enumMeta);
448 }
449 }
450}
451
452void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
453{
454 const SourceApiMap *api = source->m_api;
455 const QByteArray desiredClassName(api->typeName().toLatin1());
456 const QByteArray originalClassName = api->className();
457 // The dynamic class will be called typeName on the receiving side of this definition
458 // However, there are types like enums that have the QObject's class name. Replace()
459 // will convert a parameter such as "ParentClassSource::MyEnum" to "ParentClass::MyEnum"
460 // so the type can be properly resolved and registered.
461 auto replace = [&originalClassName, &desiredClassName](QByteArray &name) {
462 name.replace(before: originalClassName, after: desiredClassName);
463 };
464
465 ds << source->m_api->typeName();
466#ifdef QTRO_VERBOSE_PROTOCOL
467 qDebug() << "Serializing definition for" << source->m_api->typeName();
468#endif
469
470 //Now copy the property data
471 const int numEnums = api->enumCount();
472 const auto metaObject = source->m_object->metaObject();
473 ds << quint32(numEnums); //Number of Enums
474#ifdef QTRO_VERBOSE_PROTOCOL
475 qDebug() << " Found" << numEnums << "enumeration types";
476#endif
477 for (int i = 0; i < numEnums; ++i) {
478 auto enumerator = metaObject->enumerator(index: api->sourceEnumIndex(index: i));
479 Q_ASSERT(enumerator.isValid());
480 serializeEnum(ds, enumerator);
481 }
482
483 QSet<const QMetaObject *> gadgets;
484 QSet<QMetaEnum> enums;
485 recurseForGadgets(gadgets, enums, source);
486 serializeGadgets(ds, gadgets, enums, source);
487
488 const int numSignals = api->signalCount();
489 ds << quint32(numSignals); //Number of signals
490 for (int i = 0; i < numSignals; ++i) {
491 const int index = api->sourceSignalIndex(index: i);
492 Q_ASSERT(index >= 0);
493 auto signature = api->signalSignature(index: i);
494 replace(signature);
495#ifdef QTRO_VERBOSE_PROTOCOL
496 qDebug() << " Signal" << i << "(signature =" << signature << "parameter names =" << api->signalParameterNames(i) << ")";
497#endif
498 ds << signature;
499 ds << api->signalParameterNames(index: i);
500 }
501
502 const int numMethods = api->methodCount();
503 ds << quint32(numMethods); //Number of methods
504 for (int i = 0; i < numMethods; ++i) {
505 const int index = api->sourceMethodIndex(index: i);
506 Q_ASSERT(index >= 0);
507 auto signature = api->methodSignature(index: i);
508 replace(signature);
509 auto typeName = api->typeName(index: i);
510 replace(typeName);
511#ifdef QTRO_VERBOSE_PROTOCOL
512 qDebug() << " Slot" << i << "(signature =" << signature << "parameter names =" << api->methodParameterNames(i) << "return type =" << typeName << ")";
513#endif
514 ds << signature;
515 ds << typeName;
516 ds << api->methodParameterNames(index: i);
517 }
518
519 const int numProperties = api->propertyCount();
520 ds << quint32(numProperties); //Number of properties
521 for (int i = 0; i < numProperties; ++i) {
522 const int index = api->sourcePropertyIndex(index: i);
523 Q_ASSERT(index >= 0);
524
525 const auto target = api->isAdapterProperty(i) ? source->m_adapter : source->m_object;
526 const auto metaProperty = target->metaObject()->property(index);
527 ds << metaProperty.name();
528#ifdef QTRO_VERBOSE_PROTOCOL
529 qDebug() << " Property" << i << "name =" << metaProperty.name();
530#endif
531 if (QMetaType::typeFlags(type: metaProperty.userType()).testFlag(flag: QMetaType::PointerToQObject)) {
532 auto objectType = getObjectType(typeName: QLatin1String(metaProperty.typeName()));
533 ds << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
534#ifdef QTRO_VERBOSE_PROTOCOL
535 qDebug() << " Type:" << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
536#endif
537 } else {
538 ds << metaProperty.typeName();
539#ifdef QTRO_VERBOSE_PROTOCOL
540 qDebug() << " Type:" << metaProperty.typeName();
541#endif
542 }
543 if (metaProperty.notifySignalIndex() == -1) {
544 ds << QByteArray();
545#ifdef QTRO_VERBOSE_PROTOCOL
546 qDebug() << " Notification signal: None";
547#endif
548 } else {
549 auto signature = metaProperty.notifySignal().methodSignature();
550 replace(signature);
551 ds << signature;
552#ifdef QTRO_VERBOSE_PROTOCOL
553 qDebug() << " Notification signal:" << signature;
554#endif
555 }
556 }
557}
558
559void serializeAddObjectPacket(DataStreamPacket &ds, const QString &name, bool isDynamic)
560{
561 ds.setId(AddObject);
562 ds << name;
563 ds << isDynamic;
564 ds.finishPacket();
565}
566
567void deserializeAddObjectPacket(QDataStream &ds, bool &isDynamic)
568{
569 ds >> isDynamic;
570}
571
572void serializeRemoveObjectPacket(DataStreamPacket &ds, const QString &name)
573{
574 ds.setId(RemoveObject);
575 ds << name;
576 ds.finishPacket();
577}
578//There is no deserializeRemoveObjectPacket - no parameters other than id and name
579
580void serializeInvokePacket(DataStreamPacket &ds, const QString &name, int call, int index, const QVariantList &args, int serialId, int propertyIndex)
581{
582 ds.setId(InvokePacket);
583 ds << name;
584 ds << call;
585 ds << index;
586
587 ds << (quint32)args.size();
588 for (const auto &arg : args)
589 ds << encodeVariant(value: arg);
590
591 ds << serialId;
592 ds << propertyIndex;
593 ds.finishPacket();
594}
595
596void deserializeInvokePacket(QDataStream& in, int &call, int &index, QVariantList &args, int &serialId, int &propertyIndex)
597{
598 in >> call;
599 in >> index;
600 const bool success = deserializeQVariantList(s&: in, l&: args);
601 Q_ASSERT(success);
602 Q_UNUSED(success);
603 in >> serialId;
604 in >> propertyIndex;
605}
606
607void serializeInvokeReplyPacket(DataStreamPacket &ds, const QString &name, int ackedSerialId, const QVariant &value)
608{
609 ds.setId(InvokeReplyPacket);
610 ds << name;
611 ds << ackedSerialId;
612 ds << value;
613 ds.finishPacket();
614}
615
616void deserializeInvokeReplyPacket(QDataStream& in, int &ackedSerialId, QVariant &value){
617 in >> ackedSerialId;
618 in >> value;
619}
620
621void serializePropertyChangePacket(QRemoteObjectSourceBase *source, int signalIndex)
622{
623 int internalIndex = source->m_api->propertyRawIndexFromSignal(index: signalIndex);
624 auto &ds = source->d->m_packet;
625 ds.setId(PropertyChangePacket);
626 ds << source->name();
627 ds << internalIndex;
628 serializeProperty(ds, source, internalIndex);
629 ds.finishPacket();
630}
631
632void deserializePropertyChangePacket(QDataStream& in, int &index, QVariant &value)
633{
634 in >> index;
635 in >> value;
636}
637
638void serializeObjectListPacket(DataStreamPacket &ds, const ObjectInfoList &objects)
639{
640 ds.setId(ObjectList);
641 ds << objects;
642 ds.finishPacket();
643}
644
645void deserializeObjectListPacket(QDataStream &in, ObjectInfoList &objects)
646{
647 in >> objects;
648}
649
650void serializePingPacket(DataStreamPacket &ds, const QString &name)
651{
652 ds.setId(Ping);
653 ds << name;
654 ds.finishPacket();
655}
656
657void serializePongPacket(DataStreamPacket &ds, const QString &name)
658{
659 ds.setId(Pong);
660 ds << name;
661 ds.finishPacket();
662}
663
664QRO_::QRO_(QRemoteObjectSourceBase *source)
665 : name(source->name())
666 , typeName(source->m_api->typeName())
667 , type(source->m_adapter ? ObjectType::MODEL : getObjectType(typeName))
668 , isNull(source->m_object == nullptr)
669 , classDefinition()
670 , parameters()
671{}
672
673QRO_::QRO_(const QVariant &value)
674 : type(ObjectType::GADGET)
675 , isNull(false)
676{
677 auto meta = QMetaType::metaObjectForType(type: value.userType());
678 QDataStream out(&classDefinition, QIODevice::WriteOnly);
679 const int numProperties = meta->propertyCount();
680 const auto typeName = QByteArray::fromRawData(QMetaType::typeName(type: value.userType()), size: qstrlen(str: QMetaType::typeName(type: value.userType())));
681 out << quint32(0) << quint32(1);
682 out << typeName;
683 out << numProperties;
684#ifdef QTRO_VERBOSE_PROTOCOL
685 qDebug("Serializing POD definition to QRO_ (name = %s)", typeName.constData());
686#endif
687 for (int i = 0; i < numProperties; ++i) {
688 const auto property = meta->property(index: i);
689#ifdef QTRO_VERBOSE_PROTOCOL
690 qDebug(" Data member %d (name = %s, type = %s):", i, property.name(), property.typeName());
691#endif
692 out << QByteArray::fromRawData(property.name(), size: qstrlen(str: property.name()));
693 out << QByteArray::fromRawData(property.typeName(), size: qstrlen(str: property.typeName()));
694 }
695 QDataStream ds(&parameters, QIODevice::WriteOnly);
696 ds << value;
697#ifdef QTRO_VERBOSE_PROTOCOL
698 qDebug() << " Value:" << value;
699#endif
700}
701
702QDataStream &operator<<(QDataStream &stream, const QRO_ &info)
703{
704 stream << info.name << info.typeName << (quint8)(info.type) << info.classDefinition << info.isNull;
705 qCDebug(QT_REMOTEOBJECT) << "Serializing " << info;
706 // info.parameters will be filled in by serializeProperty
707 return stream;
708}
709
710QDataStream &operator>>(QDataStream &stream, QRO_ &info)
711{
712 quint8 tmpType;
713 stream >> info.name >> info.typeName >> tmpType >> info.classDefinition >> info.isNull;
714 info.type = static_cast<ObjectType>(tmpType);
715 qCDebug(QT_REMOTEOBJECT) << "Deserializing " << info;
716 if (!info.isNull)
717 stream >> info.parameters;
718 return stream;
719}
720
721} // namespace QRemoteObjectPackets
722
723QT_END_NAMESPACE
724

source code of qtremoteobjects/src/remoteobjects/qremoteobjectpacket.cpp