1 | // Copyright (C) 2017 Ford Motor Company |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include <QtCore/qabstractitemmodel.h> |
5 | #include <QtCore/qbytearrayview.h> |
6 | |
7 | #include "qremoteobjectcontainers_p.h" |
8 | #include "qremoteobjectpendingcall.h" |
9 | #include "qremoteobjectsource.h" |
10 | #include "qremoteobjectsource_p.h" |
11 | #include "qremoteobjectpacket_p.h" |
12 | #include "qconnectionfactories.h" |
13 | #include "qconnectionfactories_p.h" |
14 | #include <cstring> |
15 | |
16 | //#define QTRO_VERBOSE_PROTOCOL |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | |
20 | // Add methods so we can use QMetaEnum in a set |
21 | // Note for both functions we are skipping string comparisons/hashes. Since the |
22 | // metaObjects are the same, we can just use the address of the string. |
23 | inline bool operator==(const QMetaEnum e1, const QMetaEnum e2) |
24 | { |
25 | return e1.enclosingMetaObject() == e2.enclosingMetaObject() |
26 | && e1.name() == e2.name() |
27 | && e1.enumName() == e2.enumName() |
28 | && e1.scope() == e2.scope(); |
29 | } |
30 | |
31 | inline size_t qHash(const QMetaEnum &key, size_t seed=0) Q_DECL_NOTHROW |
32 | { |
33 | return qHash(key: key.enclosingMetaObject(), seed) ^ qHash(key: static_cast<const void *>(key.name()), seed) |
34 | ^ qHash(key: static_cast<const void *>(key.enumName()), seed) ^ qHash(key: static_cast<const void *>(key.scope()), seed); |
35 | } |
36 | |
37 | static bool isSequentialGadgetType(QMetaType metaType) |
38 | { |
39 | if (QMetaType::canConvert(fromType: metaType, toType: QMetaType::fromType<QSequentialIterable>())) { |
40 | static QHash<int, bool> lookup; |
41 | if (!lookup.contains(key: metaType.id())) { |
42 | auto stubVariant = QVariant(metaType, nullptr); |
43 | auto asIterable = stubVariant.value<QSequentialIterable>(); |
44 | auto valueMetaType = asIterable.metaContainer().valueMetaType(); |
45 | lookup[metaType.id()] = valueMetaType.flags().testFlag(flag: QMetaType::IsGadget); |
46 | } |
47 | return lookup[metaType.id()]; |
48 | } |
49 | return false; |
50 | } |
51 | |
52 | static bool isAssociativeGadgetType(QMetaType metaType) |
53 | { |
54 | if (QMetaType::canConvert(fromType: metaType, toType: QMetaType::fromType<QAssociativeIterable>())) { |
55 | static QHash<int, bool> lookup; |
56 | if (!lookup.contains(key: metaType.id())) { |
57 | auto stubVariant = QVariant(metaType, nullptr); |
58 | auto asIterable = stubVariant.value<QAssociativeIterable>(); |
59 | auto valueMetaType = asIterable.metaContainer().mappedMetaType(); |
60 | lookup[metaType.id()] = valueMetaType.flags().testFlag(flag: QMetaType::IsGadget); |
61 | } |
62 | return lookup[metaType.id()]; |
63 | } |
64 | return false; |
65 | } |
66 | |
67 | using namespace QtRemoteObjects; |
68 | |
69 | namespace QRemoteObjectPackets { |
70 | |
71 | QMetaType transferTypeForEnum(QMetaType enumType) |
72 | { |
73 | const auto size = enumType.sizeOf(); |
74 | switch (size) { |
75 | case 1: return QMetaType::fromType<qint8>(); |
76 | case 2: return QMetaType::fromType<qint16>(); |
77 | case 4: return QMetaType::fromType<qint32>(); |
78 | // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int) |
79 | // case 8: args.push_back(QVariant(QMetaType::Int, argv[i + 1])); break; |
80 | default: |
81 | qCWarning(QT_REMOTEOBJECT_IO) << "Invalid enum detected (Dynamic Replica)" << enumType.name() << "with size" << size; |
82 | return QMetaType::fromType<qint32>(); |
83 | } |
84 | } |
85 | |
86 | // QDataStream sends QVariants of custom types by sending their typename, allowing decode |
87 | // on the receiving side. For QtRO and enums, this won't work, as the enums have different |
88 | // scopes. E.g., the examples have ParentClassSource::MyEnum and ParentClassReplica::MyEnum. |
89 | // Dynamic types will be created as ParentClass::MyEnum. So instead, we change the variants |
90 | // to integers (encodeVariant) when sending them. On the receive side, the we know the |
91 | // types of properties and the signatures for methods, so we can use that information to |
92 | // decode the integer variant into an enum variant (via decodeVariant). |
93 | QVariant encodeVariant(const QVariant &value) |
94 | { |
95 | const auto metaType = value.metaType(); |
96 | if (metaType.flags().testFlag(flag: QMetaType::IsEnumeration)) { |
97 | auto converted = QVariant(value); |
98 | auto transferType = transferTypeForEnum(enumType: metaType); |
99 | converted.convert(type: transferType); |
100 | #ifdef QTRO_VERBOSE_PROTOCOL |
101 | qDebug() << "Converting from enum to integer type" << transferType.sizeOf() << converted << value; |
102 | #endif |
103 | return converted; |
104 | } |
105 | if (isSequentialGadgetType(metaType)) { // Doesn't include QtROSequentialContainer |
106 | // TODO Way to create the QVariant without copying the QSQ_? |
107 | QSQ_ sequence(value); |
108 | #ifdef QTRO_VERBOSE_PROTOCOL |
109 | qDebug() << "Encoding sequential container" << metaType.name() << "to QSQ_ to transmit" ; |
110 | #endif |
111 | return QVariant::fromValue<QSQ_>(value: sequence); |
112 | } |
113 | if (metaType == QMetaType::fromType<QtROSequentialContainer>()) { |
114 | QSQ_ sequence(value); |
115 | #ifdef QTRO_VERBOSE_PROTOCOL |
116 | qDebug() << "Encoding QtROSequentialContainer container to QSQ_ to transmit" ; |
117 | #endif |
118 | return QVariant::fromValue<QSQ_>(value: sequence); |
119 | } |
120 | if (isAssociativeGadgetType(metaType)) { // Doesn't include QtROAssociativeContainer |
121 | QAS_ map(value); |
122 | #ifdef QTRO_VERBOSE_PROTOCOL |
123 | qDebug() << "Encoding associative container" << metaType.name() << "to QAS_ to transmit" ; |
124 | #endif |
125 | return QVariant::fromValue<QAS_>(value: map); |
126 | } |
127 | if (metaType == QMetaType::fromType<QtROAssociativeContainer>()) { |
128 | QAS_ map(value); |
129 | #ifdef QTRO_VERBOSE_PROTOCOL |
130 | qDebug() << "Encoding QtROAssociativeContainer container to QAS_ to transmit" ; |
131 | #endif |
132 | return QVariant::fromValue<QAS_>(value: map); |
133 | } |
134 | return value; |
135 | } |
136 | |
137 | QVariant decodeVariant(QVariant &&value, QMetaType metaType) |
138 | { |
139 | if (metaType.flags().testFlag(flag: QMetaType::IsEnumeration)) { |
140 | #ifdef QTRO_VERBOSE_PROTOCOL |
141 | QVariant encoded(value); |
142 | #endif |
143 | value.convert(type: metaType); |
144 | #ifdef QTRO_VERBOSE_PROTOCOL |
145 | qDebug() << "Converting to enum from integer type" << value << encoded; |
146 | #endif |
147 | } else if (value.metaType() == QMetaType::fromType<QRemoteObjectPackets::QSQ_>()) { |
148 | const auto *qsq_ = static_cast<const QRemoteObjectPackets::QSQ_ *>(value.constData()); |
149 | QDataStream in(qsq_->values); |
150 | auto containerType = QMetaType::fromName(name: qsq_->typeName.constData()); |
151 | bool isRegistered = containerType.isRegistered(); |
152 | if (isRegistered) { |
153 | QVariant seq{containerType, nullptr}; |
154 | if (!seq.canView<QSequentialIterable>()) { |
155 | qWarning() << "Unsupported container" << qsq_->typeName.constData() |
156 | << "(not viewable)" ; |
157 | return QVariant(); |
158 | } |
159 | QSequentialIterable seqIter = seq.view<QSequentialIterable>(); |
160 | if (!seqIter.metaContainer().canAddValue()) { |
161 | qWarning() << "Unsupported container" << qsq_->typeName.constData() |
162 | << "(Unable to add values)" ; |
163 | return QVariant(); |
164 | } |
165 | QByteArray valueTypeName; |
166 | quint32 count; |
167 | in >> valueTypeName; |
168 | in >> count; |
169 | QMetaType valueType = QMetaType::fromName(name: valueTypeName.constData()); |
170 | QVariant tmp{valueType, nullptr}; |
171 | for (quint32 i = 0; i < count; i++) { |
172 | if (!valueType.load(stream&: in, data: tmp.data())) { |
173 | if (seqIter.metaContainer().canRemoveValue() || i == 0) { |
174 | for (quint32 ii = 0; ii < i; ii++) |
175 | seqIter.removeValue(); |
176 | qWarning(msg: "QSQ_: unable to load type '%s', returning an empty list." , valueTypeName.constData()); |
177 | } else { |
178 | qWarning(msg: "QSQ_: unable to load type '%s', returning a partial list." , valueTypeName.constData()); |
179 | } |
180 | break; |
181 | } |
182 | seqIter.addValue(value: tmp); |
183 | } |
184 | value = seq; |
185 | #ifdef QTRO_VERBOSE_PROTOCOL |
186 | qDebug() << "Decoding QSQ_ to sequential container" << containerType.name() |
187 | << valueTypeName; |
188 | #endif |
189 | } else { |
190 | QtROSequentialContainer container{}; |
191 | in >> container; |
192 | container.m_typeName = qsq_->typeName; |
193 | value = QVariant(QMetaType::fromType<QtROSequentialContainer>(), &container); |
194 | #ifdef QTRO_VERBOSE_PROTOCOL |
195 | qDebug() << "Decoding QSQ_ to QtROSequentialContainer of" |
196 | << container.m_valueTypeName; |
197 | #endif |
198 | } |
199 | } else if (value.metaType() == QMetaType::fromType<QRemoteObjectPackets::QAS_>()) { |
200 | const auto *qas_ = static_cast<const QRemoteObjectPackets::QAS_ *>(value.constData()); |
201 | QDataStream in(qas_->values); |
202 | auto containerType = QMetaType::fromName(name: qas_->typeName.constData()); |
203 | bool isRegistered = containerType.isRegistered(); |
204 | if (isRegistered) { |
205 | QVariant map{containerType, nullptr}; |
206 | if (!map.canView<QAssociativeIterable>()) { |
207 | qWarning() << "Unsupported container" << qas_->typeName.constData() |
208 | << "(not viewable)" ; |
209 | return QVariant(); |
210 | } |
211 | QAssociativeIterable mapIter = map.view<QAssociativeIterable>(); |
212 | if (!mapIter.metaContainer().canSetMappedAtKey()) { |
213 | qWarning() << "Unsupported container" << qas_->typeName.constData() |
214 | << "(Unable to insert values)" ; |
215 | return QVariant(); |
216 | } |
217 | QByteArray keyTypeName, valueTypeName; |
218 | quint32 count; |
219 | in >> keyTypeName; |
220 | QMetaType keyType = QMetaType::fromName(name: keyTypeName.constData()); |
221 | if (!keyType.isValid()) { |
222 | // This happens for class enums, where the passed keyType is <ClassName>::<enum> |
223 | // For a compiled replica, the keyType is <ClassName>Replica::<enum> |
224 | // Since the full typename is registered, we can pull the keyType from there |
225 | keyType = mapIter.metaContainer().keyMetaType(); |
226 | } |
227 | QMetaType transferType = keyType; |
228 | if (keyType.flags().testFlag(flag: QMetaType::IsEnumeration)) |
229 | transferType = transferTypeForEnum(enumType: keyType); |
230 | QVariant key{transferType, nullptr}; |
231 | in >> valueTypeName; |
232 | QMetaType valueType = QMetaType::fromName(name: valueTypeName.constData()); |
233 | QVariant val{valueType, nullptr}; |
234 | in >> count; |
235 | for (quint32 i = 0; i < count; i++) { |
236 | if (!transferType.load(stream&: in, data: key.data())) { |
237 | map = QVariant{containerType, nullptr}; |
238 | qWarning(msg: "QAS_: unable to load key of type '%s', returning an empty map." , |
239 | keyTypeName.constData()); |
240 | break; |
241 | } |
242 | if (!valueType.load(stream&: in, data: val.data())) { |
243 | map = QVariant{containerType, nullptr}; |
244 | qWarning(msg: "QAS_: unable to load value of type '%s', returning an empty map." , |
245 | valueTypeName.constData()); |
246 | break; |
247 | } |
248 | if (transferType != keyType) { |
249 | QVariant enumKey(key); |
250 | enumKey.convert(type: keyType); |
251 | mapIter.setValue(key: enumKey, mapped: val); |
252 | } else { |
253 | mapIter.setValue(key, mapped: val); |
254 | } |
255 | } |
256 | value = map; |
257 | #ifdef QTRO_VERBOSE_PROTOCOL |
258 | qDebug() << "Decoding QAS_ to associative container" << containerType.name() |
259 | << valueTypeName << keyTypeName << count << mapIter.size() << map; |
260 | #endif |
261 | } else { |
262 | QtROAssociativeContainer container{}; |
263 | in >> container; |
264 | container.m_typeName = qas_->typeName; |
265 | value = QVariant(QMetaType::fromType<QtROAssociativeContainer>(), &container); |
266 | #ifdef QTRO_VERBOSE_PROTOCOL |
267 | qDebug() << "Decoding QAS_ to QtROAssociativeContainer of" |
268 | << container.m_valueTypeName; |
269 | #endif |
270 | } |
271 | } |
272 | return std::move(value); |
273 | } |
274 | |
275 | void QDataStreamCodec::serializeProperty(const QRemoteObjectSourceBase *source, int internalIndex) |
276 | { |
277 | serializeProperty(ds&: m_packet, source, internalIndex); |
278 | } |
279 | |
280 | void QDataStreamCodec::serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, int internalIndex) |
281 | { |
282 | const int propertyIndex = source->m_api->sourcePropertyIndex(index: internalIndex); |
283 | Q_ASSERT (propertyIndex >= 0); |
284 | const auto target = source->m_api->isAdapterProperty(internalIndex) ? source->m_adapter : source->m_object; |
285 | const auto property = target->metaObject()->property(index: propertyIndex); |
286 | const QVariant value = property.read(obj: target); |
287 | if (property.metaType().flags().testFlag(flag: QMetaType::PointerToQObject)) { |
288 | auto const childSource = source->m_children.value(key: internalIndex); |
289 | auto valueAsPointerToQObject = qvariant_cast<QObject *>(v: value); |
290 | if (childSource->m_object != valueAsPointerToQObject) |
291 | childSource->resetObject(newObject: valueAsPointerToQObject); |
292 | QRO_ qro(childSource); |
293 | if (source->d->isDynamic && qro.type == ObjectType::CLASS && childSource->m_object && !source->d->sentTypes.contains(value: qro.typeName)) { |
294 | QDataStream classDef(&qro.classDefinition, QIODevice::WriteOnly); |
295 | serializeDefinition(classDef, childSource); |
296 | source->d->sentTypes.insert(value: qro.typeName); |
297 | } |
298 | ds << QVariant::fromValue<QRO_>(value: qro); |
299 | if (qro.isNull) |
300 | return; |
301 | const int propertyCount = childSource->m_api->propertyCount(); |
302 | // Put the properties in a buffer, the receiver may not know how to |
303 | // interpret the types until it registers new ones. |
304 | QDataStream params(&qro.parameters, QIODevice::WriteOnly); |
305 | params << propertyCount; |
306 | for (int internalIndex = 0; internalIndex < propertyCount; ++internalIndex) |
307 | serializeProperty(ds&: params, source: childSource, internalIndex); |
308 | ds << qro.parameters; |
309 | return; |
310 | } |
311 | if (source->d->isDynamic && property.userType() == QMetaType::QVariant |
312 | && value.metaType().flags().testFlag(flag: QMetaType::IsGadget)) { |
313 | const auto typeName = QString::fromLatin1(ba: value.metaType().name()); |
314 | if (!source->d->sentTypes.contains(value: typeName)) { |
315 | QRO_ qro(value); |
316 | ds << QVariant::fromValue<QRO_>(value: qro); |
317 | ds << qro.parameters; |
318 | source->d->sentTypes.insert(value: typeName); |
319 | return; |
320 | } |
321 | } |
322 | ds << encodeVariant(value); |
323 | } |
324 | |
325 | void QDataStreamCodec::serializeHandshakePacket() |
326 | { |
327 | m_packet.setId(Handshake); |
328 | m_packet << QString(protocolVersion); |
329 | m_packet.finishPacket(); |
330 | } |
331 | |
332 | void QDataStreamCodec::serializeInitPacket(const QRemoteObjectRootSource *source) |
333 | { |
334 | m_packet.setId(InitPacket); |
335 | m_packet << source->name(); |
336 | serializeProperties(source); |
337 | m_packet.finishPacket(); |
338 | } |
339 | |
340 | void QDataStreamCodec::serializeProperties(const QRemoteObjectSourceBase *source) |
341 | { |
342 | const SourceApiMap *api = source->m_api; |
343 | |
344 | //Now copy the property data |
345 | const int numProperties = api->propertyCount(); |
346 | m_packet << quint32(numProperties); //Number of properties |
347 | |
348 | for (int internalIndex = 0; internalIndex < numProperties; ++internalIndex) |
349 | serializeProperty(source, internalIndex); |
350 | } |
351 | |
352 | bool deserializeQVariantList(QDataStream &s, QVariantList &l) |
353 | { |
354 | // note: optimized version of: QDataStream operator>>(QDataStream& s, QList<T>& l) |
355 | quint32 c; |
356 | s >> c; |
357 | |
358 | const qsizetype count = static_cast<qsizetype>(c); |
359 | const qsizetype listSize = l.size(); |
360 | if (listSize < count) |
361 | l.reserve(asize: count); |
362 | else if (listSize > count) |
363 | l.resize(size: count); |
364 | |
365 | for (int i = 0; i < l.size(); ++i) |
366 | { |
367 | if (s.atEnd()) |
368 | return false; |
369 | s >> l[i]; |
370 | } |
371 | for (auto i = l.size(); i < count; ++i) |
372 | { |
373 | if (s.atEnd()) |
374 | return false; |
375 | s >> l.emplace_back(); |
376 | } |
377 | return true; |
378 | } |
379 | |
380 | void QDataStreamCodec::deserializeInitPacket(QDataStream &in, QVariantList &values) |
381 | { |
382 | const bool success = deserializeQVariantList(s&: in, l&: values); |
383 | Q_ASSERT(success); |
384 | Q_UNUSED(success) |
385 | } |
386 | |
387 | void QDataStreamCodec::serializeInitDynamicPacket(const QRemoteObjectRootSource *source) |
388 | { |
389 | m_packet.setId(InitDynamicPacket); |
390 | m_packet << source->name(); |
391 | serializeDefinition(m_packet, source); |
392 | serializeProperties(source); |
393 | m_packet.finishPacket(); |
394 | } |
395 | |
396 | static ObjectType getObjectType(const QString &typeName) |
397 | { |
398 | if (typeName == QLatin1String("QAbstractItemModelAdapter" )) |
399 | return ObjectType::MODEL; |
400 | auto tid = QMetaType::fromName(name: typeName.toUtf8()).id(); |
401 | if (tid == QMetaType::UnknownType) |
402 | return ObjectType::CLASS; |
403 | QMetaType type(tid); |
404 | auto mo = type.metaObject(); |
405 | if (mo && mo->inherits(metaObject: &QAbstractItemModel::staticMetaObject)) |
406 | return ObjectType::MODEL; |
407 | return ObjectType::CLASS; |
408 | } |
409 | |
410 | static QByteArrayView resolveEnumName(QMetaType t, bool &isFlag) |
411 | { |
412 | // Takes types like `MyPOD::Position` or `QFlags<MyPOD::Position>` and returns 'Position` |
413 | QByteArrayView enumName(t.name()); |
414 | isFlag = enumName.startsWith(other: "QFlags<" ); |
415 | auto lastColon = enumName.lastIndexOf(ch: ':'); |
416 | if (lastColon >= 0) |
417 | enumName = QByteArrayView(t.name() + lastColon + 1); |
418 | if (isFlag) |
419 | enumName.chop(n: 1); |
420 | return enumName; |
421 | } |
422 | |
423 | static QMetaEnum metaEnumFromType(QMetaType t) |
424 | { |
425 | if (t.flags().testFlag(flag: QMetaType::IsEnumeration)) { |
426 | if (const QMetaObject *m = t.metaObject()) { |
427 | bool isFlag; |
428 | auto enumName = resolveEnumName(t, isFlag); |
429 | if (isFlag) { |
430 | for (int i = m->enumeratorOffset(); i < m->enumeratorCount(); i++) { |
431 | auto testType = m->enumerator(index: i); |
432 | if (testType.isFlag() && |
433 | enumName.compare(a: QByteArrayView(testType.enumName())) == 0) |
434 | return testType; |
435 | } |
436 | } |
437 | return m->enumerator(index: m->indexOfEnumerator(name: enumName.data())); |
438 | } |
439 | } |
440 | return QMetaEnum(); |
441 | } |
442 | |
443 | static bool checkEnum(QMetaType metaType, QSet<QMetaEnum> &enums) |
444 | { |
445 | if (metaType.flags().testFlag(flag: QMetaType::IsEnumeration)) { |
446 | QMetaEnum meta = metaEnumFromType(t: metaType); |
447 | enums.insert(value: meta); |
448 | return true; |
449 | } |
450 | return false; |
451 | } |
452 | |
453 | static void recurseMetaobject(const QMetaObject *mo, QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums) |
454 | { |
455 | if (!mo || gadgets.contains(value: mo)) |
456 | return; |
457 | gadgets.insert(value: mo); |
458 | const int numProperties = mo->propertyCount(); |
459 | for (int i = 0; i < numProperties; ++i) { |
460 | const auto property = mo->property(index: i); |
461 | if (checkEnum(metaType: property.metaType(), enums)) |
462 | continue; |
463 | if (property.metaType().flags().testFlag(flag: QMetaType::IsGadget)) |
464 | recurseMetaobject(mo: property.metaType().metaObject(), gadgets, enums); |
465 | } |
466 | } |
467 | |
468 | // A Source may only use a subset of the metaobjects properties/signals/slots, so we only search |
469 | // the ones in the API. For nested pointer types, we will have another api to limit the search. |
470 | // For nested PODs/enums, we search the entire qobject (using the recurseMetaobject call()). |
471 | void recurseForGadgets(QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source) |
472 | { |
473 | const SourceApiMap *api = source->m_api; |
474 | |
475 | const int numSignals = api->signalCount(); |
476 | const int numMethods = api->methodCount(); |
477 | const int numProperties = api->propertyCount(); |
478 | |
479 | for (int si = 0; si < numSignals; ++si) { |
480 | const int params = api->signalParameterCount(index: si); |
481 | for (int pi = 0; pi < params; ++pi) { |
482 | const int type = api->signalParameterType(sigIndex: si, paramIndex: pi); |
483 | const auto metaType = QMetaType(type); |
484 | if (checkEnum(metaType, enums)) |
485 | continue; |
486 | if (!metaType.flags().testFlag(flag: QMetaType::IsGadget)) |
487 | continue; |
488 | const auto mo = metaType.metaObject(); |
489 | if (source->d->sentTypes.contains(value: QLatin1String(mo->className()))) |
490 | continue; |
491 | recurseMetaobject(mo, gadgets, enums); |
492 | source->d->sentTypes.insert(value: QLatin1String(mo->className())); |
493 | } |
494 | } |
495 | |
496 | for (int mi = 0; mi < numMethods; ++mi) { |
497 | const int params = api->methodParameterCount(index: mi); |
498 | for (int pi = 0; pi < params; ++pi) { |
499 | const int type = api->methodParameterType(methodIndex: mi, paramIndex: pi); |
500 | const auto metaType = QMetaType(type); |
501 | if (checkEnum(metaType, enums)) |
502 | continue; |
503 | if (!metaType.flags().testFlag(flag: QMetaType::IsGadget)) |
504 | continue; |
505 | const auto mo = metaType.metaObject(); |
506 | if (source->d->sentTypes.contains(value: QLatin1String(mo->className()))) |
507 | continue; |
508 | recurseMetaobject(mo, gadgets, enums); |
509 | source->d->sentTypes.insert(value: QLatin1String(mo->className())); |
510 | } |
511 | } |
512 | for (int pi = 0; pi < numProperties; ++pi) { |
513 | const int index = api->sourcePropertyIndex(index: pi); |
514 | Q_ASSERT(index >= 0); |
515 | const auto target = api->isAdapterProperty(pi) ? source->m_adapter : source->m_object; |
516 | const auto metaProperty = target->metaObject()->property(index); |
517 | const auto metaType = metaProperty.metaType(); |
518 | if (checkEnum(metaType, enums)) |
519 | continue; |
520 | if (metaType.flags().testFlag(flag: QMetaType::PointerToQObject)) { |
521 | auto const objectType = getObjectType(typeName: QString::fromLatin1(ba: metaProperty.typeName())); |
522 | if (objectType == ObjectType::CLASS) { |
523 | auto const childSource = source->m_children.value(key: pi); |
524 | if (childSource->m_object) |
525 | recurseForGadgets(gadgets, enums, source: childSource); |
526 | } |
527 | } |
528 | if (!metaType.flags().testFlag(flag: QMetaType::IsGadget)) |
529 | continue; |
530 | const auto mo = metaType.metaObject(); |
531 | if (source->d->sentTypes.contains(value: QLatin1String(mo->className()))) |
532 | continue; |
533 | recurseMetaobject(mo, gadgets, enums); |
534 | source->d->sentTypes.insert(value: QLatin1String(mo->className())); |
535 | } |
536 | } |
537 | |
538 | static bool checkForEnumsInSource(const QMetaObject *meta, const QRemoteObjectSourceBase *source) |
539 | { |
540 | if (source->m_object->inherits(classname: meta->className())) |
541 | return true; |
542 | for (const auto &child : source->m_children) { |
543 | if (child->m_object && checkForEnumsInSource(meta, source: child)) |
544 | return true; |
545 | } |
546 | return false; |
547 | } |
548 | |
549 | static void serializeEnum(QDataStream &ds, const QMetaEnum &enumerator) |
550 | { |
551 | ds << QByteArray::fromRawData(data: enumerator.name(), size: qsizetype(qstrlen(str: enumerator.name()))); |
552 | ds << enumerator.isFlag(); |
553 | ds << enumerator.isScoped(); |
554 | const auto typeName = QByteArray(enumerator.scope()).append(s: "::" ).append(s: enumerator.name()); |
555 | const quint32 size = quint32(QMetaType::fromName(name: typeName.constData()).sizeOf()); |
556 | ds << size; |
557 | #ifdef QTRO_VERBOSE_PROTOCOL |
558 | qDebug(" Enum (name = %s, size = %d, isFlag = %s, isScoped = %s):" , enumerator.name(), size, enumerator.isFlag() ? "true" : "false" , enumerator.isScoped() ? "true" : "false" ); |
559 | #endif |
560 | const int keyCount = enumerator.keyCount(); |
561 | ds << keyCount; |
562 | for (int k = 0; k < keyCount; ++k) { |
563 | ds << QByteArray::fromRawData(data: enumerator.key(index: k), size: qsizetype(qstrlen(str: enumerator.key(index: k)))); |
564 | ds << enumerator.value(index: k); |
565 | #ifdef QTRO_VERBOSE_PROTOCOL |
566 | qDebug(" Key %d (name = %s, value = %d):" , k, enumerator.key(k), enumerator.value(k)); |
567 | #endif |
568 | } |
569 | } |
570 | |
571 | static void serializeGadgets(QDataStream &ds, const QSet<const QMetaObject *> &gadgets, const QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source=nullptr) |
572 | { |
573 | // Determine how to handle the enums found |
574 | QSet<QMetaEnum> qtEnums; |
575 | QSet<const QMetaObject *> dynamicEnumMetaObjects; |
576 | for (const auto &metaEnum : enums) { |
577 | auto const metaObject = metaEnum.enclosingMetaObject(); |
578 | if (gadgets.contains(value: metaObject)) // Part of a gadget will we serialize |
579 | continue; |
580 | // This checks if the enum is defined in our object heirarchy, in which case it will |
581 | // already have been serialized. |
582 | if (source && checkForEnumsInSource(meta: metaObject, source: source->d->root)) |
583 | continue; |
584 | // qtEnums are enumerations already known by Qt, so we only need register them. |
585 | // We don't need to send all of the key/value data. |
586 | if (metaObject == &Qt::staticMetaObject) // Are the other Qt metaclasses for enums? |
587 | qtEnums.insert(value: metaEnum); |
588 | else |
589 | dynamicEnumMetaObjects.insert(value: metaEnum.enclosingMetaObject()); |
590 | } |
591 | ds << quint32(qtEnums.size()); |
592 | for (const auto &metaEnum : qtEnums) { |
593 | QByteArray enumName(metaEnum.scope()); |
594 | enumName.append(s: "::" , len: 2).append(s: metaEnum.name()); |
595 | ds << enumName; |
596 | } |
597 | const auto allMetaObjects = gadgets + dynamicEnumMetaObjects; |
598 | ds << quint32(allMetaObjects.size()); |
599 | #ifdef QTRO_VERBOSE_PROTOCOL |
600 | qDebug() << " Found" << gadgets.size() << "gadget/pod and" << (allMetaObjects.size() - gadgets.size()) << "enum types" ; |
601 | int i = 0; |
602 | #endif |
603 | // There isn't an easy way to update a metaobject incrementally, so we |
604 | // send all of the metaobject's enums, but no properties, when an external |
605 | // enum is requested. |
606 | for (auto const meta : allMetaObjects) { |
607 | ds << QByteArray::fromRawData(data: meta->className(), size: qsizetype(qstrlen(str: meta->className()))); |
608 | int propertyCount = gadgets.contains(value: meta) ? meta->propertyCount() : 0; |
609 | ds << quint32(propertyCount); |
610 | #ifdef QTRO_VERBOSE_PROTOCOL |
611 | qDebug(" Gadget %d (name = %s, # properties = %d, # enums = %d):" , i++, meta->className(), propertyCount, meta->enumeratorCount() - meta->enumeratorOffset()); |
612 | #endif |
613 | for (int j = 0; j < propertyCount; j++) { |
614 | auto prop = meta->property(index: j); |
615 | #ifdef QTRO_VERBOSE_PROTOCOL |
616 | qDebug(" Data member %d (name = %s, type = %s):" , j, prop.name(), prop.typeName()); |
617 | #endif |
618 | ds << QByteArray::fromRawData(data: prop.name(), size: qsizetype(qstrlen(str: prop.name()))); |
619 | ds << QByteArray::fromRawData(data: prop.typeName(), size: qsizetype(qstrlen(str: prop.typeName()))); |
620 | } |
621 | int enumCount = meta->enumeratorCount() - meta->enumeratorOffset(); |
622 | ds << quint32(enumCount); |
623 | for (int j = meta->enumeratorOffset(); j < meta->enumeratorCount(); j++) { |
624 | auto const enumMeta = meta->enumerator(index: j); |
625 | serializeEnum(ds, enumerator: enumMeta); |
626 | } |
627 | } |
628 | } |
629 | |
630 | void QDataStreamCodec::serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source) |
631 | { |
632 | const SourceApiMap *api = source->m_api; |
633 | const QByteArray desiredClassName(api->typeName().toLatin1()); |
634 | const QByteArray originalClassName = api->className(); |
635 | // The dynamic class will be called typeName on the receiving side of this definition |
636 | // However, there are types like enums that have the QObject's class name. Replace() |
637 | // will convert a parameter such as "ParentClassSource::MyEnum" to "ParentClass::MyEnum" |
638 | // so the type can be properly resolved and registered. |
639 | auto replace = [&originalClassName, &desiredClassName](QByteArray &name) { |
640 | name.replace(before: originalClassName, after: desiredClassName); |
641 | }; |
642 | |
643 | ds << source->m_api->typeName(); |
644 | #ifdef QTRO_VERBOSE_PROTOCOL |
645 | qDebug() << "Serializing definition for" << source->m_api->typeName(); |
646 | #endif |
647 | |
648 | //Now copy the property data |
649 | const int = api->enumCount(); |
650 | const auto metaObject = source->m_object->metaObject(); |
651 | ds << quint32(numEnums); //Number of Enums |
652 | #ifdef QTRO_VERBOSE_PROTOCOL |
653 | qDebug() << " Found" << numEnums << "enumeration types" ; |
654 | #endif |
655 | for (int i = 0; i < numEnums; ++i) { |
656 | auto enumerator = metaObject->enumerator(index: api->sourceEnumIndex(index: i)); |
657 | Q_ASSERT(enumerator.isValid()); |
658 | serializeEnum(ds, enumerator); |
659 | } |
660 | |
661 | QSet<const QMetaObject *> gadgets; |
662 | QSet<QMetaEnum> enums; |
663 | recurseForGadgets(gadgets, enums, source); |
664 | serializeGadgets(ds, gadgets, enums, source); |
665 | |
666 | const int numSignals = api->signalCount(); |
667 | ds << quint32(numSignals); //Number of signals |
668 | for (int i = 0; i < numSignals; ++i) { |
669 | const int index = api->sourceSignalIndex(index: i); |
670 | Q_ASSERT(index >= 0); |
671 | auto signature = api->signalSignature(index: i); |
672 | replace(signature); |
673 | const int count = api->signalParameterCount(index: i); |
674 | for (int pi = 0; pi < count; ++pi) { |
675 | const auto metaType = QMetaType(api->signalParameterType(sigIndex: i, paramIndex: pi)); |
676 | if (isSequentialGadgetType(metaType)) |
677 | signature.replace(before: metaType.name(), after: "QtROSequentialContainer" ); |
678 | else if (isAssociativeGadgetType(metaType)) |
679 | signature.replace(before: metaType.name(), after: "QtROAssociativeContainer" ); |
680 | } |
681 | #ifdef QTRO_VERBOSE_PROTOCOL |
682 | qDebug() << " Signal" << i << "(signature =" << signature << "parameter names =" << api->signalParameterNames(i) << ")" ; |
683 | #endif |
684 | ds << signature; |
685 | ds << api->signalParameterNames(index: i); |
686 | } |
687 | |
688 | const int numMethods = api->methodCount(); |
689 | ds << quint32(numMethods); //Number of methods |
690 | for (int i = 0; i < numMethods; ++i) { |
691 | const int index = api->sourceMethodIndex(index: i); |
692 | Q_ASSERT(index >= 0); |
693 | auto signature = api->methodSignature(index: i); |
694 | replace(signature); |
695 | const int count = api->methodParameterCount(index: i); |
696 | for (int pi = 0; pi < count; ++pi) { |
697 | const auto metaType = QMetaType(api->methodParameterType(methodIndex: i, paramIndex: pi)); |
698 | if (isSequentialGadgetType(metaType)) |
699 | signature.replace(before: metaType.name(), after: "QtROSequentialContainer" ); |
700 | else if (isAssociativeGadgetType(metaType)) |
701 | signature.replace(before: metaType.name(), after: "QtROAssociativeContainer" ); |
702 | } |
703 | auto typeName = api->typeName(index: i); |
704 | replace(typeName); |
705 | #ifdef QTRO_VERBOSE_PROTOCOL |
706 | qDebug() << " Slot" << i << "(signature =" << signature << "parameter names =" << api->methodParameterNames(i) << "return type =" << typeName << ")" ; |
707 | #endif |
708 | ds << signature; |
709 | ds << typeName; |
710 | ds << api->methodParameterNames(index: i); |
711 | } |
712 | |
713 | const int numProperties = api->propertyCount(); |
714 | ds << quint32(numProperties); //Number of properties |
715 | for (int i = 0; i < numProperties; ++i) { |
716 | const int index = api->sourcePropertyIndex(index: i); |
717 | Q_ASSERT(index >= 0); |
718 | |
719 | const auto target = api->isAdapterProperty(i) ? source->m_adapter : source->m_object; |
720 | const auto metaProperty = target->metaObject()->property(index); |
721 | ds << metaProperty.name(); |
722 | #ifdef QTRO_VERBOSE_PROTOCOL |
723 | qDebug() << " Property" << i << "name =" << metaProperty.name(); |
724 | #endif |
725 | if (metaProperty.metaType().flags().testFlag(flag: QMetaType::PointerToQObject)) { |
726 | auto objectType = getObjectType(typeName: QLatin1String(metaProperty.typeName())); |
727 | ds << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*" ); |
728 | #ifdef QTRO_VERBOSE_PROTOCOL |
729 | qDebug() << " Type:" << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*" ); |
730 | #endif |
731 | } else { |
732 | if (isSequentialGadgetType(metaType: metaProperty.metaType())) { |
733 | ds << "QtROSequentialContainer" ; |
734 | #ifdef QTRO_VERBOSE_PROTOCOL |
735 | qDebug() << " Type:" << "QtROSequentialContainer" ; |
736 | #endif |
737 | } else if (isAssociativeGadgetType(metaType: metaProperty.metaType())) { |
738 | ds << "QtROAssociativeContainer" ; |
739 | #ifdef QTRO_VERBOSE_PROTOCOL |
740 | qDebug() << " Type:" << "QtROAssociativeContainer" ; |
741 | #endif |
742 | } else { |
743 | ds << metaProperty.typeName(); |
744 | #ifdef QTRO_VERBOSE_PROTOCOL |
745 | qDebug() << " Type:" << metaProperty.typeName(); |
746 | #endif |
747 | } |
748 | } |
749 | if (metaProperty.notifySignalIndex() == -1) { |
750 | ds << QByteArray(); |
751 | #ifdef QTRO_VERBOSE_PROTOCOL |
752 | qDebug() << " Notification signal: None" ; |
753 | #endif |
754 | } else { |
755 | auto signature = metaProperty.notifySignal().methodSignature(); |
756 | replace(signature); |
757 | ds << signature; |
758 | #ifdef QTRO_VERBOSE_PROTOCOL |
759 | qDebug() << " Notification signal:" << signature; |
760 | #endif |
761 | } |
762 | } |
763 | } |
764 | |
765 | void QDataStreamCodec::serializeAddObjectPacket(const QString &name, bool isDynamic) |
766 | { |
767 | m_packet.setId(AddObject); |
768 | m_packet << name; |
769 | m_packet << isDynamic; |
770 | m_packet.finishPacket(); |
771 | } |
772 | |
773 | void QDataStreamCodec::deserializeAddObjectPacket(QDataStream &ds, bool &isDynamic) |
774 | { |
775 | ds >> isDynamic; |
776 | } |
777 | |
778 | void QDataStreamCodec::serializeRemoveObjectPacket(const QString &name) |
779 | { |
780 | m_packet.setId(RemoveObject); |
781 | m_packet << name; |
782 | m_packet.finishPacket(); |
783 | } |
784 | //There is no deserializeRemoveObjectPacket - no parameters other than id and name |
785 | |
786 | void QDataStreamCodec::serializeInvokePacket(const QString &name, int call, int index, const QVariantList &args, int serialId, int propertyIndex) |
787 | { |
788 | m_packet.setId(InvokePacket); |
789 | m_packet << name; |
790 | m_packet << call; |
791 | m_packet << index; |
792 | |
793 | m_packet << quint32(args.size()); |
794 | for (const auto &arg : args) |
795 | m_packet << encodeVariant(value: arg); |
796 | |
797 | m_packet << serialId; |
798 | m_packet << propertyIndex; |
799 | m_packet.finishPacket(); |
800 | } |
801 | |
802 | void QDataStreamCodec::deserializeInvokePacket(QDataStream& in, int &call, int &index, QVariantList &args, int &serialId, int &propertyIndex) |
803 | { |
804 | in >> call; |
805 | in >> index; |
806 | const bool success = deserializeQVariantList(s&: in, l&: args); |
807 | Q_ASSERT(success); |
808 | Q_UNUSED(success) |
809 | in >> serialId; |
810 | in >> propertyIndex; |
811 | } |
812 | |
813 | void QDataStreamCodec::serializeInvokeReplyPacket(const QString &name, int ackedSerialId, const QVariant &value) |
814 | { |
815 | m_packet.setId(InvokeReplyPacket); |
816 | m_packet << name; |
817 | m_packet << ackedSerialId; |
818 | m_packet << value; |
819 | m_packet.finishPacket(); |
820 | } |
821 | |
822 | void QDataStreamCodec::deserializeInvokeReplyPacket(QDataStream& in, int &ackedSerialId, QVariant &value){ |
823 | in >> ackedSerialId; |
824 | in >> value; |
825 | } |
826 | |
827 | void QDataStreamCodec::serializePropertyChangePacket(QRemoteObjectSourceBase *source, int signalIndex) |
828 | { |
829 | int internalIndex = source->m_api->propertyRawIndexFromSignal(index: signalIndex); |
830 | m_packet.setId(PropertyChangePacket); |
831 | m_packet << source->name(); |
832 | m_packet << internalIndex; |
833 | serializeProperty(source, internalIndex); |
834 | m_packet.finishPacket(); |
835 | } |
836 | |
837 | void QDataStreamCodec::deserializePropertyChangePacket(QDataStream& in, int &index, QVariant &value) |
838 | { |
839 | in >> index; |
840 | in >> value; |
841 | } |
842 | |
843 | void QDataStreamCodec::serializeObjectListPacket(const ObjectInfoList &objects) |
844 | { |
845 | m_packet.setId(ObjectList); |
846 | m_packet << objects; |
847 | m_packet.finishPacket(); |
848 | } |
849 | |
850 | void QDataStreamCodec::deserializeObjectListPacket(QDataStream &in, ObjectInfoList &objects) |
851 | { |
852 | in >> objects; |
853 | } |
854 | |
855 | void QDataStreamCodec::serializePingPacket(const QString &name) |
856 | { |
857 | m_packet.setId(Ping); |
858 | m_packet << name; |
859 | m_packet.finishPacket(); |
860 | } |
861 | |
862 | void QDataStreamCodec::serializePongPacket(const QString &name) |
863 | { |
864 | m_packet.setId(Pong); |
865 | m_packet << name; |
866 | m_packet.finishPacket(); |
867 | } |
868 | |
869 | QRO_::QRO_(QRemoteObjectSourceBase *source) |
870 | : name(source->name()) |
871 | , typeName(source->m_api->typeName()) |
872 | , type(source->m_adapter ? ObjectType::MODEL : getObjectType(typeName)) |
873 | , isNull(source->m_object == nullptr) |
874 | , classDefinition() |
875 | , parameters() |
876 | {} |
877 | |
878 | QRO_::QRO_(const QVariant &value) |
879 | : type(ObjectType::GADGET) |
880 | , isNull(false) |
881 | { |
882 | const auto metaType = value.metaType(); |
883 | auto meta = metaType.metaObject(); |
884 | QDataStream out(&classDefinition, QIODevice::WriteOnly); |
885 | const int numProperties = meta->propertyCount(); |
886 | const auto name = metaType.name(); |
887 | const auto typeName = QByteArray::fromRawData(data: name, size: qsizetype(qstrlen(str: name))); |
888 | out << quint32(0) << quint32(1); |
889 | out << typeName; |
890 | out << numProperties; |
891 | #ifdef QTRO_VERBOSE_PROTOCOL |
892 | qDebug("Serializing POD definition to QRO_ (name = %s)" , typeName.constData()); |
893 | #endif |
894 | for (int i = 0; i < numProperties; ++i) { |
895 | const auto property = meta->property(index: i); |
896 | #ifdef QTRO_VERBOSE_PROTOCOL |
897 | qDebug(" Data member %d (name = %s, type = %s):" , i, property.name(), property.typeName()); |
898 | #endif |
899 | out << QByteArray::fromRawData(data: property.name(), size: qsizetype(qstrlen(str: property.name()))); |
900 | out << QByteArray::fromRawData(data: property.typeName(), size: qsizetype(qstrlen(str: property.typeName()))); |
901 | } |
902 | int enumCount = meta->enumeratorCount() - meta->enumeratorOffset(); |
903 | out << quint32(enumCount); |
904 | for (int j = meta->enumeratorOffset(); j < meta->enumeratorCount(); j++) { |
905 | auto const enumMeta = meta->enumerator(index: j); |
906 | serializeEnum(ds&: out, enumerator: enumMeta); |
907 | } |
908 | QDataStream ds(¶meters, QIODevice::WriteOnly); |
909 | ds << value; |
910 | #ifdef QTRO_VERBOSE_PROTOCOL |
911 | qDebug() << " Value:" << value; |
912 | #endif |
913 | } |
914 | |
915 | QDataStream &operator<<(QDataStream &stream, const QRO_ &info) |
916 | { |
917 | stream << info.name << info.typeName << quint8(info.type) << info.classDefinition << info.isNull; |
918 | qCDebug(QT_REMOTEOBJECT) << "Serializing " << info; |
919 | // info.parameters will be filled in by serializeProperty |
920 | return stream; |
921 | } |
922 | |
923 | QDataStream &operator>>(QDataStream &stream, QRO_ &info) |
924 | { |
925 | quint8 tmpType; |
926 | stream >> info.name >> info.typeName >> tmpType >> info.classDefinition >> info.isNull; |
927 | info.type = static_cast<ObjectType>(tmpType); |
928 | qCDebug(QT_REMOTEOBJECT) << "Deserializing " << info; |
929 | if (!info.isNull) |
930 | stream >> info.parameters; |
931 | return stream; |
932 | } |
933 | |
934 | QSQ_::QSQ_(const QVariant &variant) |
935 | { |
936 | QSequentialIterable sequence; |
937 | QMetaType valueType; |
938 | if (variant.metaType() == QMetaType::fromType<QtROSequentialContainer>()) { |
939 | auto container = static_cast<const QtROSequentialContainer *>(variant.constData()); |
940 | typeName = container->m_typeName; |
941 | valueType = container->m_valueType; |
942 | valueTypeName = container->m_valueTypeName; |
943 | sequence = QSequentialIterable(reinterpret_cast<const QVariantList *>(variant.constData())); |
944 | } else { |
945 | sequence = variant.value<QSequentialIterable>(); |
946 | typeName = QByteArray(variant.metaType().name()); |
947 | valueType = sequence.metaContainer().valueMetaType(); |
948 | valueTypeName = QByteArray(valueType.name()); |
949 | } |
950 | #ifdef QTRO_VERBOSE_PROTOCOL |
951 | qDebug("Serializing POD sequence to QSQ_ (type = %s, valueType = %s) with size = %lld" , |
952 | typeName.constData(), valueTypeName.constData(), sequence.size()); |
953 | #endif |
954 | QDataStream ds(&values, QIODevice::WriteOnly); |
955 | ds << valueTypeName; |
956 | auto pos = ds.device()->pos(); |
957 | ds << quint32(sequence.size()); |
958 | for (const auto &v : sequence) { |
959 | if (!valueType.save(stream&: ds, data: v.data())) { |
960 | ds.device()->seek(pos); |
961 | ds.resetStatus(); |
962 | ds << quint32(0); |
963 | values.resize(size: ds.device()->pos()); |
964 | qWarning(msg: "QSQ_: unable to save type '%s', sending empty list." , valueType.name()); |
965 | break; |
966 | } |
967 | } |
968 | } |
969 | |
970 | QDataStream &operator<<(QDataStream &stream, const QSQ_ &sequence) |
971 | { |
972 | stream << sequence.typeName << sequence.valueTypeName << sequence.values; |
973 | qCDebug(QT_REMOTEOBJECT) << "Serializing " << sequence; |
974 | return stream; |
975 | } |
976 | |
977 | QDataStream &operator>>(QDataStream &stream, QSQ_ &sequence) |
978 | { |
979 | stream >> sequence.typeName >> sequence.valueTypeName >> sequence.values; |
980 | qCDebug(QT_REMOTEOBJECT) << "Deserializing " << sequence; |
981 | return stream; |
982 | } |
983 | |
984 | QAS_::QAS_(const QVariant &variant) |
985 | { |
986 | QAssociativeIterable map; |
987 | QMetaType keyType, transferType, valueType; |
988 | const QtROAssociativeContainer *container = nullptr; |
989 | if (variant.metaType() == QMetaType::fromType<QtROAssociativeContainer>()) { |
990 | container = static_cast<const QtROAssociativeContainer *>(variant.constData()); |
991 | typeName = container->m_typeName; |
992 | keyType = container->m_keyType; |
993 | keyTypeName = container->m_keyTypeName; |
994 | valueType = container->m_valueType; |
995 | valueTypeName = container->m_valueTypeName; |
996 | map = QAssociativeIterable(reinterpret_cast<const QVariantMap *>(variant.constData())); |
997 | } else { |
998 | map = variant.value<QAssociativeIterable>(); |
999 | typeName = QByteArray(variant.metaType().name()); |
1000 | keyType = map.metaContainer().keyMetaType(); |
1001 | keyTypeName = QByteArray(keyType.name()); |
1002 | valueType = map.metaContainer().mappedMetaType(); |
1003 | valueTypeName = QByteArray(valueType.name()); |
1004 | } |
1005 | // Special handling for enums... |
1006 | transferType = keyType; |
1007 | if (keyType.flags().testFlag(flag: QMetaType::IsEnumeration)) { |
1008 | transferType = transferTypeForEnum(enumType: keyType); |
1009 | auto meta = keyType.metaObject(); |
1010 | // If we are the source, make sure the typeName is converted for any downstream replicas |
1011 | // the `meta` variable will be non-null when the enum is a class enum |
1012 | if (meta && !container) { |
1013 | const int ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); |
1014 | if (ind >= 0) { |
1015 | bool isFlag = keyTypeName.startsWith(bv: "QFlags<" ); |
1016 | if (isFlag || keyTypeName.startsWith(bv: meta->className())) { |
1017 | #ifdef QTRO_VERBOSE_PROTOCOL |
1018 | QByteArray orig(keyTypeName); |
1019 | #endif |
1020 | if (isFlag) { |
1021 | // Q_DECLARE_FLAGS(Flags, Enum) -> `typedef QFlags<Enum> Flags;` |
1022 | // We know we have an enum for `Flags` because we sent the enums |
1023 | // from the source, so we just need to look up the alias. |
1024 | keyTypeName = keyTypeName.mid(index: 7); |
1025 | keyTypeName.chop(n: 1); // Remove trailing '>' |
1026 | int index = keyTypeName.lastIndexOf(c: ':'); |
1027 | for (int i = meta->enumeratorOffset(); i < meta->enumeratorCount(); i++) { |
1028 | auto en = meta->enumerator(index: i); |
1029 | auto name = keyTypeName.data() + index + 1; |
1030 | if (en.isFlag() && qstrcmp(str1: en.enumName(), str2: name) == 0) |
1031 | keyTypeName.replace(index: index + 1, len: qstrlen(str: en.enumName()), s: en.name()); |
1032 | } |
1033 | } |
1034 | keyTypeName.replace(before: meta->className(), after: meta->classInfo(index: ind).value()); |
1035 | QByteArray repName(meta->classInfo(index: ind).value()); |
1036 | repName.append(s: "Replica" ); |
1037 | typeName.replace(before: meta->className(), after: repName); |
1038 | #ifdef QTRO_VERBOSE_PROTOCOL |
1039 | qDebug() << "Converted map key typename from" << orig << "to" << keyTypeName; |
1040 | #endif |
1041 | } |
1042 | } |
1043 | } |
1044 | } |
1045 | |
1046 | #ifdef QTRO_VERBOSE_PROTOCOL |
1047 | qDebug("Serializing POD map to QAS_ (type = %s, keyType = %s, valueType = %s), size = %lld" , |
1048 | typeName.constData(), keyTypeName.constData(), valueTypeName.constData(), |
1049 | map.size()); |
1050 | #endif |
1051 | QDataStream ds(&values, QIODevice::WriteOnly); |
1052 | ds << keyTypeName; |
1053 | ds << valueTypeName; |
1054 | auto pos = ds.device()->pos(); |
1055 | ds << quint32(map.size()); |
1056 | QAssociativeIterable::const_iterator iter = map.begin(); |
1057 | for (int i = 0; i < map.size(); i++) { |
1058 | QVariant key(container ? container->m_keys.at(i) : iter.key()); |
1059 | if (transferType != keyType) |
1060 | key.convert(type: transferType); |
1061 | if (!transferType.save(stream&: ds, data: key.data())) { |
1062 | ds.device()->seek(pos); |
1063 | ds.resetStatus(); |
1064 | ds << quint32(0); |
1065 | values.resize(size: ds.device()->pos()); |
1066 | qWarning(msg: "QAS_: unable to save key '%s', sending empty map." , keyType.name()); |
1067 | break; |
1068 | } |
1069 | if (!valueType.save(stream&: ds, data: iter.value().data())) { |
1070 | ds.device()->seek(pos); |
1071 | ds.resetStatus(); |
1072 | ds << quint32(0); |
1073 | values.resize(size: ds.device()->pos()); |
1074 | qWarning(msg: "QAS_: unable to save value '%s', sending empty map." , valueType.name()); |
1075 | break; |
1076 | } |
1077 | iter++; |
1078 | } |
1079 | } |
1080 | |
1081 | QDataStream &operator<<(QDataStream &stream, const QAS_ &map) |
1082 | { |
1083 | stream << map.typeName << map.keyTypeName << map.valueTypeName << map.values; |
1084 | qCDebug(QT_REMOTEOBJECT) << "Serializing " << map; |
1085 | return stream; |
1086 | } |
1087 | |
1088 | QDataStream &operator>>(QDataStream &stream, QAS_ &map) |
1089 | { |
1090 | stream >> map.typeName >> map.keyTypeName >> map.valueTypeName >> map.values; |
1091 | qCDebug(QT_REMOTEOBJECT) << "Deserializing " << map; |
1092 | return stream; |
1093 | } |
1094 | |
1095 | DataStreamPacket::DataStreamPacket(quint16 id) |
1096 | : QDataStream(&array, QIODevice::WriteOnly), baseAddress(0), size(0) |
1097 | { |
1098 | this->setVersion(QtRemoteObjects::dataStreamVersion); |
1099 | this->setByteOrder(QDataStream::LittleEndian); |
1100 | *this << quint32(0); |
1101 | *this << id; |
1102 | } |
1103 | |
1104 | void CodecBase::send(const QSet<QtROIoDeviceBase *> &connections) |
1105 | { |
1106 | const auto bytearray = getPayload(); |
1107 | for (auto conn : connections) |
1108 | conn->write(data: bytearray); |
1109 | reset(); |
1110 | } |
1111 | |
1112 | void CodecBase::send(const QList<QtROIoDeviceBase *> &connections) |
1113 | { |
1114 | const auto bytearray = getPayload(); |
1115 | for (auto conn : connections) |
1116 | conn->write(data: bytearray); |
1117 | reset(); |
1118 | } |
1119 | |
1120 | void CodecBase::send(QtROIoDeviceBase *connection) |
1121 | { |
1122 | const auto bytearray = getPayload(); |
1123 | connection->write(data: bytearray); |
1124 | reset(); |
1125 | } |
1126 | |
1127 | } // namespace QRemoteObjectPackets |
1128 | |
1129 | QT_IMPL_METATYPE_EXTERN_TAGGED(QRemoteObjectPackets::QRO_, QRemoteObjectPackets__QRO_) |
1130 | |
1131 | QT_END_NAMESPACE |
1132 | |