blob: d5177942e8ed1f042615ff2135dc6adf2be899d2 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 Ford Motor Company
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtRemoteObjects module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qremoteobjectpacket_p.h"
#include <QtCore/qabstractitemmodel.h>
#include "qremoteobjectpendingcall.h"
#include "qremoteobjectsource.h"
#include "qremoteobjectsource_p.h"
#include <cstring>
//#define QTRO_VERBOSE_PROTOCOL
QT_BEGIN_NAMESPACE
// Add methods so we can use QMetaEnum in a set
// Note for both functions we are skipping string comparisons/hashes. Since the
// metaObjects are the same, we can just use the address of the string.
inline bool operator==(const QMetaEnum e1, const QMetaEnum e2)
{
return e1.enclosingMetaObject() == e2.enclosingMetaObject()
&& e1.name() == e2.name()
&& e1.enumName() == e2.enumName()
&& e1.scope() == e2.scope();
}
inline uint qHash(const QMetaEnum &key, uint seed=0) Q_DECL_NOTHROW
{
return qHash(key.enclosingMetaObject(), seed) ^ qHash(static_cast<const void *>(key.name()), seed)
^ qHash(static_cast<const void *>(key.enumName()), seed) ^ qHash(static_cast<const void *>(key.scope()), seed);
}
using namespace QtRemoteObjects;
namespace QRemoteObjectPackets {
// QDataStream sends QVariants of custom types by sending their typename, allowing decode
// on the receiving side. For QtRO and enums, this won't work, as the enums have different
// scopes. E.g., the examples have ParentClassSource::MyEnum and ParentClassReplica::MyEnum.
// Dynamic types will be created as ParentClass::MyEnum. So instead, we change the variants
// to integers (encodeVariant) when sending them. On the receive side, the we know the
// types of properties and the signatures for methods, so we can use that information to
// decode the integer variant into an enum variant (via decodeVariant).
const QVariant encodeVariant(const QVariant &value)
{
if (QMetaType::typeFlags(value.userType()).testFlag(QMetaType::IsEnumeration)) {
auto converted = QVariant(value);
const auto size = QMetaType(value.userType()).sizeOf();
switch (size) {
case 1: converted.convert(QMetaType::Char); break;
case 2: converted.convert(QMetaType::Short); break;
case 4: converted.convert(QMetaType::Int); break;
// Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
// case 8: converted.convert(QMetaType::Long); break; // typeId for long from qmetatype.h
default:
qWarning() << "Invalid enum detected" << QMetaType::typeName(value.userType()) << "with size" << size;
converted.convert(QMetaType::Int);
}
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << "Converting from enum to integer type" << size << converted << value;
#endif
return converted;
}
return value;
}
QVariant &decodeVariant(QVariant &value, int type)
{
if (QMetaType::typeFlags(type).testFlag(QMetaType::IsEnumeration)) {
#ifdef QTRO_VERBOSE_PROTOCOL
QVariant encoded(value);
#endif
value.convert(type);
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << "Converting to enum from integer type" << value << encoded;
#endif
}
return value;
}
void serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, int internalIndex)
{
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 (QMetaType::typeFlags(property.userType()).testFlag(QMetaType::PointerToQObject)) {
auto const childSource = source->m_children.value(internalIndex);
auto valueAsPointerToQObject = qvariant_cast<QObject *>(value);
if (childSource->m_object != valueAsPointerToQObject)
childSource->resetObject(valueAsPointerToQObject);
QRO_ qro(childSource);
if (source->d->isDynamic && qro.type == ObjectType::CLASS && childSource->m_object && !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();
// Put the properties in a buffer, the receiver may not know how to
// interpret the types until it registers new ones.
QDataStream params(&qro.parameters, QIODevice::WriteOnly);
params << propertyCount;
for (int internalIndex = 0; internalIndex < propertyCount; ++internalIndex)
serializeProperty(params, childSource, internalIndex);
ds << qro.parameters;
return;
}
if (source->d->isDynamic && property.userType() == QMetaType::QVariant &&
QMetaType::typeFlags(value.userType()).testFlag(QMetaType::IsGadget)) {
const auto typeName = QString::fromLatin1(QMetaType::typeName(value.userType()));
if (!source->d->sentTypes.contains(typeName)) {
QRO_ qro(value);
ds << QVariant::fromValue<QRO_>(qro);
ds << qro.parameters;
source->d->sentTypes.insert(typeName);
return;
}
}
ds << encodeVariant(value);
}
void serializeHandshakePacket(DataStreamPacket &ds)
{
ds.setId(Handshake);
ds << QString(protocolVersion);
ds.finishPacket();
}
void serializeInitPacket(DataStreamPacket &ds, const QRemoteObjectRootSource *source)
{
ds.setId(InitPacket);
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 internalIndex = 0; internalIndex < numProperties; ++internalIndex)
serializeProperty(ds, source, internalIndex);
}
bool deserializeQVariantList(QDataStream &s, QList<QVariant> &l)
{
// note: optimized version of: QDataStream operator>>(QDataStream& s, QList<T>& l)
quint32 c;
s >> c;
const int initialListSize = l.size();
if (static_cast<quint32>(l.size()) < c)
l.reserve(c);
else if (static_cast<quint32>(l.size()) > c)
for (int i = c; i < initialListSize; ++i)
l.removeLast();
for (int i = 0; i < l.size(); ++i)
{
if (s.atEnd())
return false;
QVariant t;
s >> t;
l[i] = t;
}
for (quint32 i = l.size(); i < c; ++i)
{
if (s.atEnd())
return false;
QVariant t;
s >> t;
l.append(t);
}
return true;
}
void deserializeInitPacket(QDataStream &in, QVariantList &values)
{
const bool success = deserializeQVariantList(in, values);
Q_ASSERT(success);
Q_UNUSED(success);
}
void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectRootSource *source)
{
ds.setId(InitDynamicPacket);
ds << source->name();
serializeDefinition(ds, source);
serializeProperties(ds, source);
ds.finishPacket();
}
static ObjectType getObjectType(const QString &typeName)
{
if (typeName == QLatin1String("QAbstractItemModelAdapter"))
return ObjectType::MODEL;
auto tid = QMetaType::type(typeName.toUtf8());
if (tid == QMetaType::UnknownType)
return ObjectType::CLASS;
QMetaType type(tid);
auto mo = type.metaObject();
if (mo && mo->inherits(&QAbstractItemModel::staticMetaObject))
return ObjectType::MODEL;
return ObjectType::CLASS;
}
// Same method as in QVariant.cpp, as it isn't publicly exposed...
static QMetaEnum metaEnumFromType(int type)
{
QMetaType t(type);
if (t.flags() & QMetaType::IsEnumeration) {
if (const QMetaObject *metaObject = t.metaObject()) {
const char *enumName = QMetaType::typeName(type);
const char *lastColon = std::strrchr(enumName, ':');
if (lastColon)
enumName = lastColon + 1;
return metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
}
}
return QMetaEnum();
}
static bool checkEnum(int type, QSet<QMetaEnum> &enums)
{
if (QMetaType::typeFlags(type).testFlag(QMetaType::IsEnumeration)) {
QMetaEnum meta = metaEnumFromType(type);
enums.insert(meta);
return true;
}
return false;
}
static void recurseMetaobject(const QMetaObject *mo, QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums)
{
if (!mo || gadgets.contains(mo))
return;
gadgets.insert(mo);
const int numProperties = mo->propertyCount();
for (int i = 0; i < numProperties; ++i) {
const auto property = mo->property(i);
if (checkEnum(property.userType(), enums))
continue;
if (QMetaType::typeFlags(property.userType()).testFlag(QMetaType::IsGadget))
recurseMetaobject(QMetaType::metaObjectForType(property.userType()), gadgets, enums);
}
}
// A Source may only use a subset of the metaobjects properties/signals/slots, so we only search
// the ones in the API. For nested pointer types, we will have another api to limit the search.
// For nested PODs/enums, we search the entire qobject (using the recurseMetaobject call()).
void recurseForGadgets(QSet<const QMetaObject *> &gadgets, QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source)
{
const SourceApiMap *api = source->m_api;
const int numSignals = api->signalCount();
const int numMethods = api->methodCount();
const int numProperties = api->propertyCount();
for (int si = 0; si < numSignals; ++si) {
const int params = api->signalParameterCount(si);
for (int pi = 0; pi < params; ++pi) {
const int type = api->signalParameterType(si, pi);
if (checkEnum(type, enums))
continue;
if (!QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
continue;
const auto mo = QMetaType::metaObjectForType(type);
if (source->d->sentTypes.contains(QLatin1String(mo->className())))
continue;
recurseMetaobject(mo, gadgets, enums);
source->d->sentTypes.insert(QLatin1String(mo->className()));
}
}
for (int mi = 0; mi < numMethods; ++mi) {
const int params = api->methodParameterCount(mi);
for (int pi = 0; pi < params; ++pi) {
const int type = api->methodParameterType(mi, pi);
if (checkEnum(type, enums))
continue;
if (!QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
continue;
const auto mo = QMetaType::metaObjectForType(type);
if (source->d->sentTypes.contains(QLatin1String(mo->className())))
continue;
recurseMetaobject(mo, gadgets, enums);
source->d->sentTypes.insert(QLatin1String(mo->className()));
}
}
for (int pi = 0; pi < numProperties; ++pi) {
const int index = api->sourcePropertyIndex(pi);
Q_ASSERT(index >= 0);
const auto target = api->isAdapterProperty(pi) ? source->m_adapter : source->m_object;
const auto metaProperty = target->metaObject()->property(index);
const int type = metaProperty.userType();
if (checkEnum(type, enums))
continue;
if (QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject)) {
auto const objectType = getObjectType(QString::fromLatin1(metaProperty.typeName()));
if (objectType == ObjectType::CLASS) {
auto const childSource = source->m_children.value(pi);
if (childSource->m_object)
recurseForGadgets(gadgets, enums, childSource);
}
}
if (!QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
continue;
const auto mo = QMetaType::metaObjectForType(type);
if (source->d->sentTypes.contains(QLatin1String(mo->className())))
continue;
recurseMetaobject(mo, gadgets, enums);
source->d->sentTypes.insert(QLatin1String(mo->className()));
}
}
static bool checkForEnumsInSource(const QMetaObject *meta, const QRemoteObjectSourceBase *source)
{
if (source->m_object->inherits(meta->className()))
return true;
for (const auto child : source->m_children) {
if (child->m_object && checkForEnumsInSource(meta, child))
return true;
}
return false;
}
static void serializeEnum(QDataStream &ds, const QMetaEnum &enumerator)
{
ds << QByteArray::fromRawData(enumerator.name(), qstrlen(enumerator.name()));
ds << enumerator.isFlag();
ds << enumerator.isScoped();
const auto typeName = QByteArray(enumerator.scope()).append("::").append(enumerator.name());
quint32 size = QMetaType(QMetaType::type(typeName.constData())).sizeOf();
ds << size;
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug(" Enum (name = %s, size = %d, isFlag = %s, isScoped = %s):", enumerator.name(), size, enumerator.isFlag() ? "true" : "false", enumerator.isScoped() ? "true" : "false");
#endif
const int keyCount = enumerator.keyCount();
ds << keyCount;
for (int k = 0; k < keyCount; ++k) {
ds << QByteArray::fromRawData(enumerator.key(k), qstrlen(enumerator.key(k)));
ds << enumerator.value(k);
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug(" Key %d (name = %s, value = %d):", k, enumerator.key(k), enumerator.value(k));
#endif
}
}
static void serializeGadgets(QDataStream &ds, const QSet<const QMetaObject *> &gadgets, const QSet<QMetaEnum> &enums, const QRemoteObjectSourceBase *source=nullptr)
{
// Determine how to handle the enums found
QSet<QMetaEnum> qtEnums;
QSet<const QMetaObject *> dynamicEnumMetaObjects;
for (const auto metaEnum : enums) {
auto const metaObject = metaEnum.enclosingMetaObject();
if (gadgets.contains(metaObject)) // Part of a gadget will we serialize
continue;
// This checks if the enum is defined in our object heirarchy, in which case it will
// already have been serialized.
if (source && checkForEnumsInSource(metaObject, source->d->root))
continue;
// qtEnums are enumerations already known by Qt, so we only need register them.
// We don't need to send all of the key/value data.
if (metaObject == qt_getQtMetaObject()) // Are the other Qt metaclasses for enums?
qtEnums.insert(metaEnum);
else
dynamicEnumMetaObjects.insert(metaEnum.enclosingMetaObject());
}
ds << quint32(qtEnums.size());
for (const auto metaEnum : qtEnums) {
QByteArray enumName(metaEnum.scope());
enumName.append("::", 2).append(metaEnum.name());
ds << enumName;
}
const auto allMetaObjects = gadgets + dynamicEnumMetaObjects;
ds << quint32(allMetaObjects.size());
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Found" << gadgets.size() << "gadget/pod and" << (allMetaObjects.size() - gadgets.size()) << "enum types";
int i = 0;
#endif
// There isn't an easy way to update a metaobject incrementally, so we
// send all of the metaobject's enums, but no properties, when an external
// enum is requested.
for (auto const meta : allMetaObjects) {
ds << QByteArray::fromRawData(meta->className(), qstrlen(meta->className()));
int propertyCount = gadgets.contains(meta) ? meta->propertyCount() : 0;
ds << quint32(propertyCount);
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug(" Gadget %d (name = %s, # properties = %d, # enums = %d):", i++, meta->className(), propertyCount, meta->enumeratorCount());
#endif
for (int j = 0; j < propertyCount; j++) {
auto prop = meta->property(j);
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug(" Data member %d (name = %s, type = %s):", j, prop.name(), prop.typeName());
#endif
ds << QByteArray::fromRawData(prop.name(), qstrlen(prop.name()));
ds << QByteArray::fromRawData(prop.typeName(), qstrlen(prop.typeName()));
}
int enumCount = meta->enumeratorCount();
ds << enumCount;
for (int j = 0; j < enumCount; j++) {
auto const enumMeta = meta->enumerator(j);
serializeEnum(ds, enumMeta);
}
}
}
void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
{
const SourceApiMap *api = source->m_api;
const QByteArray desiredClassName(api->typeName().toLatin1());
const QByteArray originalClassName = api->className();
// The dynamic class will be called typeName on the receiving side of this definition
// However, there are types like enums that have the QObject's class name. Replace()
// will convert a parameter such as "ParentClassSource::MyEnum" to "ParentClass::MyEnum"
// so the type can be properly resolved and registered.
auto replace = [&originalClassName, &desiredClassName](QByteArray &name) {
name.replace(originalClassName, desiredClassName);
};
ds << source->m_api->typeName();
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << "Serializing definition for" << source->m_api->typeName();
#endif
//Now copy the property data
const int numEnums = api->enumCount();
const auto metaObject = source->m_object->metaObject();
ds << quint32(numEnums); //Number of Enums
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Found" << numEnums << "enumeration types";
#endif
for (int i = 0; i < numEnums; ++i) {
auto enumerator = metaObject->enumerator(api->sourceEnumIndex(i));
Q_ASSERT(enumerator.isValid());
serializeEnum(ds, enumerator);
}
if (source->d->isDynamic) {
QSet<const QMetaObject *> gadgets;
QSet<QMetaEnum> enums;
recurseForGadgets(gadgets, enums, source);
serializeGadgets(ds, gadgets, enums, source);
} else
ds << quint32(0) << quint32(0); // qtEnums, numGadgets
const int numSignals = api->signalCount();
ds << quint32(numSignals); //Number of signals
for (int i = 0; i < numSignals; ++i) {
const int index = api->sourceSignalIndex(i);
Q_ASSERT(index >= 0);
auto signature = api->signalSignature(i);
replace(signature);
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Signal" << i << "(signature =" << signature << "parameter names =" << api->signalParameterNames(i) << ")";
#endif
ds << signature;
ds << api->signalParameterNames(i);
}
const int numMethods = api->methodCount();
ds << quint32(numMethods); //Number of methods
for (int i = 0; i < numMethods; ++i) {
const int index = api->sourceMethodIndex(i);
Q_ASSERT(index >= 0);
auto signature = api->methodSignature(i);
replace(signature);
auto typeName = api->typeName(i);
replace(typeName);
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Slot" << i << "(signature =" << signature << "parameter names =" << api->methodParameterNames(i) << "return type =" << typeName << ")";
#endif
ds << signature;
ds << typeName;
ds << api->methodParameterNames(i);
}
const int numProperties = api->propertyCount();
ds << quint32(numProperties); //Number of properties
for (int i = 0; i < numProperties; ++i) {
const int index = api->sourcePropertyIndex(i);
Q_ASSERT(index >= 0);
const auto target = api->isAdapterProperty(i) ? source->m_adapter : source->m_object;
const auto metaProperty = target->metaObject()->property(index);
ds << metaProperty.name();
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Property" << i << "name =" << metaProperty.name();
#endif
if (QMetaType::typeFlags(metaProperty.userType()).testFlag(QMetaType::PointerToQObject)) {
auto objectType = getObjectType(QLatin1String(metaProperty.typeName()));
ds << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Type:" << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
#endif
} else {
ds << metaProperty.typeName();
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Type:" << metaProperty.typeName();
#endif
}
if (metaProperty.notifySignalIndex() == -1) {
ds << QByteArray();
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Notification signal: None";
#endif
} else {
auto signature = metaProperty.notifySignal().methodSignature();
replace(signature);
ds << signature;
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Notification signal:" << signature;
#endif
}
}
}
void serializeAddObjectPacket(DataStreamPacket &ds, const QString &name, bool isDynamic)
{
ds.setId(AddObject);
ds << name;
ds << isDynamic;
ds.finishPacket();
}
void deserializeAddObjectPacket(QDataStream &ds, bool &isDynamic)
{
ds >> isDynamic;
}
void serializeRemoveObjectPacket(DataStreamPacket &ds, const QString &name)
{
ds.setId(RemoveObject);
ds << name;
ds.finishPacket();
}
//There is no deserializeRemoveObjectPacket - no parameters other than id and name
void serializeInvokePacket(DataStreamPacket &ds, const QString &name, int call, int index, const QVariantList &args, int serialId, int propertyIndex)
{
ds.setId(InvokePacket);
ds << name;
ds << call;
ds << index;
ds << (quint32)args.size();
for (const auto &arg : args)
ds << encodeVariant(arg);
ds << serialId;
ds << propertyIndex;
ds.finishPacket();
}
void deserializeInvokePacket(QDataStream& in, int &call, int &index, QVariantList &args, int &serialId, int &propertyIndex)
{
in >> call;
in >> index;
const bool success = deserializeQVariantList(in, args);
Q_ASSERT(success);
Q_UNUSED(success);
in >> serialId;
in >> propertyIndex;
}
void serializeInvokeReplyPacket(DataStreamPacket &ds, const QString &name, int ackedSerialId, const QVariant &value)
{
ds.setId(InvokeReplyPacket);
ds << name;
ds << ackedSerialId;
ds << value;
ds.finishPacket();
}
void deserializeInvokeReplyPacket(QDataStream& in, int &ackedSerialId, QVariant &value){
in >> ackedSerialId;
in >> value;
}
void serializePropertyChangePacket(QRemoteObjectSourceBase *source, int signalIndex)
{
int internalIndex = source->m_api->propertyRawIndexFromSignal(signalIndex);
auto &ds = source->d->m_packet;
ds.setId(PropertyChangePacket);
ds << source->name();
ds << internalIndex;
serializeProperty(ds, source, internalIndex);
ds.finishPacket();
}
void deserializePropertyChangePacket(QDataStream& in, int &index, QVariant &value)
{
in >> index;
in >> value;
}
void serializeObjectListPacket(DataStreamPacket &ds, const ObjectInfoList &objects)
{
ds.setId(ObjectList);
ds << objects;
ds.finishPacket();
}
void deserializeObjectListPacket(QDataStream &in, ObjectInfoList &objects)
{
in >> objects;
}
void serializePingPacket(DataStreamPacket &ds, const QString &name)
{
ds.setId(Ping);
ds << name;
ds.finishPacket();
}
void serializePongPacket(DataStreamPacket &ds, const QString &name)
{
ds.setId(Pong);
ds << name;
ds.finishPacket();
}
QRO_::QRO_(QRemoteObjectSourceBase *source)
: name(source->name())
, typeName(source->m_api->typeName())
, type(source->m_adapter ? ObjectType::MODEL : getObjectType(typeName))
, isNull(source->m_object == nullptr)
, classDefinition()
, parameters()
{}
QRO_::QRO_(const QVariant &value)
: type(ObjectType::GADGET)
, isNull(false)
{
auto meta = QMetaType::metaObjectForType(value.userType());
QDataStream out(&classDefinition, QIODevice::WriteOnly);
const int numProperties = meta->propertyCount();
const auto typeName = QByteArray::fromRawData(QMetaType::typeName(value.userType()), qstrlen(QMetaType::typeName(value.userType())));
out << quint32(0) << quint32(1);
out << typeName;
out << numProperties;
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug("Serializing POD definition to QRO_ (name = %s)", typeName.constData());
#endif
for (int i = 0; i < numProperties; ++i) {
const auto property = meta->property(i);
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug(" Data member %d (name = %s, type = %s):", i, property.name(), property.typeName());
#endif
out << QByteArray::fromRawData(property.name(), qstrlen(property.name()));
out << QByteArray::fromRawData(property.typeName(), qstrlen(property.typeName()));
}
QDataStream ds(&parameters, QIODevice::WriteOnly);
ds << value;
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug() << " Value:" << value;
#endif
}
QDataStream &operator<<(QDataStream &stream, const QRO_ &info)
{
stream << info.name << info.typeName << (quint8)(info.type) << info.classDefinition << info.isNull;
qCDebug(QT_REMOTEOBJECT) << "Serializing " << info;
// info.parameters will be filled in by serializeProperty
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 " << info;
if (!info.isNull)
stream >> info.parameters;
return stream;
}
} // namespace QRemoteObjectPackets
QT_END_NAMESPACE