summaryrefslogtreecommitdiffstats
path: root/src/remoteobjects/qremoteobjectsource.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/remoteobjects/qremoteobjectsource.h')
-rw-r--r--src/remoteobjects/qremoteobjectsource.h79
1 files changed, 78 insertions, 1 deletions
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;