summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArno Rehn <a.rehn@menlosystems.com>2019-01-15 18:07:18 +0100
committerMilian Wolff <milian.wolff@kdab.com>2019-03-29 21:06:14 +0000
commitd91fd4fc4e57a22e1d268e4602017f629cfccf46 (patch)
tree9a50d8d1fc4320008a62e5d6cc4b81ee18eeabf5
parent984c4e6b4dd05561bb39f6daf305e520dfa9f0e6 (diff)
downloadqtwebchannel-d91fd4fc4e57a22e1d268e4602017f629cfccf46.tar.gz
Implement actual overload resolution
This implements host-side overload resolution. If a client invokes a method by its name instead of its id, the overload resolution tries to find the best match for the given arguments. The JavaScript client implementation now defaults to invocation-by-name, except when a method is invoked by its full signature. In that case, the invocation is still performed by method id. Change-Id: I09f12bdbfee2e84ff66a1454608468113f96e3ed Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
-rw-r--r--examples/webchannel/shared/qwebchannel.js6
-rw-r--r--src/webchannel/doc/src/javascript.qdoc20
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp183
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h38
-rw-r--r--tests/auto/webchannel/tst_webchannel.cpp83
-rw-r--r--tests/auto/webchannel/tst_webchannel.h6
6 files changed, 293 insertions, 43 deletions
diff --git a/examples/webchannel/shared/qwebchannel.js b/examples/webchannel/shared/qwebchannel.js
index 800a66e..9108a61 100644
--- a/examples/webchannel/shared/qwebchannel.js
+++ b/examples/webchannel/shared/qwebchannel.js
@@ -339,6 +339,10 @@ function QObject(name, data, webChannel)
{
var methodName = methodData[0];
var methodIdx = methodData[1];
+
+ // Fully specified methods are invoked by id, others by name for host-side overload resolution
+ var invokedMethod = methodName[methodName.length - 1] === ')' ? methodIdx : methodName
+
object[methodName] = function() {
var args = [];
var callback;
@@ -357,7 +361,7 @@ function QObject(name, data, webChannel)
webChannel.exec({
"type": QWebChannelMessageTypes.invokeMethod,
"object": object.__id__,
- "method": methodIdx,
+ "method": invokedMethod,
"args": args
}, function(response) {
if (response !== undefined) {
diff --git a/src/webchannel/doc/src/javascript.qdoc b/src/webchannel/doc/src/javascript.qdoc
index 9f8c580..ef44250 100644
--- a/src/webchannel/doc/src/javascript.qdoc
+++ b/src/webchannel/doc/src/javascript.qdoc
@@ -100,9 +100,15 @@ new QWebChannel(yourTransport, function(channel) {
\section2 Overloaded methods and signals
- When you publish a \c QObject that has overloaded methods or signals, then
- only the first one is accessible directly via the pretty JavaScript notation.
- All others are accessible through their complete \c QMetaMethod signature.
+ When you publish a \c QObject that has overloaded methods, QWebChannel will resolve
+ method invocations to the best match. Note that due to JavaScript's type system, there is only
+ a single 'number' type which maps best to a C++ 'double'. When overloads differ only in the type
+ of a number-like parameter, QWebChannel will always choose that overload which best matches the
+ JavaScript 'number' type.
+ When you connect to an overloaded signal, the QWebChannel client will by default only connect to
+ the first signal overload of that name.
+ Additionally, overloads of methods and signals can explicitly be requested by their complete
+ \c QMetaMethod signature.
Assume we have the following \c QObject subclass on the C++ side:
\code
@@ -111,6 +117,7 @@ new QWebChannel(yourTransport, function(channel) {
Q_OBJECT
slots:
void foo(int i);
+ void foo(double d);
void foo(const QString &str);
void foo(const QString &str, int i);
@@ -125,9 +132,10 @@ new QWebChannel(yourTransport, function(channel) {
\code
// methods
- foo.foo(42); // will call first method named foo, i.e. foo(int i)
- foo.foo("asdf"); // will also call foo(int i), probably not what you want
- foo["foo(int)"](42); // explicitly call foo(int i)
+ foo.foo(42); // will call the method named foo which best matches the JavaScript number parameter, i.e. foo(double d)
+ foo.foo("asdf"); // will call foo(const QString &str)
+ foo.foo("asdf", 42); // will call foo(const QString &str, int i)
+ foo["foo(int)"](42); // explicitly call foo(int i), *not* foo(double d)
foo["foo(QString)"]("asdf"); // explicitly call foo(const QString &str)
foo["foo(QString,int)"]("asdf", 42); // explicitly call foo(const QString &str, int i)
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index b0581c4..9f5e9cd 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -83,6 +83,65 @@ bool isQFlagsType(uint id)
return mo->indexOfEnumerator(name.constData()) > -1;
}
+// Common scores for overload resolution
+enum OverloadScore {
+ PerfectMatchScore = 0,
+ VariantScore = 1,
+ NumberBaseScore = 2,
+ GenericConversionScore = 100,
+ IncompatibleScore = 10000,
+};
+
+// Scores the conversion of a double to a number-like user type. Better matches
+// for a JS 'number' get a lower score.
+int doubleToNumberConversionScore(int userType)
+{
+ switch (userType) {
+ case QMetaType::Bool:
+ return NumberBaseScore + 7;
+ case QMetaType::Char:
+ case QMetaType::SChar:
+ case QMetaType::UChar:
+ return NumberBaseScore + 6;
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ return NumberBaseScore + 5;
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ return NumberBaseScore + 4;
+ case QMetaType::Long:
+ case QMetaType::ULong:
+ return NumberBaseScore + 3;
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ return NumberBaseScore + 2;
+ case QMetaType::Float:
+ return NumberBaseScore + 1;
+ case QMetaType::Double:
+ return NumberBaseScore;
+ default:
+ break;
+ }
+
+ if (QMetaType::typeFlags(userType) & QMetaType::IsEnumeration)
+ return doubleToNumberConversionScore(QMetaType::Int);
+
+ return IncompatibleScore;
+}
+
+// Keeps track of the badness of a QMetaMethod candidate for overload resolution
+struct OverloadResolutionCandidate
+{
+ OverloadResolutionCandidate(const QMetaMethod &method = QMetaMethod(), int badness = PerfectMatchScore)
+ : method(method), badness(badness)
+ {}
+
+ QMetaMethod method;
+ int badness;
+
+ bool operator<(const OverloadResolutionCandidate &other) const { return badness < other.badness; }
+};
+
MessageType toType(const QJsonValue &value)
{
int i = value.toInt(-1);
@@ -122,6 +181,8 @@ QJsonObject createResponse(const QJsonValue &id, const QJsonValue &data)
const int PROPERTY_UPDATE_INTERVAL = 50;
}
+Q_DECLARE_TYPEINFO(OverloadResolutionCandidate, Q_MOVABLE_TYPE);
+
QMetaObjectPublisher::QMetaObjectPublisher(QWebChannel *webChannel)
: QObject(webChannel)
, webChannel(webChannel)
@@ -368,17 +429,15 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates()
}
}
-QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
+QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMethod &method,
const QJsonArray &args)
{
- const QMetaMethod &method = object->metaObject()->method(methodIndex);
-
if (method.name() == QByteArrayLiteral("deleteLater")) {
// invoke `deleteLater` on wrapped QObject indirectly
deleteWrappedObject(object);
return QJsonValue();
} else if (!method.isValid()) {
- qWarning() << "Cannot invoke unknown method of index" << methodIndex << "on object" << object << '.';
+ qWarning() << "Cannot invoke invalid method on object" << object << '.';
return QJsonValue();
} else if (method.access() != QMetaMethod::Public) {
qWarning() << "Cannot invoke non-public method" << method.name() << "on object" << object << '.';
@@ -422,6 +481,55 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int met
return returnValue;
}
+QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
+ const QJsonArray &args)
+{
+ const QMetaMethod &method = object->metaObject()->method(methodIndex);
+ if (!method.isValid()) {
+ qWarning() << "Cannot invoke method of unknown index" << methodIndex << "on object"
+ << object << '.';
+ return QJsonValue();
+ }
+ return invokeMethod(object, method, args);
+}
+
+QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QByteArray &methodName,
+ const QJsonArray &args)
+{
+ QVector<OverloadResolutionCandidate> candidates;
+
+ const QMetaObject *mo = object->metaObject();
+ for (int i = 0; i < mo->methodCount(); ++i) {
+ QMetaMethod method = mo->method(i);
+ if (method.name() != methodName || method.parameterCount() != args.count()
+ || method.access() != QMetaMethod::Public
+ || (method.methodType() != QMetaMethod::Method
+ && method.methodType() != QMetaMethod::Slot)
+ || method.parameterCount() > 10)
+ {
+ // Not a candidate
+ continue;
+ }
+
+ candidates.append({method, methodOverloadBadness(method, args)});
+ }
+
+ if (candidates.isEmpty()) {
+ qWarning() << "No candidates found for" << methodName << "with" << args.size()
+ << "arguments on object" << object << '.';
+ return QJsonValue();
+ }
+
+ 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);
+}
+
void QMetaObjectPublisher::setProperty(QObject *object, const int propertyIndex, const QJsonValue &value)
{
QMetaProperty property = object->metaObject()->property(propertyIndex);
@@ -534,6 +642,57 @@ QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType
return variant;
}
+int QMetaObjectPublisher::conversionScore(const QJsonValue &value, int targetType) const
+{
+ if (targetType == QMetaType::QJsonValue) {
+ return PerfectMatchScore;
+ } else if (targetType == QMetaType::QJsonArray) {
+ return value.isArray() ? PerfectMatchScore : IncompatibleScore;
+ } else if (targetType == QMetaType::QJsonObject) {
+ return value.isObject() ? PerfectMatchScore : IncompatibleScore;
+ } else if (QMetaType::typeFlags(targetType) & QMetaType::PointerToQObject) {
+ if (value.isNull())
+ return PerfectMatchScore;
+ if (!value.isObject())
+ return IncompatibleScore;
+
+ QJsonObject object = value.toObject();
+ if (object[KEY_ID].isUndefined())
+ return IncompatibleScore;
+
+ QObject *unwrappedObject = unwrapObject(object[KEY_ID].toString());
+ return unwrappedObject != Q_NULLPTR ? PerfectMatchScore : IncompatibleScore;
+ } else if (targetType == QMetaType::QVariant) {
+ return VariantScore;
+ }
+
+ // Check if this is a number conversion
+ if (value.isDouble()) {
+ int score = doubleToNumberConversionScore(targetType);
+ if (score != IncompatibleScore) {
+ return score;
+ }
+ }
+
+ QVariant variant = value.toVariant();
+ if (variant.userType() == targetType) {
+ return PerfectMatchScore;
+ } else if (variant.canConvert(targetType)) {
+ return GenericConversionScore;
+ }
+
+ return IncompatibleScore;
+}
+
+int QMetaObjectPublisher::methodOverloadBadness(const QMetaMethod &method, const QJsonArray &args) const
+{
+ int badness = PerfectMatchScore;
+ for (int i = 0; i < args.size(); ++i) {
+ badness += conversionScore(args[i], method.parameterType(i));
+ }
+ return badness;
+}
+
void QMetaObjectPublisher::transportRemoved(QWebChannelAbstractTransport *transport)
{
auto it = transportedWrappedObjects.find(transport);
@@ -715,10 +874,18 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel
QPointer<QMetaObjectPublisher> publisherExists(this);
QPointer<QWebChannelAbstractTransport> transportExists(transport);
- QVariant result =
- invokeMethod(object,
- message.value(KEY_METHOD).toInt(-1),
- message.value(KEY_ARGS).toArray());
+ QJsonValue method = message.value(KEY_METHOD);
+ QVariant result;
+
+ if (method.isString()) {
+ result = invokeMethod(object,
+ method.toString().toUtf8(),
+ message.value(KEY_ARGS).toArray());
+ } else {
+ result = invokeMethod(object,
+ method.toInt(-1),
+ message.value(KEY_ARGS).toArray());
+ }
if (!publisherExists || !transportExists)
return;
transport->sendMessage(createResponse(message.value(KEY_ID), wrapResult(result, transport)));
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 23a9b96..6030de2 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -148,6 +148,14 @@ public:
void sendPendingPropertyUpdates();
/**
+ * 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
+ * is returned.
+ */
+ QVariant invokeMethod(QObject *const object, const QMetaMethod &method, const QJsonArray &args);
+
+ /**
* Invoke the method of index @p methodIndex on @p object with the arguments @p args.
*
* The return value of the method invocation is then serialized and a response message
@@ -156,6 +164,16 @@ public:
QVariant invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args);
/**
+ * Invoke the method of name @p methodName on @p object with the arguments @p args.
+ *
+ * This method performs overload resolution on @p methodName.
+ *
+ * The return value of the method invocation is then serialized and a response message
+ * is returned.
+ */
+ QVariant invokeMethod(QObject *const object, const QByteArray &methodName, const QJsonArray &args);
+
+ /**
* Set the value of property @p propertyIndex on @p object to @p value.
*/
void setProperty(QObject *object, const int propertyIndex, const QJsonValue &value);
@@ -177,6 +195,26 @@ public:
QVariant toVariant(const QJsonValue &value, int targetType) const;
/**
+ * Assigns a score for the conversion from @p value to @p targetType.
+ *
+ * Scores can be compared to find the best match. The lower the score, the
+ * more preferable is the conversion.
+ *
+ * @sa invokeMethod, methodOverloadBadness
+ */
+ int conversionScore(const QJsonValue &value, int targetType) const;
+
+ /**
+ * Scores @p method against @p args.
+ *
+ * Scores can be compared to find the best match from a set of overloads.
+ * The lower the score, the more preferable is the method.
+ *
+ * @sa invokeMethod, conversionScore
+ */
+ int methodOverloadBadness(const QMetaMethod &method, const QJsonArray &args) const;
+
+ /**
* Remove wrapped objects which last transport relation is with the passed transport object.
*/
void transportRemoved(QWebChannelAbstractTransport *transport);
diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp
index 24778c7..4e622c7 100644
--- a/tests/auto/webchannel/tst_webchannel.cpp
+++ b/tests/auto/webchannel/tst_webchannel.cpp
@@ -383,9 +383,12 @@ void TestWebChannel::testInfoForObject()
addMethod(QStringLiteral("setObjectProperty"), "setObjectProperty(QObject*)");
addMethod(QStringLiteral("setProp"), "setProp(QString)");
addMethod(QStringLiteral("fire"), "fire()");
- addMethod(QStringLiteral("overload"), "overload(int)");
+ addMethod(QStringLiteral("overload"), "overload(double)");
+ addMethod(QStringLiteral("overload"), "overload(int)", false);
+ addMethod(QStringLiteral("overload"), "overload(QObject*)", false);
addMethod(QStringLiteral("overload"), "overload(QString)", false);
addMethod(QStringLiteral("overload"), "overload(QString,int)", false);
+ addMethod(QStringLiteral("overload"), "overload(QJsonArray)", false);
addMethod(QStringLiteral("method1"), "method1()");
QCOMPARE(info["methods"].toArray(), expected);
}
@@ -506,9 +509,7 @@ void TestWebChannel::testInvokeMethodConversion()
args.append(QJsonValue(1000));
{
- int method = metaObject()->indexOfMethod("setInt(int)");
- QVERIFY(method != -1);
- channel.d_func()->publisher->invokeMethod(this, method, args);
+ channel.d_func()->publisher->invokeMethod(this, "setInt", args);
QCOMPARE(m_lastInt, args.at(0).toInt());
int getterMethod = metaObject()->indexOfMethod("readInt()");
QVERIFY(getterMethod != -1);
@@ -516,11 +517,9 @@ void TestWebChannel::testInvokeMethodConversion()
QCOMPARE(retVal, args.at(0).toVariant());
}
{
- int method = metaObject()->indexOfMethod("setBool(bool)");
- QVERIFY(method != -1);
QJsonArray args;
args.append(QJsonValue(!m_lastBool));
- channel.d_func()->publisher->invokeMethod(this, method, args);
+ channel.d_func()->publisher->invokeMethod(this, "setBool", args);
QCOMPARE(m_lastBool, args.at(0).toBool());
int getterMethod = metaObject()->indexOfMethod("readBool()");
QVERIFY(getterMethod != -1);
@@ -528,9 +527,7 @@ void TestWebChannel::testInvokeMethodConversion()
QCOMPARE(retVal, args.at(0).toVariant());
}
{
- int method = metaObject()->indexOfMethod("setDouble(double)");
- QVERIFY(method != -1);
- channel.d_func()->publisher->invokeMethod(this, method, args);
+ channel.d_func()->publisher->invokeMethod(this, "setDouble", args);
QCOMPARE(m_lastDouble, args.at(0).toDouble());
int getterMethod = metaObject()->indexOfMethod("readDouble()");
QVERIFY(getterMethod != -1);
@@ -538,9 +535,7 @@ void TestWebChannel::testInvokeMethodConversion()
QCOMPARE(retVal, args.at(0).toVariant());
}
{
- int method = metaObject()->indexOfMethod("setVariant(QVariant)");
- QVERIFY(method != -1);
- channel.d_func()->publisher->invokeMethod(this, method, args);
+ channel.d_func()->publisher->invokeMethod(this, "setVariant", args);
QCOMPARE(m_lastVariant, args.at(0).toVariant());
int getterMethod = metaObject()->indexOfMethod("readVariant()");
QVERIFY(getterMethod != -1);
@@ -548,9 +543,7 @@ void TestWebChannel::testInvokeMethodConversion()
QCOMPARE(retVal, args.at(0).toVariant());
}
{
- int method = metaObject()->indexOfMethod("setJsonValue(QJsonValue)");
- QVERIFY(method != -1);
- channel.d_func()->publisher->invokeMethod(this, method, args);
+ channel.d_func()->publisher->invokeMethod(this, "setJsonValue", args);
QCOMPARE(m_lastJsonValue, args.at(0));
int getterMethod = metaObject()->indexOfMethod("readJsonValue()");
QVERIFY(getterMethod != -1);
@@ -558,13 +551,11 @@ void TestWebChannel::testInvokeMethodConversion()
QCOMPARE(retVal, args.at(0).toVariant());
}
{
- int method = metaObject()->indexOfMethod("setJsonObject(QJsonObject)");
- QVERIFY(method != -1);
QJsonObject object;
object["foo"] = QJsonValue(123);
object["bar"] = QJsonValue(4.2);
args[0] = object;
- channel.d_func()->publisher->invokeMethod(this, method, args);
+ channel.d_func()->publisher->invokeMethod(this, "setJsonObject", args);
QCOMPARE(m_lastJsonObject, object);
int getterMethod = metaObject()->indexOfMethod("readJsonObject()");
QVERIFY(getterMethod != -1);
@@ -572,13 +563,11 @@ void TestWebChannel::testInvokeMethodConversion()
QCOMPARE(retVal, QVariant::fromValue(object));
}
{
- int setterMethod = metaObject()->indexOfMethod("setJsonArray(QJsonArray)");
- QVERIFY(setterMethod != -1);
QJsonArray array;
array << QJsonValue(123);
array << QJsonValue(4.2);
args[0] = array;
- channel.d_func()->publisher->invokeMethod(this, setterMethod, args);
+ channel.d_func()->publisher->invokeMethod(this, "setJsonArray", args);
QCOMPARE(m_lastJsonArray, array);
int getterMethod = metaObject()->indexOfMethod("readJsonArray()");
QVERIFY(getterMethod != -1);
@@ -674,6 +663,49 @@ void TestWebChannel::testSetPropertyConversion()
}
}
+void TestWebChannel::testInvokeMethodOverloadResolution()
+{
+ QWebChannel channel;
+ TestObject testObject;
+ TestObject exportedObject;
+ channel.registerObject("test", &exportedObject);
+ channel.connectTo(m_dummyTransport);
+
+ QVariant result;
+ QMetaObjectPublisher *publisher = channel.d_func()->publisher;
+
+ {
+ result = publisher->invokeMethod(&testObject, "overload", { 41.0 });
+ QVERIFY(result.userType() == QMetaType::Double);
+ QCOMPARE(result.toDouble(), 42.0);
+ }
+ {
+ // In JavaScript, there's only 'double', so this should always invoke the 'double' overload
+ result = publisher->invokeMethod(&testObject, "overload", { 41 });
+ QVERIFY(result.userType() == QMetaType::Double);
+ QCOMPARE(result.toDouble(), 42);
+ }
+ {
+ QJsonObject wrappedObject { {"id", "test"} };
+ result = publisher->invokeMethod(&testObject, "overload", { wrappedObject });
+ QCOMPARE(result.value<TestObject*>(), &exportedObject);
+ }
+ {
+ result = publisher->invokeMethod(&testObject, "overload", { "hello world" });
+ QCOMPARE(result.toString(), QStringLiteral("HELLO WORLD"));
+ }
+ {
+ result = publisher->invokeMethod(&testObject, "overload", { "the answer is ", 41 });
+ QCOMPARE(result.toString(), QStringLiteral("THE ANSWER IS 42"));
+ }
+ {
+ QJsonArray args;
+ args.append(QJsonArray { "foobar", 42 });
+ result = publisher->invokeMethod(&testObject, "overload", args);
+ QCOMPARE(result.toString(), QStringLiteral("42foobar"));
+ }
+}
+
void TestWebChannel::testDisconnect()
{
QWebChannel channel;
@@ -771,7 +803,7 @@ void TestWebChannel::testPassWrappedObjectBack()
QJsonObject argProperty;
argProperty["id"] = returnedObjPropertyInfo["id"];
- pub->invokeMethod(&registeredObj, registeredObj.metaObject()->indexOfSlot("setReturnedObject(TestObject*)"), argsMethod);
+ pub->invokeMethod(&registeredObj, "setReturnedObject", argsMethod);
QCOMPARE(registeredObj.mReturnedObject, &returnedObjMethod);
pub->setProperty(&registeredObj, registeredObj.metaObject()->indexOfProperty("returnedObject"), argProperty);
QCOMPARE(registeredObj.mReturnedObject, &returnedObjProperty);
@@ -858,12 +890,9 @@ void TestWebChannel::testAsyncObject()
QJsonArray args;
args.append(QJsonValue("message"));
- int method = obj.metaObject()->indexOfMethod("setProp(QString)");
- QVERIFY(method != -1);
-
{
QSignalSpy spy(&obj, &TestObject::propChanged);
- channel.d_func()->publisher->invokeMethod(&obj, method, args);
+ channel.d_func()->publisher->invokeMethod(&obj, "setProp", args);
QTRY_COMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).at(0).toString(), args.at(0).toString());
}
diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h
index ed769e9..8ca1cdd 100644
--- a/tests/auto/webchannel/tst_webchannel.h
+++ b/tests/auto/webchannel/tst_webchannel.h
@@ -158,9 +158,12 @@ public slots:
void setProp(const QString&prop) {emit propChanged(mProp=prop);}
void fire() {emit replay();}
- int overload(int i) { return i + 1; }
+ double overload(double d) { return d + 1; }
+ int overload(int i) { return i * 2; }
+ QObject *overload(QObject *object) { return object; }
QString overload(const QString &str) { return str.toUpper(); }
QString overload(const QString &str, int i) { return str.toUpper() + QString::number(i + 1); }
+ QString overload(const QJsonArray &v) { return QString::number(v[1].toInt()) + v[0].toString(); }
protected slots:
void slot3() {}
@@ -320,6 +323,7 @@ private slots:
void testInvokeMethodConversion();
void testFunctionOverloading();
void testSetPropertyConversion();
+ void testInvokeMethodOverloadResolution();
void testDisconnect();
void testWrapRegisteredObject();
void testUnwrapObject();