summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-06-02 21:01:24 +0200
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-06-02 21:01:24 +0200
commitc107dbf2aa7c5c603a23f543fe68c9c77ec822df (patch)
treedbcfd75e6427f71f49095b8e5066de0712b9b027
parentba171e01f90e9399ca4bfab0204b2932380ad64f (diff)
parentff04810bd09d3897ccede880680dd94fcf585171 (diff)
Merge remote-tracking branch 'origin/5.12' into 5.13
-rw-r--r--src/remoteobjects/qremoteobjectdynamicreplica.cpp20
-rw-r--r--src/remoteobjects/qremoteobjectnode.cpp247
-rw-r--r--src/remoteobjects/qremoteobjectpacket.cpp362
-rw-r--r--src/remoteobjects/qremoteobjectpacket_p.h20
-rw-r--r--src/remoteobjects/qremoteobjectreplica.cpp4
-rw-r--r--src/remoteobjects/qremoteobjectreplica_p.h2
-rw-r--r--src/remoteobjects/qremoteobjectsource.cpp6
-rw-r--r--src/remoteobjects/qremoteobjectsource.h79
-rw-r--r--src/remoteobjects/qremoteobjectsource_p.h4
-rw-r--r--src/remoteobjects/qremoteobjectsourceio.cpp13
-rw-r--r--tests/auto/integration/tst_integration.cpp5
-rw-r--r--tests/auto/integration_external/client/main.cpp4
-rw-r--r--tests/auto/integration_multiprocess/client/main.cpp4
-rw-r--r--tests/auto/proxy_multiprocess/client/client.pro3
-rw-r--r--tests/auto/proxy_multiprocess/client/main.cpp31
-rw-r--r--tests/auto/proxy_multiprocess/namespace.h15
-rw-r--r--tests/auto/proxy_multiprocess/proxy_multiprocess.pro3
-rw-r--r--tests/auto/proxy_multiprocess/server/main.cpp7
-rw-r--r--tests/auto/proxy_multiprocess/server/mytestserver.cpp14
-rw-r--r--tests/auto/proxy_multiprocess/server/mytestserver.h2
-rw-r--r--tests/auto/proxy_multiprocess/server/server.pro1
-rw-r--r--tests/auto/proxy_multiprocess/subclass.rep22
-rw-r--r--tools/repc/repcodegenerator.cpp27
23 files changed, 682 insertions, 213 deletions
diff --git a/src/remoteobjects/qremoteobjectdynamicreplica.cpp b/src/remoteobjects/qremoteobjectdynamicreplica.cpp
index 58b8778..a004593 100644
--- a/src/remoteobjects/qremoteobjectdynamicreplica.cpp
+++ b/src/remoteobjects/qremoteobjectdynamicreplica.cpp
@@ -171,6 +171,7 @@ int QRemoteObjectDynamicReplica::qt_metacall(QMetaObject::Call call, int id, voi
id = -1;
} else if (call == QMetaObject::InvokeMetaMethod) {
if (id < impl->m_numSignals) {
+ qCDebug(QT_REMOTEOBJECT) << "DynamicReplica Activate" << impl->m_metaObject->method(saved_id).methodSignature();
// signal relay from Source world to Replica
QMetaObject::activate(this, impl->m_metaObject, id, argv);
@@ -183,10 +184,21 @@ int QRemoteObjectDynamicReplica::qt_metacall(QMetaObject::Call call, int id, voi
QVariantList args;
args.reserve(typeSize);
for (int i = 0; i < typeSize; ++i) {
- if (impl->m_metaObject->indexOfEnumerator(types[i].constData()) != -1)
- args.push_back(QVariant(QMetaType::Int, argv[i + 1]));
- else
- args.push_back(QVariant(QMetaType::type(types[i].constData()), argv[i + 1]));
+ const int type = QMetaType::type(types[i].constData());
+ if (impl->m_metaObject->indexOfEnumerator(types[i].constData()) != -1) {
+ const auto size = QMetaType(type).sizeOf();
+ switch (size) {
+ case 1: args.push_back(QVariant(QMetaType::Char, argv[i + 1])); break;
+ case 2: args.push_back(QVariant(QMetaType::Short, argv[i + 1])); break;
+ case 4: args.push_back(QVariant(QMetaType::Int, argv[i + 1])); break;
+ // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
+// case 8: args.push_back(QVariant(QMetaType::Int, argv[i + 1])); break;
+ default:
+ qWarning() << "Invalid enum detected (Dynamic Replica)" << QMetaType::typeName(type) << "with size" << size;
+ args.push_back(QVariant(QMetaType::Int, argv[i + 1])); break;
+ }
+ } else
+ args.push_back(QVariant(type, argv[i + 1]));
}
if (debugArgs) {
diff --git a/src/remoteobjects/qremoteobjectnode.cpp b/src/remoteobjects/qremoteobjectnode.cpp
index d241e15..127ed9d 100644
--- a/src/remoteobjects/qremoteobjectnode.cpp
+++ b/src/remoteobjects/qremoteobjectnode.cpp
@@ -118,6 +118,42 @@ static void GadgetLoadOperator(QDataStream &in, void *data)
in >> prop;
}
+// Like the Q_GADGET static methods above, we need constructor/destructor methods
+// in order to use dynamically defined enums with QVariant or as signal/slot
+// parameters (i.e., the queued connection mechanism, which QtRO leverages).
+//
+// We will need the enum methods to support different sizes when typed scope enum
+// support is added, so might as well use that now.
+template<typename T>
+static void EnumDestructor(void *ptr)
+{
+ static_cast<T*>(ptr)->~T();
+}
+
+template<typename T>
+static void *EnumConstructor(void *where, const void *copy)
+{
+ T *ret = where ? new(where) T : new T;
+ if (copy)
+ *ret = *static_cast<const T*>(copy);
+ return ret;
+}
+
+// Not used, but keeping these in case we end up with a need for save/load.
+template<typename T>
+static void EnumSaveOperator(QDataStream & out, const void *data)
+{
+ const T value = *static_cast<const T *>(data);
+ out << value;
+}
+
+template<typename T>
+static void EnumLoadOperator(QDataStream &in, void *data)
+{
+ T value = *static_cast<T *>(data);
+ in >> value;
+}
+
static QString name(const QMetaObject * const mobj)
{
const int ind = mobj->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
@@ -659,9 +695,67 @@ static void trackConnection(int typeId, IoDeviceBase *connection)
QObject::connect(connection, &IoDeviceBase::destroyed, unregisterIfNotUsed);
}
-static int registerGadget(IoDeviceBase *connection, QRemoteObjectPackets::GadgetsData &gadgets, QByteArray typeName)
+struct EnumPair {
+ QByteArray name;
+ int value;
+};
+
+struct EnumData {
+ QByteArray name;
+ bool isFlag, isScoped;
+ quint32 keyCount, size;
+ QVector<EnumPair> values;
+};
+
+struct GadgetProperty {
+ QByteArray name;
+ QByteArray type;
+};
+
+struct GadgetData {
+ QVector<GadgetProperty> properties;
+ QVector<EnumData> enums;
+};
+
+using Gadgets = QHash<QByteArray, GadgetData>;
+
+static void registerEnum(const QByteArray &name, const QMetaObject *meta, int size=4)
+{
+ // When we add support for enum classes, we will need to set this to something like
+ // QByteArray(enumClass).append("::").append(enumMeta.name()) when enumMeta.isScoped() is true.
+ // That is a new feature, though.
+ if (QMetaType::isRegistered(QMetaType::type(name)))
+ return;
+ static const auto flags = QMetaType::IsEnumeration | QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
+ int id;
+ switch (size) {
+ case 1: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint8>,
+ &EnumConstructor<qint8>, size, flags, meta);
+ break;
+ case 2: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint16>,
+ &EnumConstructor<qint16>, size, flags, meta);
+ break;
+ case 4: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint32>,
+ &EnumConstructor<qint32>, size, flags, meta);
+ break;
+ // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
+// case 8: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint64>,
+// &EnumConstructor<qint64>, size, flags, meta);
+// break;
+ default:
+ qWarning() << "Invalid enum detected" << name << "with size" << size << ". Defaulting to register as int.";
+ id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint32>,
+ &EnumConstructor<qint32>, size, flags, meta);
+ }
+#ifdef QTRO_VERBOSE_PROTOCOL
+ qDebug() << "Registering new enum with id" << id << name << "size:" << size;
+#endif
+ qCDebug(QT_REMOTEOBJECT) << "Registering new enum with id" << id << name << "size:" << size;
+}
+
+static int registerGadgets(IoDeviceBase *connection, Gadgets &gadgets, QByteArray typeName)
{
- const auto &properties = gadgets.take(typeName);
+ const auto &gadget = gadgets.take(typeName);
int typeId = QMetaType::type(typeName);
if (typeId != QMetaType::UnknownType) {
trackConnection(typeId, connection);
@@ -672,52 +766,110 @@ static int registerGadget(IoDeviceBase *connection, QRemoteObjectPackets::Gadget
gadgetBuilder.setClassName(typeName);
gadgetBuilder.setFlags(QMetaObjectBuilder::DynamicMetaObject | QMetaObjectBuilder::PropertyAccessInStaticMetaCall);
GadgetType gadgetType;
- for (const auto &prop : properties) {
+ for (const auto &prop : gadget.properties) {
int propertyType = QMetaType::type(prop.type);
if (!propertyType && gadgets.contains(prop.type))
- propertyType = registerGadget(connection, gadgets, prop.type);
+ propertyType = registerGadgets(connection, gadgets, prop.type);
gadgetType.push_back(QVariant(QVariant::Type(propertyType)));
auto dynamicProperty = gadgetBuilder.addProperty(prop.name, prop.type);
dynamicProperty.setWritable(true);
dynamicProperty.setReadable(true);
}
+ for (const auto &enumData: gadget.enums) {
+ auto enumBuilder = gadgetBuilder.addEnumerator(enumData.name);
+ enumBuilder.setIsFlag(enumData.isFlag);
+ enumBuilder.setIsScoped(enumData.isScoped);
+
+ for (quint32 k = 0; k < enumData.keyCount; ++k) {
+ const auto pair = enumData.values.at(k);
+ enumBuilder.addKey(pair.name, pair.value);
+ }
+ }
auto meta = gadgetBuilder.toMetaObject();
- meta->d.static_metacall = &GadgetsStaticMetacallFunction;
- meta->d.superdata = nullptr;
- const auto flags = QMetaType::IsGadget | QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
- int gadgetTypeId = QMetaType::registerType(typeName.constData(),
+ const auto enumCount = meta->enumeratorCount();
+ for (int i = 0; i < enumCount; i++) {
+ const QByteArray registeredName = QByteArray(typeName).append("::").append(meta->enumerator(i).name());
+ registerEnum(registeredName, meta, gadget.enums.at(i).size);
+ }
+ QMetaType::TypeFlags flags = QMetaType::IsGadget;
+ int gadgetTypeId;
+ if (meta->propertyCount()) {
+ meta->d.static_metacall = &GadgetsStaticMetacallFunction;
+ meta->d.superdata = nullptr;
+ flags |= QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
+ gadgetTypeId = QMetaType::registerType(typeName.constData(),
&GadgetTypedDestructor,
&GadgetTypedConstructor,
sizeof(GadgetType),
flags, meta);
- QMetaType::registerStreamOperators(gadgetTypeId, &GadgetSaveOperator, &GadgetLoadOperator);
+ QMetaType::registerStreamOperators(gadgetTypeId, &GadgetSaveOperator, &GadgetLoadOperator);
+ } else {
+ gadgetTypeId = QMetaType::registerType(typeName.constData(),
+ nullptr,
+ nullptr,
+ sizeof(GadgetType),
+ flags, meta);
+ }
trackConnection(gadgetTypeId, connection);
QMutexLocker lock(&s_managedTypesMutex);
s_managedTypes[gadgetTypeId] = qMakePair(gadgetType, std::shared_ptr<QMetaObject>{meta, [](QMetaObject *ptr){ ::free(ptr); }});
return gadgetTypeId;
}
-static void registerAllGadgets(IoDeviceBase *connection, QRemoteObjectPackets::GadgetsData &gadgets)
+static void registerAllGadgets(IoDeviceBase *connection, Gadgets &gadgets)
{
while (!gadgets.isEmpty())
- registerGadget(connection, gadgets, gadgets.constBegin().key());
+ registerGadgets(connection, gadgets, gadgets.constBegin().key());
+}
+
+static void deserializeEnum(QDataStream &ds, EnumData &enumData)
+{
+ ds >> enumData.name;
+ ds >> enumData.isFlag;
+ ds >> enumData.isScoped;
+ ds >> enumData.size;
+ ds >> enumData.keyCount;
+ for (quint32 i = 0; i < enumData.keyCount; i++) {
+ EnumPair pair;
+ ds >> pair.name;
+ ds >> pair.value;
+ enumData.values.push_back(pair);
+ }
}
-static void parseGadgets(IoDeviceBase *connection, QDataStream &in, quint32 numGadgets = 1)
+static void parseGadgets(IoDeviceBase *connection, QDataStream &in)
{
- QRemoteObjectPackets::GadgetsData gadgets;
+ quint32 qtEnums, numGadgets;
+ in >> qtEnums; // Qt enums - just need registration
+ for (quint32 i = 0; i < qtEnums; ++i) {
+ QByteArray enumName;
+ in >> enumName;
+ QMetaType t(QMetaType::type(enumName.constData()));
+ registerEnum(enumName, t.metaObject()); // All Qt enums have default type int
+ }
+ in >> numGadgets;
+ if (numGadgets == 0)
+ return;
+ Gadgets gadgets;
for (quint32 i = 0; i < numGadgets; ++i) {
QByteArray type;
in >> type;
- quint32 numProperties;
+ quint32 numProperties, numEnums;
in >> numProperties;
- auto &properties = gadgets[type];
+ auto &properties = gadgets[type].properties;
for (quint32 p = 0; p < numProperties; ++p) {
- QRemoteObjectPackets::GadgetProperty prop;
+ GadgetProperty prop;
in >> prop.name;
in >> prop.type;
properties.push_back(prop);
}
+ in >> numEnums;
+ auto &enums = gadgets[type].enums;
+ for (quint32 e = 0; e < numEnums; ++e) {
+ EnumData enumData;
+ deserializeEnum(in, enumData);
+ enums.push_back(enumData);
+ }
}
registerAllGadgets(connection, gadgets);
}
@@ -728,43 +880,34 @@ QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(IoDeviceBase *connec
builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject);
builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
- QString type;
+ QString typeString;
+ QByteArray type;
quint32 numEnums = 0;
- quint32 numGadgets = 0;
quint32 numSignals = 0;
quint32 numMethods = 0;
quint32 numProperties = 0;
- in >> type;
- builder.addClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE, type.toLatin1());
- builder.setClassName(type.toLatin1());
+ in >> typeString;
+ type = typeString.toLatin1();
+ builder.addClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE, type);
+ builder.setClassName(type);
in >> numEnums;
+ QVector<quint32> enumSizes(numEnums);
for (quint32 i = 0; i < numEnums; ++i) {
- QByteArray name;
- in >> name;
- auto enumBuilder = builder.addEnumerator(name);
- bool isFlag;
- in >> isFlag;
- enumBuilder.setIsFlag(isFlag);
-
- QByteArray scopeName;
- in >> scopeName; // scope
- // TODO uncomment this line after https://bugreports.qt.io/browse/QTBUG-64081 is implemented
- //enumBuilder.setScope(scopeName);
-
- int keyCount;
- in >> keyCount;
- for (int k = 0; k < keyCount; ++k) {
- QByteArray key;
- int value;
- in >> key;
- in >> value;
- enumBuilder.addKey(key, value);
+ EnumData enumData;
+ deserializeEnum(in, enumData);
+ auto enumBuilder = builder.addEnumerator(enumData.name);
+ enumBuilder.setIsFlag(enumData.isFlag);
+ enumBuilder.setIsScoped(enumData.isScoped);
+ enumSizes[i] = enumData.size;
+
+ for (quint32 k = 0; k < enumData.keyCount; ++k) {
+ const auto pair = enumData.values.at(k);
+ enumBuilder.addKey(pair.name, pair.value);
}
}
- in >> numGadgets;
- parseGadgets(connection, in, numGadgets);
+ parseGadgets(connection, in);
int curIndex = 0;
@@ -812,7 +955,17 @@ QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(IoDeviceBase *connec
}
auto meta = builder.toMetaObject();
- dynamicTypes.insert(type, meta);
+ // Our type likely has enumerations from the inherited base classes, such as the Replica State
+ // We only want to register the new enumerations, and since we just added them, we know they
+ // are the last indices. Thus a backwards count seems most efficient.
+ const int totalEnumCount = meta->enumeratorCount();
+ int incrementingIndex = 0;
+ for (int i = numEnums; i > 0; i--) {
+ auto const enumMeta = meta->enumerator(totalEnumCount - i);
+ const QByteArray registeredName = QByteArray(type).append("::").append(enumMeta.name());
+ registerEnum(registeredName, meta, enumSizes.at(incrementingIndex++));
+ }
+ dynamicTypes.insert(typeString, meta);
return meta;
}
@@ -1197,7 +1350,7 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
QDataStream ds(typeInfo.parameters);
ds >> rxValue;
}
- rep->setProperty(propertyIndex, deserializedProperty(rxValue, property));
+ rep->setProperty(propertyIndex, decodeVariant(rxValue, property.userType()));
}
} else { //replica has been deleted, remove from list
replicas.remove(rxName);
@@ -1220,8 +1373,10 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
for (int i = 0; i < rxArgs.size(); i++) {
if (signal.parameterType(i) == QMetaType::QVariant)
param[i + 1] = const_cast<void*>(reinterpret_cast<const void*>(&rxArgs.at(i)));
- else
+ else {
+ decodeVariant(rxArgs[i], signal.parameterType(i));
param[i + 1] = const_cast<void *>(rxArgs.at(i).data());
+ }
}
} else if (propertyIndex != -1) {
param.resize(2);
diff --git a/src/remoteobjects/qremoteobjectpacket.cpp b/src/remoteobjects/qremoteobjectpacket.cpp
index 283d21b..4b8f77c 100644
--- a/src/remoteobjects/qremoteobjectpacket.cpp
+++ b/src/remoteobjects/qremoteobjectpacket.cpp
@@ -44,14 +44,77 @@
#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);
@@ -59,10 +122,6 @@ void serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, i
const auto target = source->m_api->isAdapterProperty(internalIndex) ? source->m_adapter : source->m_object;
const auto property = target->metaObject()->property(propertyIndex);
const QVariant value = property.read(target);
- if (property.isEnumType()) {
- ds << QVariant::fromValue<qint32>(value.toInt());
- return;
- }
if (QMetaType::typeFlags(property.userType()).testFlag(QMetaType::PointerToQObject)) {
auto const childSource = source->m_children.value(internalIndex);
auto valueAsPointerToQObject = qvariant_cast<QObject *>(value);
@@ -98,17 +157,7 @@ void serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, i
return;
}
}
- ds << value; // return original
-}
-
-QVariant deserializedProperty(const QVariant &in, const QMetaProperty &property)
-{
- if (property.isEnumType()) {
- const qint32 enumValue = in.toInt();
- return QVariant(property.userType(), &enumValue);
- } else {
- return in; // return original
- }
+ ds << encodeVariant(value);
}
void serializeHandshakePacket(DataStreamPacket &ds)
@@ -185,32 +234,7 @@ void serializeInitDynamicPacket(DataStreamPacket &ds, const QRemoteObjectRootSou
ds.finishPacket();
}
-static void mergeData(GadgetsData &a, const GadgetsData &b)
-{
- for (auto it = b.constBegin(); it != b.constEnd(); ++it)
- a[it.key()] = it.value();
-}
-
-static GadgetsData gadgetData(const QMetaObject *mo)
-{
- if (!mo)
- return {};
- GadgetsData res;
- auto & properties = res[mo->className()];
- const int numProperties = mo->propertyCount();
- for (int i = 0; i < numProperties; ++i) {
- const auto property = mo->property(i);
- GadgetProperty data;
- data.name = property.name();
- data.type = property.typeName();
- if (QMetaType::typeFlags(property.userType()).testFlag(QMetaType::IsGadget))
- mergeData(res, gadgetData(QMetaType::metaObjectForType(property.userType())));
- properties.push_back(data);
- }
- return res;
-}
-
-static ObjectType objectType(const QString &typeName)
+static ObjectType getObjectType(const QString &typeName)
{
if (typeName == QLatin1String("QAbstractItemModelAdapter"))
return ObjectType::MODEL;
@@ -224,7 +248,51 @@ static ObjectType objectType(const QString &typeName)
return ObjectType::CLASS;
}
-void recurseForGadgets(GadgetsData &gadgets, const QRemoteObjectSourceBase *source)
+// 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;
@@ -236,12 +304,14 @@ void recurseForGadgets(GadgetsData &gadgets, const QRemoteObjectSourceBase *sour
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;
- mergeData(gadgets, gadgetData(mo));
+ recurseMetaobject(mo, gadgets, enums);
source->d->sentTypes.insert(QLatin1String(mo->className()));
}
}
@@ -250,12 +320,14 @@ void recurseForGadgets(GadgetsData &gadgets, const QRemoteObjectSourceBase *sour
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;
- mergeData(gadgets, gadgetData(mo));
+ recurseMetaobject(mo, gadgets, enums);
source->d->sentTypes.insert(QLatin1String(mo->className()));
}
}
@@ -264,34 +336,130 @@ void recurseForGadgets(GadgetsData &gadgets, const QRemoteObjectSourceBase *sour
Q_ASSERT(index >= 0);
const auto target = api->isAdapterProperty(pi) ? source->m_adapter : source->m_object;
const auto metaProperty = target->metaObject()->property(index);
- if (QMetaType::typeFlags(metaProperty.userType()).testFlag(QMetaType::PointerToQObject)) {
- auto const type = objectType(QString::fromLatin1(metaProperty.typeName()));
- if (type == ObjectType::CLASS) {
+ 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, childSource);
+ recurseForGadgets(gadgets, enums, childSource);
}
}
- const int type = metaProperty.userType();
if (!QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
continue;
const auto mo = QMetaType::metaObjectForType(type);
if (source->d->sentTypes.contains(QLatin1String(mo->className())))
continue;
- mergeData(gadgets, gadgetData(mo));
+ 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;
- bool dynamic = source->m_api->isDynamic();
- const QByteArray classname(source->m_api->typeName().toLatin1());
- const QByteArray sourcename = QByteArray(classname).append("Source");
- auto replace = [&classname, &sourcename, dynamic](QByteArray &name) {
- if (!dynamic) // Compiled classes likely have <ClassNameSource> that should be <ClassName>
- name.replace(sourcename, classname);
+ 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();
@@ -309,47 +477,16 @@ void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
for (int i = 0; i < numEnums; ++i) {
auto enumerator = metaObject->enumerator(api->sourceEnumIndex(i));
Q_ASSERT(enumerator.isValid());
- ds << enumerator.name();
- ds << enumerator.isFlag();
- ds << enumerator.scope();
-#ifdef QTRO_VERBOSE_PROTOCOL
- qDebug(" Enum %d (name = %s, isFlag = %s, scope = %s):", i, enumerator.name(), enumerator.isFlag() ? "true" : "false", enumerator.scope());
-#endif
- const int keyCount = enumerator.keyCount();
- ds << keyCount;
- for (int k = 0; k < keyCount; ++k) {
- ds << 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
- }
+ serializeEnum(ds, enumerator);
}
if (source->d->isDynamic) {
- GadgetsData gadgets;
- recurseForGadgets(gadgets, source);
- ds << quint32(gadgets.size());
-#ifdef QTRO_VERBOSE_PROTOCOL
- qDebug() << " Found" << gadgets.size() << "gadget/pod types";
- int i = 0, j = 0;
-#endif
- for (auto it = gadgets.constBegin(); it != gadgets.constEnd(); ++it) {
- ds << it.key();
- ds << quint32(it.value().size());
-#ifdef QTRO_VERBOSE_PROTOCOL
- qDebug(" Gadget %d (name = %s):", i++, it.key().constData());
-#endif
- for (const auto &prop : qAsConst(it.value())) {
-#ifdef QTRO_VERBOSE_PROTOCOL
- qDebug(" Data member %d (name = %s, type = %s):", j++, prop.name.constData(), prop.type.constData());
-#endif
- ds << prop.name;
- ds << prop.type;
- }
- }
+ QSet<const QMetaObject *> gadgets;
+ QSet<QMetaEnum> enums;
+ recurseForGadgets(gadgets, enums, source);
+ serializeGadgets(ds, gadgets, enums, source);
} else
- ds << quint32(0);
+ ds << quint32(0) << quint32(0); // qtEnums, numGadgets
const int numSignals = api->signalCount();
ds << quint32(numSignals); //Number of signals
@@ -370,11 +507,15 @@ void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
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 =" << api->methodSignature(i) << "parameter names =" << api->methodParameterNames(i) << "return type =" << api->typeName(i) << ")";
+ qDebug() << " Slot" << i << "(signature =" << signature << "parameter names =" << api->methodParameterNames(i) << "return type =" << typeName << ")";
#endif
- ds << api->methodSignature(i);
- ds << api->typeName(i);
+ ds << signature;
+ ds << typeName;
ds << api->methodParameterNames(i);
}
@@ -391,10 +532,10 @@ void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
qDebug() << " Property" << i << "name =" << metaProperty.name();
#endif
if (QMetaType::typeFlags(metaProperty.userType()).testFlag(QMetaType::PointerToQObject)) {
- auto type = objectType(QLatin1String(metaProperty.typeName()));
- ds << (type == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
+ auto objectType = getObjectType(QLatin1String(metaProperty.typeName()));
+ ds << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
#ifdef QTRO_VERBOSE_PROTOCOL
- qDebug() << " Type:" << (type == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
+ qDebug() << " Type:" << (objectType == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
#endif
} else {
ds << metaProperty.typeName();
@@ -447,12 +588,8 @@ void serializeInvokePacket(DataStreamPacket &ds, const QString &name, int call,
ds << index;
ds << (quint32)args.size();
- foreach (const auto &arg, args) {
- if (QMetaType::typeFlags(arg.userType()).testFlag(QMetaType::IsEnumeration))
- ds << QVariant::fromValue<qint32>(arg.toInt());
- else
- ds << arg;
- }
+ foreach (const auto &arg, args)
+ ds << encodeVariant(arg);
ds << serialId;
ds << propertyIndex;
@@ -530,7 +667,7 @@ void serializePongPacket(DataStreamPacket &ds, const QString &name)
QRO_::QRO_(QRemoteObjectSourceBase *source)
: name(source->name())
, typeName(source->m_api->typeName())
- , type(source->m_adapter ? ObjectType::MODEL : objectType(typeName))
+ , type(source->m_adapter ? ObjectType::MODEL : getObjectType(typeName))
, isNull(source->m_object == nullptr)
, classDefinition()
, parameters()
@@ -543,7 +680,8 @@ QRO_::QRO_(const QVariant &value)
auto meta = QMetaType::metaObjectForType(value.userType());
QDataStream out(&classDefinition, QIODevice::WriteOnly);
const int numProperties = meta->propertyCount();
- const auto typeName = QByteArray(QMetaType::typeName(value.userType()));
+ 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
@@ -554,8 +692,8 @@ QRO_::QRO_(const QVariant &value)
#ifdef QTRO_VERBOSE_PROTOCOL
qDebug(" Data member %d (name = %s, type = %s):", i, property.name(), property.typeName());
#endif
- out << property.name();
- out << property.typeName();
+ out << QByteArray::fromRawData(property.name(), qstrlen(property.name()));
+ out << QByteArray::fromRawData(property.typeName(), qstrlen(property.typeName()));
}
QDataStream ds(&parameters, QIODevice::WriteOnly);
ds << value;
@@ -567,8 +705,7 @@ QRO_::QRO_(const QVariant &value)
QDataStream &operator<<(QDataStream &stream, const QRO_ &info)
{
stream << info.name << info.typeName << (quint8)(info.type) << info.classDefinition << info.isNull;
- qCDebug(QT_REMOTEOBJECT) << "Serializing QRO_" << info.name << info.typeName << (info.type == ObjectType::CLASS ? "Class" : info.type == ObjectType::MODEL ? "Model" : "Gadget")
- << (info.isNull ? "nullptr" : "valid pointer") << (info.classDefinition.isEmpty() ? "no definitions" : "with definitions");
+ qCDebug(QT_REMOTEOBJECT) << "Serializing " << info;
// info.parameters will be filled in by serializeProperty
return stream;
}
@@ -578,8 +715,7 @@ QDataStream &operator>>(QDataStream &stream, QRO_ &info)
quint8 tmpType;
stream >> info.name >> info.typeName >> tmpType >> info.classDefinition >> info.isNull;
info.type = static_cast<ObjectType>(tmpType);
- qCDebug(QT_REMOTEOBJECT) << "Deserializing QRO_" << info.name << info.typeName << (info.isNull ? "nullptr" : "valid pointer")
- << (info.classDefinition.isEmpty() ? "no definitions" : "with definitions");
+ qCDebug(QT_REMOTEOBJECT) << "Deserializing " << info;
if (!info.isNull)
stream >> info.parameters;
return stream;
diff --git a/src/remoteobjects/qremoteobjectpacket_p.h b/src/remoteobjects/qremoteobjectpacket_p.h
index 542f998..612fabf 100644
--- a/src/remoteobjects/qremoteobjectpacket_p.h
+++ b/src/remoteobjects/qremoteobjectpacket_p.h
@@ -72,6 +72,8 @@ class QRemoteObjectRootSource;
namespace QRemoteObjectPackets {
+Q_NAMESPACE
+
class DataStreamPacket;
struct ObjectInfo
@@ -100,6 +102,7 @@ inline QDataStream& operator>>(QDataStream &stream, ObjectInfo &info)
typedef QVector<ObjectInfo> ObjectInfoList;
enum class ObjectType : quint8 { CLASS, MODEL, GADGET };
+Q_ENUM_NS(ObjectType)
// Use a short name, as QVariant::save writes the name every time a qvariant of
// this type is serialized
@@ -118,9 +121,9 @@ public:
inline QDebug operator<<(QDebug dbg, const QRO_ &info)
{
- dbg.nospace() << "QRO_(name: " << info.name << "typeName: " << info.typeName << "type: " << (info.type == ObjectType::CLASS ? "Class" : "Model")
- << ", valid: " << (info.isNull ? "true" : "false")
- << ", paremeters: {" << info.parameters <<")";
+ dbg.nospace() << "QRO_(name: " << info.name << ", typeName: " << info.typeName << ", type: " << info.type
+ << ", valid: " << (info.isNull ? "true" : "false") << ", paremeters: {" << info.parameters <<")"
+ << (info.classDefinition.isEmpty() ? " no definitions)" : " with definitions)");
return dbg.space();
}
@@ -131,13 +134,6 @@ QDataStream& operator>>(QDataStream &stream, QRO_ &info);
void serializeObjectListPacket(DataStreamPacket&, const ObjectInfoList&);
void deserializeObjectListPacket(QDataStream&, ObjectInfoList&);
-struct GadgetProperty {
- QByteArray name;
- QByteArray type;
-};
-
-using GadgetsData = QHash<QByteArray, QVector<GadgetProperty>>;
-
//Helper class for creating a QByteArray from a QRemoteObjectPacket
class DataStreamPacket : public QDataStream
{
@@ -172,8 +168,10 @@ private:
Q_DISABLE_COPY(DataStreamPacket)
};
+const QVariant encodeVariant(const QVariant &value);
+QVariant &decodeVariant(QVariant &value, int type);
+
void serializeProperty(QDataStream &, const QRemoteObjectSourceBase *source, int internalIndex);
-QVariant deserializedProperty(const QVariant &in, const QMetaProperty &property);
void serializeHandshakePacket(DataStreamPacket &);
void serializeInitPacket(DataStreamPacket &, const QRemoteObjectRootSource*);
diff --git a/src/remoteobjects/qremoteobjectreplica.cpp b/src/remoteobjects/qremoteobjectreplica.cpp
index c663efd..6536dc8 100644
--- a/src/remoteobjects/qremoteobjectreplica.cpp
+++ b/src/remoteobjects/qremoteobjectreplica.cpp
@@ -196,7 +196,7 @@ QVector<int> QConnectedReplicaImplementation::childIndices() const
return m_childIndices;
}
-void QConnectedReplicaImplementation::initialize(const QVariantList &values)
+void QConnectedReplicaImplementation::initialize(QVariantList &values)
{
qCDebug(QT_REMOTEOBJECT) << "initialize()" << m_propertyStorage.size();
const int nParam = values.size();
@@ -207,7 +207,7 @@ void QConnectedReplicaImplementation::initialize(const QVariantList &values)
changedProperties[i] = -1;
if (m_propertyStorage[i] != values.at(i)) {
const QMetaProperty property = m_metaObject->property(i+offset);
- m_propertyStorage[i] = QRemoteObjectPackets::deserializedProperty(values.at(i), property);
+ m_propertyStorage[i] = QRemoteObjectPackets::decodeVariant(values[i], property.userType());
changedProperties[i] = i;
}
qCDebug(QT_REMOTEOBJECT) << "SETPROPERTY" << i << m_metaObject->property(i+offset).name() << values.at(i).typeName() << values.at(i).toString();
diff --git a/src/remoteobjects/qremoteobjectreplica_p.h b/src/remoteobjects/qremoteobjectreplica_p.h
index e724dae..79a07cf 100644
--- a/src/remoteobjects/qremoteobjectreplica_p.h
+++ b/src/remoteobjects/qremoteobjectreplica_p.h
@@ -158,7 +158,7 @@ public:
bool isInitialized() const override;
bool waitForSource(int timeout) override;
QVector<int> childIndices() const;
- void initialize(const QVariantList &values);
+ void initialize(QVariantList &values);
void configurePrivate(QRemoteObjectReplica *) override;
void requestRemoteObjectSource();
bool sendCommand();
diff --git a/src/remoteobjects/qremoteobjectsource.cpp b/src/remoteobjects/qremoteobjectsource.cpp
index 8b8b280..3ddec27 100644
--- a/src/remoteobjects/qremoteobjectsource.cpp
+++ b/src/remoteobjects/qremoteobjectsource.cpp
@@ -146,7 +146,7 @@ QRemoteObjectSource::QRemoteObjectSource(QObject *obj, Private *d, const SourceA
QRemoteObjectRootSource::QRemoteObjectRootSource(QObject *obj, const SourceApiMap *api,
QObject *adapter, QRemoteObjectSourceIo *sourceIo)
- : QRemoteObjectSourceBase(obj, new Private(sourceIo), api, adapter)
+ : QRemoteObjectSourceBase(obj, new Private(sourceIo, this), api, adapter)
, m_name(api->name())
{
d->m_sourceIo->registerSource(this);
@@ -365,7 +365,9 @@ void QRemoteObjectSourceBase::handleMetaCall(int index, QMetaObject::Call call,
}
qCDebug(QT_REMOTEOBJECT) << "# Listeners" << d->m_listeners.length();
- qCDebug(QT_REMOTEOBJECT) << "Invoke args:" << m_object << call << index << *marshalArgs(index, a);
+ qCDebug(QT_REMOTEOBJECT) << "Invoke args:" << m_object
+ << (call == 0 ? QLatin1String("InvokeMetaMethod") : QStringLiteral("Non-invoked call: %d").arg(call))
+ << m_api->signalSignature(index) << *marshalArgs(index, a);
serializeInvokePacket(d->m_packet, name(), call, index, *marshalArgs(index, a), -1, propertyIndex);
d->m_packet.baseAddress = 0;
diff --git a/src/remoteobjects/qremoteobjectsource.h b/src/remoteobjects/qremoteobjectsource.h
index f2993bd..61346df 100644
--- a/src/remoteobjects/qremoteobjectsource.h
+++ b/src/remoteobjects/qremoteobjectsource.h
@@ -99,6 +99,56 @@ static inline void qtro_method_test(Func1, Func2)
"Return types are not compatible.");
}
+// The stringData, methodMatch and QMetaObjectPrivate methods are modified versions of the code
+// from qmetaobject_p.h/qmetaobject.cpp. The modifications are based on our custom need to match
+// a method name that comes from the .rep file.
+// The QMetaObjectPrivate struct should only have members appended to maintain binary compatibility,
+// so we should be fine with only the listed version with the fields we use.
+inline const QByteArray apiStringData(const QMetaObject *mo, int index)
+{
+ const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) };
+ return data;
+}
+
+inline bool apiMethodMatch(const QMetaObject *m, int handle,
+ const QByteArray &name, int argc,
+ const int *types)
+{
+ if (int(m->d.data[handle + 1]) != argc)
+ return false;
+ if (apiStringData(m, m->d.data[handle]) != name)
+ return false;
+ int paramsIndex = m->d.data[handle + 2] + 1;
+ for (int i = 0; i < argc; ++i) {
+ uint typeInfo = m->d.data[paramsIndex + i];
+ if (typeInfo & 0x80000000) { // Custom/named type, compare names
+ const char *t = QMetaType::typeName(types[i]);
+ const auto type = QByteArray::fromRawData(t, qstrlen(t));
+ if (type != apiStringData(m, typeInfo & 0x7FFFFFFF))
+ return false;
+ } else if (types[i] != int(typeInfo))
+ return false;
+ }
+ return true;
+}
+
+struct QMetaObjectPrivate
+{
+ // revision 7 is Qt 5.0 everything lower is not supported
+ // revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
+ enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
+
+ int revision;
+ int className;
+ int classInfoCount, classInfoData;
+ int methodCount, methodData;
+ int propertyCount, propertyData;
+ int enumeratorCount, enumeratorData;
+ int constructorCount, constructorData;
+ int flags;
+ int signalCount;
+};
+
template <class ObjectType, typename Func1, typename Func2>
static inline int qtro_method_index(Func1, Func2, const char *methodName, int *count, int const **types)
{
@@ -114,7 +164,33 @@ static inline int qtro_method_index(Func1, Func2, const char *methodName, int *c
"Return types are not compatible.");
*count = Type2::ArgumentCount;
*types = QtPrivate::ConnectionTypes<typename Type2::Arguments>::types();
- return ObjectType::staticMetaObject.indexOfMethod(methodName);
+
+ int result = ObjectType::staticMetaObject.indexOfMethod(methodName);
+ if (result >= 0)
+ return result;
+ // We can have issues, specifically with enums, since the compiler can infer the class. Since
+ // indexOfMethod() is doing string comparisons for registered types, "MyEnum" and "MyClass::MyEnum"
+ // won't match.
+ // Below is similar to QMetaObject->indexOfMethod, but template magic has already matched parameter
+ // types, so we need to find a match for the API method name + parameters. Neither approach works
+ // 100%, as the below code doesn't match a parameter of type "size_t" (which the template match
+ // identifies as "ulong"). These subtleties can cause the below string comparison fails.
+ // There is no known case that would fail both methods.
+ // TODO: is there a way to make this a constexpr so a failure is detected at compile time?
+ int nameLength = strchr(methodName, '(') - methodName;
+ const auto name = QByteArray::fromRawData(methodName, nameLength);
+ for (const QMetaObject *m = &ObjectType::staticMetaObject; m; m = m->d.superdata) {
+ const auto priv = reinterpret_cast<const QMetaObjectPrivate*>(m->d.data);
+ int i = (priv->methodCount - 1);
+ const int end = priv->signalCount;
+ for (; i >= end; --i) {
+ int handle = priv->methodData + 5*i;
+ if (apiMethodMatch(m, handle, name, *count, *types))
+ return i + m->methodOffset();
+ }
+ }
+ qWarning() << "No matching method for" << methodName << "in the provided metaclass" << ObjectType::staticMetaObject.className();
+ return -1;
}
template <class ObjectType>
@@ -146,6 +222,7 @@ public:
virtual ~SourceApiMap() {}
virtual QString name() const = 0;
virtual QString typeName() const = 0;
+ virtual QByteArray className() const { return typeName().toLatin1().append("Source"); }
virtual int enumCount() const = 0;
virtual int propertyCount() const = 0;
virtual int signalCount() const = 0;
diff --git a/src/remoteobjects/qremoteobjectsource_p.h b/src/remoteobjects/qremoteobjectsource_p.h
index 0fdd3c8..602c89d 100644
--- a/src/remoteobjects/qremoteobjectsource_p.h
+++ b/src/remoteobjects/qremoteobjectsource_p.h
@@ -84,7 +84,7 @@ public:
QByteArray m_objectChecksum;
QMap<int, QPointer<QRemoteObjectSourceBase>> m_children;
struct Private {
- Private(QRemoteObjectSourceIo *io) : m_sourceIo(io), isDynamic(false) {}
+ Private(QRemoteObjectSourceIo *io, QRemoteObjectRootSource *root) : m_sourceIo(io), isDynamic(false), root(root) {}
QRemoteObjectSourceIo *m_sourceIo;
QVector<IoDeviceBase*> m_listeners;
QRemoteObjectPackets::DataStreamPacket m_packet;
@@ -92,6 +92,7 @@ public:
// Types needed during recursively sending a root to a new listener
QSet<QString> sentTypes;
bool isDynamic;
+ QRemoteObjectRootSource *root;
};
Private *d;
static const int qobjectPropertyOffset;
@@ -134,6 +135,7 @@ public:
~DynamicApiMap() override {}
QString name() const override { return m_name; }
QString typeName() const override { return m_typeName; }
+ QByteArray className() const override { return QByteArray(m_metaObject->className()); }
int enumCount() const override { return m_enumCount; }
int propertyCount() const override { return m_properties.size(); }
int signalCount() const override { return m_signals.size(); }
diff --git a/src/remoteobjects/qremoteobjectsourceio.cpp b/src/remoteobjects/qremoteobjectsourceio.cpp
index bde010b..189e5f4 100644
--- a/src/remoteobjects/qremoteobjectsourceio.cpp
+++ b/src/remoteobjects/qremoteobjectsourceio.cpp
@@ -235,8 +235,13 @@ void QRemoteObjectSourceIo::onServerRead(QObject *conn)
}
if (source->m_api->isAdapterMethod(index))
qRODebug(this) << "Adapter (method) Invoke-->" << m_rxName << source->m_adapter->metaObject()->method(resolvedIndex).name();
- else
- qRODebug(this) << "Source (method) Invoke-->" << m_rxName << source->m_object->metaObject()->method(resolvedIndex).name();
+ else {
+ qRODebug(this) << "Source (method) Invoke-->" << m_rxName << source->m_object->metaObject()->method(resolvedIndex).methodSignature();
+ auto method = source->m_object->metaObject()->method(resolvedIndex);
+ const int parameterCount = method.parameterCount();
+ for (int i = 0; i < parameterCount; i++)
+ decodeVariant(m_rxArgs[i], method.parameterType(i));
+ }
int typeId = QMetaType::type(source->m_api->typeName(index).constData());
if (!QMetaType(typeId).sizeOf())
typeId = QVariant::Invalid;
@@ -254,13 +259,13 @@ void QRemoteObjectSourceIo::onServerRead(QObject *conn)
QRemoteObjectPendingCallWatcher *watcher = new QRemoteObjectPendingCallWatcher(call, connection);
QObject::connect(watcher, &QRemoteObjectPendingCallWatcher::finished, connection, [this, serialId, connection, watcher]() {
if (watcher->error() == QRemoteObjectPendingCall::NoError) {
- serializeInvokeReplyPacket(this->m_packet, this->m_rxName, serialId, watcher->returnValue());
+ serializeInvokeReplyPacket(this->m_packet, this->m_rxName, serialId, encodeVariant(watcher->returnValue()));
connection->write(m_packet.array, m_packet.size);
}
watcher->deleteLater();
});
} else {
- serializeInvokeReplyPacket(m_packet, m_rxName, serialId, returnValue);
+ serializeInvokeReplyPacket(m_packet, m_rxName, serialId, encodeVariant(returnValue));
connection->write(m_packet.array, m_packet.size);
}
}
diff --git a/tests/auto/integration/tst_integration.cpp b/tests/auto/integration/tst_integration.cpp
index af5cba0..7f39381 100644
--- a/tests/auto/integration/tst_integration.cpp
+++ b/tests/auto/integration/tst_integration.cpp
@@ -345,8 +345,9 @@ private slots:
// set property on the replica (test property change packet)
{
QSignalSpy spy(tc_rep.data(), SIGNAL(classEnumChanged(TestClassReplica::ClassEnum)));
+ QVERIFY(spy.isValid());
tc_rep->pushClassEnum(TestClassReplica::Two);
- QVERIFY(spy.wait());
+ QVERIFY(spy.count() || spy.wait());
QCOMPARE((qint32)tc.classEnum(), (qint32)tc_rep->classEnum());
}
@@ -405,7 +406,7 @@ private slots:
QSignalSpy spy(tc_rep.data(), SIGNAL(classEnumChanged(TestClassReplica::ClassEnum)));
bool res = property.write(tc_repDynamic.data(), TestClassReplica::Two);
QVERIFY(!res);
- int methodIndex = metaObject->indexOfMethod("pushClassEnum(ClassEnum)");
+ int methodIndex = metaObject->indexOfMethod("pushClassEnum(TestClassReplica::ClassEnum)");
QVERIFY(methodIndex >= 0);
QMetaMethod method = metaObject->method(methodIndex);
QVERIFY(method.isValid());
diff --git a/tests/auto/integration_external/client/main.cpp b/tests/auto/integration_external/client/main.cpp
index 05f7f59..bc53333 100644
--- a/tests/auto/integration_external/client/main.cpp
+++ b/tests/auto/integration_external/client/main.cpp
@@ -129,7 +129,7 @@ private Q_SLOTS:
QCOMPARE(simm.parameterType(2), int(QMetaType::QString));
}
- int slotIdx = mo->indexOfSlot("testEnumParamsInSlots(Enum1,bool,int)");
+ int slotIdx = mo->indexOfSlot("testEnumParamsInSlots(MyInterfaceReplica::Enum1,bool,int)");
QVERIFY(slotIdx != -1);
auto slmm = mo->method(slotIdx);
{
@@ -143,7 +143,7 @@ private Q_SLOTS:
int enumVal = 0;
mo->invokeMethod(rep.data(), "testEnumParamsInSlots",
- QGenericArgument("Enum1", &enumVal),
+ QGenericArgument("MyInterfaceReplica::Enum1", &enumVal),
Q_ARG(bool, true), Q_ARG(int, 1234));
int enumIdx = mo->indexOfProperty("enum1");
diff --git a/tests/auto/integration_multiprocess/client/main.cpp b/tests/auto/integration_multiprocess/client/main.cpp
index b60a48a..87f2ad8 100644
--- a/tests/auto/integration_multiprocess/client/main.cpp
+++ b/tests/auto/integration_multiprocess/client/main.cpp
@@ -112,7 +112,7 @@ private Q_SLOTS:
QCOMPARE(simm.parameterType(2), int(QMetaType::QString));
}
- int slotIdx = mo->indexOfSlot("testEnumParamsInSlots(Enum1,bool,int)");
+ int slotIdx = mo->indexOfSlot("testEnumParamsInSlots(MyInterfaceReplica::Enum1,bool,int)");
QVERIFY(slotIdx != -1);
auto slmm = mo->method(slotIdx);
{
@@ -126,7 +126,7 @@ private Q_SLOTS:
int enumVal = 0;
mo->invokeMethod(rep.data(), "testEnumParamsInSlots",
- QGenericArgument("Enum1", &enumVal),
+ QGenericArgument("MyInterfaceReplica::Enum1", &enumVal),
Q_ARG(bool, true), Q_ARG(int, 1234));
int enumIdx = mo->indexOfProperty("enum1");
diff --git a/tests/auto/proxy_multiprocess/client/client.pro b/tests/auto/proxy_multiprocess/client/client.pro
index d6c31db..7a181a2 100644
--- a/tests/auto/proxy_multiprocess/client/client.pro
+++ b/tests/auto/proxy_multiprocess/client/client.pro
@@ -12,6 +12,7 @@ REPC_REPLICA = ../subclass.rep
SOURCES += main.cpp \
HEADERS += \
- ../shared.h
+ ../shared.h \
+ ../namespace.h
INCLUDEPATH += $$PWD
diff --git a/tests/auto/proxy_multiprocess/client/main.cpp b/tests/auto/proxy_multiprocess/client/main.cpp
index f37821f..19ba46f 100644
--- a/tests/auto/proxy_multiprocess/client/main.cpp
+++ b/tests/auto/proxy_multiprocess/client/main.cpp
@@ -60,28 +60,49 @@ private Q_SLOTS:
QCOMPARE(m_rep->subClass()->i(), initialI);
QVERIFY(m_rep->tracks() != nullptr);
QVERIFY(tracksSpy.count() || tracksSpy.wait());
- QCOMPARE(m_rep->myEnum(), QVariant::fromValue(ParentClassReplica::bar));
+ QCOMPARE(m_rep->myEnum(), ParentClassReplica::bar);
+ QCOMPARE(m_rep->date(), Qt::SystemLocaleShortDate);
+ QCOMPARE(m_rep->nsEnum(), NS::Bravo);
+ QCOMPARE(m_rep->ns2Enum(), NS2::NamespaceEnum::Bravo);
QCOMPARE(m_rep->variant(), QVariant::fromValue(42.0f));
} else {
QVERIFY(m_rep->subClass() == nullptr);
QVERIFY(m_rep->tracks() == nullptr);
- QCOMPARE(m_rep->myEnum(), QVariant::fromValue(ParentClassReplica::foo));
+ QCOMPARE(m_rep->myEnum(), ParentClassReplica::foo);
+ QCOMPARE(m_rep->date(), Qt::ISODate);
+ QCOMPARE(m_rep->nsEnum(), NS::Alpha);
+ QCOMPARE(m_rep->ns2Enum(), NS2::NamespaceEnum::Alpha);
QCOMPARE(m_rep->variant(), QVariant());
}
+ QPoint p(1, 2);
+ auto enumReply = m_rep->enumSlot(p, ParentClassReplica::bar);
+ QVERIFY(enumReply.waitForFinished());
+ QCOMPARE(enumReply.error(), QRemoteObjectPendingCall::NoError);
+ QCOMPARE(enumReply.returnValue(), QVariant::fromValue(ParentClassReplica::foobar));
+
qDebug() << "Verified expected initial states, sending start.";
+ QSignalSpy enumSpy(m_rep.data(), &ParentClassReplica::enum2);
+ QSignalSpy advanceSpy(m_rep.data(), SIGNAL(advance()));
auto reply = m_rep->start();
QVERIFY(reply.waitForFinished());
QVERIFY(reply.error() == QRemoteObjectPendingCall::NoError);
QCOMPARE(reply.returnValue(), QVariant::fromValue(true));
+ QVERIFY(enumSpy.wait());
+ QCOMPARE(enumSpy.count(), 1);
+ const auto arguments = enumSpy.takeFirst();
+ QCOMPARE(arguments.at(0), QVariant::fromValue(ParentClassReplica::foo));
+ QCOMPARE(arguments.at(1), QVariant::fromValue(ParentClassReplica::bar));
- QSignalSpy advanceSpy(m_rep.data(), SIGNAL(advance()));
- QVERIFY(advanceSpy.wait());
+ QVERIFY(advanceSpy.count() || advanceSpy.wait());
QVERIFY(m_rep->subClass() != nullptr);
QCOMPARE(m_rep->subClass()->myPOD(), updatedValue);
QCOMPARE(m_rep->subClass()->i(), updatedI);
QVERIFY(m_rep->tracks() != nullptr);
- QCOMPARE(m_rep->myEnum(), QVariant::fromValue(ParentClassReplica::foobar));
+ QCOMPARE(m_rep->myEnum(), ParentClassReplica::foobar);
+ QCOMPARE(m_rep->date(), Qt::SystemLocaleLongDate);
+ QCOMPARE(m_rep->nsEnum(), NS::Charlie);
+ QCOMPARE(m_rep->ns2Enum(), NS2::NamespaceEnum::Charlie);
QCOMPARE(m_rep->variant(), QVariant::fromValue(podValue));
qDebug() << "Verified expected final states, cleaning up.";
}
diff --git a/tests/auto/proxy_multiprocess/namespace.h b/tests/auto/proxy_multiprocess/namespace.h
new file mode 100644
index 0000000..2d06e67
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/namespace.h
@@ -0,0 +1,15 @@
+#include <QMetaType>
+
+namespace NS
+{
+ Q_NAMESPACE
+ enum NamespaceEnum { Alpha=1, Bravo, Charlie };
+ Q_ENUM_NS(NamespaceEnum)
+}
+
+namespace NS2
+{
+ Q_NAMESPACE
+ enum class NamespaceEnum : quint8 { Alpha=1, Bravo, Charlie };
+ Q_ENUM_NS(NamespaceEnum)
+}
diff --git a/tests/auto/proxy_multiprocess/proxy_multiprocess.pro b/tests/auto/proxy_multiprocess/proxy_multiprocess.pro
index 075bc00..6bd7af7 100644
--- a/tests/auto/proxy_multiprocess/proxy_multiprocess.pro
+++ b/tests/auto/proxy_multiprocess/proxy_multiprocess.pro
@@ -1,2 +1,5 @@
TEMPLATE = subdirs
SUBDIRS = client server proxy tst
+
+OTHER_FILES += shared.h \
+ namespace.h
diff --git a/tests/auto/proxy_multiprocess/server/main.cpp b/tests/auto/proxy_multiprocess/server/main.cpp
index 2e6895b..1cd005d 100644
--- a/tests/auto/proxy_multiprocess/server/main.cpp
+++ b/tests/auto/proxy_multiprocess/server/main.cpp
@@ -56,6 +56,9 @@ private Q_SLOTS:
parent.setSubClass(&subclass);
parent.setTracks(&model);
parent.setMyEnum(ParentClassSource::bar);
+ parent.setDate(Qt::SystemLocaleShortDate);
+ parent.setNsEnum(NS::Bravo);
+ parent.setNs2Enum(NS2::NamespaceEnum::Bravo);
parent.setVariant(QVariant::fromValue(42.0f));
}
@@ -82,7 +85,11 @@ private Q_SLOTS:
if (objectMode == QLatin1Literal("NullPointer"))
parent.setTracks(&model);
parent.setMyEnum(ParentClassSource::foobar);
+ parent.setDate(Qt::SystemLocaleLongDate);
+ parent.setNsEnum(NS::Charlie);
+ parent.setNs2Enum(NS2::NamespaceEnum::Charlie);
parent.setVariant(QVariant::fromValue(podValue));
+ emit parent.enum2(ParentClassSource::foo, ParentClassSource::bar);
emit parent.advance();
diff --git a/tests/auto/proxy_multiprocess/server/mytestserver.cpp b/tests/auto/proxy_multiprocess/server/mytestserver.cpp
index b0c75ab..e55739d 100644
--- a/tests/auto/proxy_multiprocess/server/mytestserver.cpp
+++ b/tests/auto/proxy_multiprocess/server/mytestserver.cpp
@@ -29,7 +29,6 @@
#include <qdebug.h>
#include "mytestserver.h"
-#include "rep_subclass_source.h"
MyTestServer::MyTestServer(QObject *parent)
: ParentClassSimpleSource(parent)
@@ -53,3 +52,16 @@ bool MyTestServer::quit()
emit quitApp();
return true;
}
+
+ParentClassSource::MyEnum MyTestServer::enumSlot(QPoint p, MyEnum myEnum)
+{
+ Q_UNUSED(p)
+ Q_UNUSED(myEnum)
+ return ParentClassSource::foobar;
+}
+
+Qt::DateFormat MyTestServer::dateSlot(Qt::DateFormat date)
+{
+ Q_UNUSED(date)
+ return Qt::SystemLocaleDate;
+}
diff --git a/tests/auto/proxy_multiprocess/server/mytestserver.h b/tests/auto/proxy_multiprocess/server/mytestserver.h
index df044c1..8a4d09b 100644
--- a/tests/auto/proxy_multiprocess/server/mytestserver.h
+++ b/tests/auto/proxy_multiprocess/server/mytestserver.h
@@ -47,6 +47,8 @@ public:
public Q_SLOTS:
bool start() override;
bool quit() override;
+ MyEnum enumSlot(QPoint p, MyEnum myEnum) override;
+ Qt::DateFormat dateSlot(Qt::DateFormat date) override;
Q_SIGNALS:
void quitApp();
diff --git a/tests/auto/proxy_multiprocess/server/server.pro b/tests/auto/proxy_multiprocess/server/server.pro
index 24c9a13..02ca498 100644
--- a/tests/auto/proxy_multiprocess/server/server.pro
+++ b/tests/auto/proxy_multiprocess/server/server.pro
@@ -14,6 +14,7 @@ SOURCES += main.cpp \
HEADERS += \
../shared.h \
+ ../namespace.h \
mytestserver.h \
$$OUT_PWD/rep_subclass_source.h
diff --git a/tests/auto/proxy_multiprocess/subclass.rep b/tests/auto/proxy_multiprocess/subclass.rep
index 7055944..2538b88 100644
--- a/tests/auto/proxy_multiprocess/subclass.rep
+++ b/tests/auto/proxy_multiprocess/subclass.rep
@@ -1,3 +1,10 @@
+#include <QPoint>
+#include "../namespace.h"
+
+USE_ENUM(Qt::DateFormat)
+USE_ENUM(NS::NamespaceEnum)
+USE_ENUM(NS2::NamespaceEnum)
+
POD MyPOD(int i, float f, QString s)
POD VariantPOD(int i, int j)
@@ -9,17 +16,22 @@ class SubClass
class ParentClass
{
- // We need several Enums to test dynamic registration
- ENUM MyEnumProp {foo=1, bar=3, foobar=6}
- ENUM MyEnumSignal {a=1, b, c}
+ ENUM MyEnum {foo=1, bar=3, foobar=6}
PROP(bool started = false)
- PROP(MyEnumProp myEnum=foo)
+ PROP(MyEnum myEnum=foo)
PROP(QVariant variant)
+ PROP(Qt::DateFormat date = Qt::ISODate)
+ PROP(NS::NamespaceEnum nsEnum = NS::Alpha)
+ PROP(NS2::NamespaceEnum ns2Enum = NS2::NamespaceEnum::Alpha)
SLOT(bool start())
SLOT(bool quit())
SIGNAL(advance())
- SIGNAL(enum2(MyEnumSignal myEnumSignal, MyEnumSignal sig2))
+ SIGNAL(enum2(MyEnum myEnum1, MyEnum myEnum2))
+ SLOT(MyEnum enumSlot(QPoint point, MyEnum myEnum))
+
+ SIGNAL(updateDate(Qt::DateFormat date1, Qt::DateFormat date2))
+ SLOT(Qt::DateFormat dateSlot(Qt::DateFormat date))
CLASS subClass(SubClass)
MODEL tracks(display)
diff --git a/tools/repc/repcodegenerator.cpp b/tools/repc/repcodegenerator.cpp
index 870033c..fd208c0 100644
--- a/tools/repc/repcodegenerator.cpp
+++ b/tools/repc/repcodegenerator.cpp
@@ -859,7 +859,7 @@ void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass
out << " void " << property.name << "Changed(" << fullyQualifiedTypeName(astClass, className, typeForMode(property, mode)) << " " << property.name << ");" << endl;
}
- QVector<ASTFunction> signalsList = transformEnumParams(astClass, astClass.signalsList, className);
+ const QVector<ASTFunction> signalsList = transformEnumParams(astClass, astClass.signalsList, className);
Q_FOREACH (const ASTFunction &signal, signalsList)
out << " void " << signal.name << "(" << signal.paramsAsString() << ");" << endl;
@@ -883,15 +883,16 @@ void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass
out << "public Q_SLOTS:" << endl;
Q_FOREACH (const ASTProperty &property, astClass.properties) {
if (property.modifier == ASTProperty::ReadPush) {
+ const auto type = fullyQualifiedTypeName(astClass, className, property.type);
if (mode != REPLICA) {
- out << " virtual void push" << cap(property.name) << "(" << property.type << " " << property.name << ")" << endl;
+ out << " virtual void push" << cap(property.name) << "(" << type << " " << property.name << ")" << endl;
out << " {" << endl;
out << " set" << cap(property.name) << "(" << property.name << ");" << endl;
out << " }" << endl;
} else {
- out << " void push" << cap(property.name) << "(" << property.type << " " << property.name << ")" << endl;
+ out << " void push" << cap(property.name) << "(" << type << " " << property.name << ")" << endl;
out << " {" << endl;
- out << " static int __repc_index = " << className << "::staticMetaObject.indexOfSlot(\"push" << cap(property.name) << "(" << property.type << ")\");" << endl;
+ out << " static int __repc_index = " << className << "::staticMetaObject.indexOfSlot(\"push" << cap(property.name) << "(" << type << ")\");" << endl;
out << " QVariantList __repc_args;" << endl;
out << " __repc_args << QVariant::fromValue(" << property.name << ");" << endl;
out << " send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);" << endl;
@@ -899,9 +900,11 @@ void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass
}
}
}
- Q_FOREACH (const ASTFunction &slot, astClass.slotsList) {
+ const QVector<ASTFunction> slotsList = transformEnumParams(astClass, astClass.slotsList, className);
+ Q_FOREACH (const ASTFunction &slot, slotsList) {
+ const auto returnType = fullyQualifiedTypeName(astClass, className, slot.returnType);
if (mode != REPLICA) {
- out << " virtual " << slot.returnType << " " << slot.name << "(" << slot.paramsAsString() << ") = 0;" << endl;
+ out << " virtual " << returnType << " " << slot.name << "(" << slot.paramsAsString() << ") = 0;" << endl;
} else {
// TODO: Discuss whether it is a good idea to special-case for void here,
const bool isVoid = slot.returnType == QStringLiteral("void");
@@ -909,7 +912,7 @@ void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass
if (isVoid)
out << " void " << slot.name << "(" << slot.paramsAsString() << ")" << endl;
else
- out << " QRemoteObjectPendingReply<" << slot.returnType << "> " << slot.name << "(" << slot.paramsAsString()<< ")" << endl;
+ out << " QRemoteObjectPendingReply<" << returnType << "> " << slot.name << "(" << slot.paramsAsString()<< ")" << endl;
out << " {" << endl;
out << " static int __repc_index = " << className << "::staticMetaObject.indexOfSlot(\"" << slot.name << "(" << slot.paramsAsString(ASTFunction::Normalized) << ")\");" << endl;
out << " QVariantList __repc_args;" << endl;
@@ -922,7 +925,7 @@ void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass
if (isVoid)
out << " send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);" << endl;
else
- out << " return QRemoteObjectPendingReply<" << slot.returnType << ">(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));" << endl;
+ out << " return QRemoteObjectPendingReply<" << returnType << ">(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));" << endl;
out << " }" << endl;
}
}
@@ -1284,8 +1287,12 @@ void RepCodeGenerator::generateSourceAPI(QTextStream &out, const ASTClass &astCl
for (int i = 0; i < slotCount; ++i)
{
const ASTFunction &slot = astClass.slotsList.at(i);
- out << QString::fromLatin1(" case %1: return QByteArrayLiteral(\"%2\");")
- .arg(QString::number(i+pushCount), slot.returnType) << endl;
+ if (isClassEnum(astClass, slot.returnType))
+ out << QString::fromLatin1(" case %1: return QByteArrayLiteral(\"$1\").replace(\"$1\", QtPrivate::qtro_enum_signature<ObjectType>(\"%2\"));")
+ .arg(QString::number(i+pushCount), slot.returnType) << endl;
+ else
+ out << QString::fromLatin1(" case %1: return QByteArrayLiteral(\"%2\");")
+ .arg(QString::number(i+pushCount), slot.returnType) << endl;
}
out << QStringLiteral(" }") << endl;
} else