aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2022-07-16 13:13:03 -0700
committerThiago Macieira <thiago.macieira@intel.com>2022-08-25 21:55:50 -0700
commit40a8e2cb8cc1624b6c4f15f6d4070c2027a1e774 (patch)
treeb647570b81c100ad1a46a049ea39502c071402af
parentcb11923e53e417a28dbb87a9e2f164f15c01fec2 (diff)
QMetaObjectPublisher: use new way to QMetaObject::invokeMethod
The new API isn't limited to 10 parameters. It also does its own parameter matching, which we use here to locate the method to be called. I don't think that was necessary, though, because we sort the methods to be called in order of preference. Fixes: QTBUG-105596 Change-Id: I36b24183fbd041179f2ffffd170268620633a72b Reviewed-by: Arno Rehn <a.rehn@menlosystems.com>
-rw-r--r--src/webchannel/CMakeLists.txt1
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp97
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h10
-rw-r--r--src/webchannel/variantargument_p.h45
4 files changed, 70 insertions, 83 deletions
diff --git a/src/webchannel/CMakeLists.txt b/src/webchannel/CMakeLists.txt
index 60f3492..9354973 100644
--- a/src/webchannel/CMakeLists.txt
+++ b/src/webchannel/CMakeLists.txt
@@ -13,7 +13,6 @@ qt_internal_add_module(WebChannel
qwebchannel.cpp qwebchannel.h qwebchannel_p.h
qwebchannelabstracttransport.cpp qwebchannelabstracttransport.h
signalhandler_p.h
- variantargument_p.h
qwebchannelglobal.h
LIBRARIES
Qt::CorePrivate
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index c97b504..62d5319 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -20,6 +20,8 @@
#endif
#include <QUuid>
+#include <QtCore/private/qmetaobject_p.h>
+
QT_BEGIN_NAMESPACE
namespace {
@@ -507,6 +509,63 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates()
sendEnqueuedPropertyUpdates(state.key());
}
+QVariant QMetaObjectPublisher::invokeMethod_helper(QObject *const object, const QMetaMethod &method,
+ const QJsonArray &args)
+{
+ // a good value for the number of arguments we'll preallocate in QVLA
+ constexpr qsizetype ArgumentCount = 16;
+
+ QVarLengthArray<QVariant, ArgumentCount> variants;
+ QVarLengthArray<const char *, ArgumentCount> names(method.parameterCount() + 1);
+ QVarLengthArray<void *, ArgumentCount> parameters(names.size());
+ variants.reserve(names.size());
+ variants << QVariant();
+
+ // start with the formal parameters
+ for (qsizetype i = 0; i < names.size() - 1; ++i) {
+ QMetaType mt = method.parameterMetaType(i);
+ QVariant &v = variants.emplace_back(toVariant(args.at(i), mt.id()));
+ parameters[i + 1] = v.data();
+ names[i + 1] = mt.name();
+ }
+
+ // now, the return type
+ QMetaType mt = method.returnMetaType();
+ names[0] = mt.name();
+ if (int id = mt.id(); id != QMetaType::Void) {
+ // Only init variant with return type if its not a variant itself,
+ // which would lead to nested variants which is not what we want.
+ if (id == QMetaType::QVariant) {
+ parameters[0] = &variants[0];
+ } else {
+ variants[0] = QVariant(mt);
+ parameters[0] = variants[0].data();
+ }
+ } else {
+ parameters[0] = nullptr;
+ }
+
+ // step 3: make the call
+ QMetaMethodInvoker::InvokeFailReason r =
+ QMetaMethodInvoker::invokeImpl(method, object, Qt::AutoConnection,
+ parameters.size(), parameters.constData(),
+ names.constData());
+
+ if (r == QMetaMethodInvoker::InvokeFailReason::None)
+ return variants.first();
+
+ // print warnings for failures to match
+ if (int(r) >= int(QMetaMethodInvoker::InvokeFailReason::FormalParameterMismatch)) {
+ int n = int(r) - int(QMetaMethodInvoker::InvokeFailReason::FormalParameterMismatch);
+ QByteArray callee = object->metaObject()->className() + QByteArrayView("::")
+ + method.methodSignature();
+ qWarning() << "Cannot convert formal parameter" << n << "from" << names[n + 1]
+ << "in call to" << callee.constData();
+ }
+
+ return QJsonValue();
+}
+
QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMethod &method,
const QJsonArray &args)
{
@@ -523,41 +582,12 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMe
} else if (method.methodType() != QMetaMethod::Method && method.methodType() != QMetaMethod::Slot) {
qWarning() << "Cannot invoke non-public method" << method.name() << "on object" << object << '.';
return QJsonValue();
- } else if (args.size() > 10) {
- qWarning() << "Cannot invoke method" << method.name() << "on object" << object << "with more than 10 arguments, as that is not supported by QMetaMethod::invoke.";
- return QJsonValue();
} else if (args.size() > method.parameterCount()) {
qWarning() << "Ignoring additional arguments while invoking method" << method.name() << "on object" << object << ':'
<< args.size() << "arguments given, but method only takes" << method.parameterCount() << '.';
}
- // construct converter objects of QVariant to QGenericArgument
- VariantArgument arguments[10];
- for (int i = 0; i < qMin(args.size(), method.parameterCount()); ++i) {
- arguments[i].value = toVariant(args.at(i), method.parameterType(i));
- arguments[i].type = method.parameterType(i);
- }
- // construct QGenericReturnArgument
- QVariant returnValue;
- if (method.returnType() == QMetaType::Void) {
- // Skip return for void methods (prevents runtime warnings inside Qt), and allows
- // QMetaMethod to invoke void-returning methods on QObjects in a different thread.
- method.invoke(object,
- arguments[0], arguments[1], arguments[2], arguments[3], arguments[4],
- arguments[5], arguments[6], arguments[7], arguments[8], arguments[9]);
- } else {
- // Only init variant with return type if its not a variant itself, which would
- // lead to nested variants which is not what we want.
- if (method.returnType() != QMetaType::QVariant)
- returnValue = QVariant(QMetaType(method.returnType()), nullptr);
-
- QGenericReturnArgument returnArgument(method.typeName(), returnValue.data());
- method.invoke(object, returnArgument,
- arguments[0], arguments[1], arguments[2], arguments[3], arguments[4],
- arguments[5], arguments[6], arguments[7], arguments[8], arguments[9]);
- }
- // now we can call the method
- return returnValue;
+ return invokeMethod_helper(object, method, args);
}
QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
@@ -583,8 +613,7 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QByteAr
if (method.name() != methodName || method.parameterCount() != args.count()
|| method.access() != QMetaMethod::Public
|| (method.methodType() != QMetaMethod::Method
- && method.methodType() != QMetaMethod::Slot)
- || method.parameterCount() > 10)
+ && method.methodType() != QMetaMethod::Slot))
{
// Not a candidate
continue;
@@ -600,13 +629,13 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QByteAr
}
std::sort(candidates.begin(), candidates.end());
-
if (candidates.size() > 1 && candidates[0].badness == candidates[1].badness) {
qWarning().nospace() << "Ambiguous overloads for method " << methodName << ". Choosing "
<< candidates.first().method.methodSignature();
+
}
- return invokeMethod(object, candidates.first().method, args);
+ return invokeMethod_helper(object, candidates.first().method, args);
}
void QMetaObjectPublisher::setProperty(QObject *object, const int propertyIndex, const QJsonValue &value)
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 2029225..8605798 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -15,7 +15,7 @@
// We mean it.
//
-#include "variantargument_p.h"
+#include "qwebchannelglobal.h"
#include "signalhandler_p.h"
#include <QStringList>
@@ -29,8 +29,6 @@
#include <unordered_map>
-#include "qwebchannelglobal.h"
-
QT_BEGIN_NAMESPACE
// NOTE: keep in sync with corresponding maps in qwebchannel.js and WebChannelTest.qml
@@ -154,6 +152,12 @@ public:
void sendPendingPropertyUpdates();
/**
+ * Helper function for the invokeMehtods below
+ */
+ QVariant invokeMethod_helper(QObject *const object, const QMetaMethod &method,
+ const QJsonArray &args);
+
+ /**
* Invoke the @p method on @p object with the arguments @p args.
*
* The return value of the method invocation is then serialized and a response message
diff --git a/src/webchannel/variantargument_p.h b/src/webchannel/variantargument_p.h
deleted file mode 100644
index 09b864f..0000000
--- a/src/webchannel/variantargument_p.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef VARIANTARGUMENT_H
-#define VARIANTARGUMENT_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QVariant>
-#include <private/qglobal_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/**
- * RAII QVariant to Q[Generic]Argument conversion
- */
-struct VariantArgument
-{
- operator QGenericArgument() const
- {
- if (type == QMetaType::QVariant) {
- return QGenericArgument("QVariant", &value);
- }
- if (!value.isValid()) {
- return QGenericArgument();
- }
- return QGenericArgument(value.typeName(), value.constData());
- }
-
- QVariant value;
- int type;
-};
-
-QT_END_NAMESPACE
-
-#endif // VARIANTARGUMENT_H