aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--examples/webchannel/shared/qwebchannel.js49
-rw-r--r--src/webchannel/doc/src/javascript.qdoc48
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp230
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h53
-rw-r--r--tests/auto/qml/testobject.cpp27
-rw-r--r--tests/auto/qml/testobject.h15
-rw-r--r--tests/auto/qml/tst_webchannel.qml118
-rw-r--r--tests/auto/webchannel/tst_webchannel.cpp260
-rw-r--r--tests/auto/webchannel/tst_webchannel.h15
10 files changed, 666 insertions, 151 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 7b49e2c..879fb0f 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,4 @@
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.13.0
+MODULE_VERSION = 5.14.0
diff --git a/examples/webchannel/shared/qwebchannel.js b/examples/webchannel/shared/qwebchannel.js
index 5b047c2..fca45d9 100644
--- a/examples/webchannel/shared/qwebchannel.js
+++ b/examples/webchannel/shared/qwebchannel.js
@@ -197,10 +197,16 @@ function QObject(name, data, webChannel)
}
return ret;
}
- if (!response
- || !response["__QObject*__"]
- || response.id === undefined) {
+ if (!(response instanceof Object))
return response;
+
+ if (!response["__QObject*__"]
+ || response.id === undefined) {
+ var jObj = {};
+ for (var propName in response) {
+ jObj[propName] = object.unwrapQObject(response[propName]);
+ }
+ return jObj;
}
var objectId = response.id;
@@ -255,9 +261,16 @@ function QObject(name, data, webChannel)
object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
object.__objectSignals__[signalIndex].push(callback);
- if (!isPropertyNotifySignal && signalName !== "destroyed") {
- // only required for "pure" signals, handled separately for properties in propertyUpdate
- // also note that we always get notified about the destroyed signal
+ // only required for "pure" signals, handled separately for properties in propertyUpdate
+ if (isPropertyNotifySignal)
+ return;
+
+ // also note that we always get notified about the destroyed signal
+ if (signalName === "destroyed" || signalName === "destroyed()" || signalName === "destroyed(QObject*)")
+ return;
+
+ // and otherwise we only need to be connected only once
+ if (object.__objectSignals__[signalIndex].length == 1) {
webChannel.exec({
type: QWebChannelMessageTypes.connectToSignal,
object: object.__id__,
@@ -307,7 +320,7 @@ function QObject(name, data, webChannel)
// update property cache
for (var propertyIndex in propertyMap) {
var propertyValue = propertyMap[propertyIndex];
- object.__propertyCache__[propertyIndex] = propertyValue;
+ object.__propertyCache__[propertyIndex] = this.unwrapQObject(propertyValue);
}
for (var signalName in signals) {
@@ -326,9 +339,14 @@ 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;
+ var errCallback;
for (var i = 0; i < arguments.length; ++i) {
var argument = arguments[i];
if (typeof argument === "function")
@@ -341,10 +359,21 @@ function QObject(name, data, webChannel)
args.push(argument);
}
+ var result;
+ // during test, webChannel.exec synchronously calls the callback
+ // therefore, the promise must be constucted before calling
+ // webChannel.exec to ensure the callback is set up
+ if (!callback && (typeof(Promise) === 'function')) {
+ result = new Promise(function(resolve, reject) {
+ callback = resolve;
+ errCallback = reject;
+ });
+ }
+
webChannel.exec({
"type": QWebChannelMessageTypes.invokeMethod,
"object": object.__id__,
- "method": methodIdx,
+ "method": invokedMethod,
"args": args
}, function(response) {
if (response !== undefined) {
@@ -352,8 +381,12 @@ function QObject(name, data, webChannel)
if (callback) {
(callback)(result);
}
+ } else if (errCallback) {
+ (errCallback)();
}
});
+
+ return result;
};
}
diff --git a/src/webchannel/doc/src/javascript.qdoc b/src/webchannel/doc/src/javascript.qdoc
index e643034..ef44250 100644
--- a/src/webchannel/doc/src/javascript.qdoc
+++ b/src/webchannel/doc/src/javascript.qdoc
@@ -97,4 +97,52 @@ new QWebChannel(yourTransport, function(channel) {
console.log(foo.MyEnum.MyEnumerator);
});
\endcode
+
+ \section2 Overloaded methods and signals
+
+ 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
+ class Foo : public QObject
+ {
+ Q_OBJECT
+ slots:
+ void foo(int i);
+ void foo(double d);
+ void foo(const QString &str);
+ void foo(const QString &str, int i);
+
+ signals:
+ void bar(int i);
+ void bar(const QString &str);
+ void bar(const QString &str, int i);
+ };
+ \endcode
+
+ Then you can interact with this class on the JavaScript side like this:
+
+ \code
+ // methods
+ 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)
+
+ // signals
+ foo.bar.connect(...); // connect to first signal named bar, i.e. bar(int i)
+ foo["bar(int)"].connect(...); // connect explicitly to bar(int i)
+ foo["bar(QString)"].connect(...); // connect explicitly to bar(const QString &str)
+ foo["bar(QString,int)"].connect(...); // connect explicitly to bar(const QString &str, int i)
+ \endcode
*/
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index a77072b..102cbda 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)
@@ -196,19 +257,13 @@ QJsonObject QMetaObjectPublisher::classInfoForObject(const QObject *object, QWeb
propertyInfo.append(wrapResult(prop.read(object), transport));
qtProperties.append(propertyInfo);
}
- for (int i = 0; i < metaObject->methodCount(); ++i) {
- if (notifySignals.contains(i)) {
- continue;
- }
- const QMetaMethod &method = metaObject->method(i);
- //NOTE: this must be a string, otherwise it will be converted to '{}' in QML
- const QString &name = QString::fromLatin1(method.name());
- // optimize: skip overloaded methods/signals or property getters, on the JS side we can only
- // call one of them anyways
- // TODO: basic support for overloaded signals, methods
- if (identifiers.contains(name)) {
- continue;
- }
+ auto addMethod = [&qtSignals, &qtMethods, &identifiers](int i, const QMetaMethod &method, const QByteArray &rawName) {
+ //NOTE: the name must be a string, otherwise it will be converted to '{}' in QML
+ const auto name = QString::fromLatin1(rawName);
+ // only the first method gets called with its name directly
+ // others must be called by explicitly passing the method signature
+ if (identifiers.contains(name))
+ return;
identifiers << name;
// send data as array to client with format: [name, index]
QJsonArray data;
@@ -219,6 +274,15 @@ QJsonObject QMetaObjectPublisher::classInfoForObject(const QObject *object, QWeb
} else if (method.access() == QMetaMethod::Public) {
qtMethods.append(data);
}
+ };
+ for (int i = 0; i < metaObject->methodCount(); ++i) {
+ if (notifySignals.contains(i)) {
+ continue;
+ }
+ const QMetaMethod &method = metaObject->method(i);
+ addMethod(i, method, method.name());
+ // for overload resolution also pass full method signature
+ addMethod(i, method, method.methodSignature());
}
for (int i = 0; i < metaObject->enumeratorCount(); ++i) {
QMetaEnum enumerator = metaObject->enumerator(i);
@@ -365,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 << '.';
@@ -419,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);
@@ -489,7 +600,7 @@ QObject *QMetaObjectPublisher::unwrapObject(const QString &objectId) const
{
if (!objectId.isEmpty()) {
ObjectInfo objectInfo = wrappedObjects.value(objectId);
- if (objectInfo.object && !objectInfo.classinfo.isEmpty())
+ if (objectInfo.object)
return objectInfo.object;
QObject *object = registeredObjects.value(objectId);
if (object)
@@ -531,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);
@@ -574,7 +736,7 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA
classInfo = classInfoForObject(object, transport);
- ObjectInfo oi(object, classInfo);
+ ObjectInfo oi(object);
if (transport) {
oi.transports.append(transport);
} else {
@@ -595,7 +757,7 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA
wrappedObjects[id].transports.append(transport);
transportedWrappedObjects.insert(transport, id);
}
- classInfo = wrappedObjects.value(id).classinfo;
+ classInfo = classInfoForObject(object, transport);
}
QJsonObject objectInfo;
@@ -623,6 +785,9 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA
// Also, don't use QSequentialIterable (yet), since that seems to trigger QTBUG-42016
// in certain cases.
return wrapList(result.value<QVariantList>(), transport);
+ } else if (result.canConvert<QVariantMap>()) {
+ // recurse and potentially wrap contents of the map
+ return wrapMap(result.toMap(), transport);
}
return QJsonValue::fromVariant(result);
@@ -637,6 +802,15 @@ QJsonArray QMetaObjectPublisher::wrapList(const QVariantList &list, QWebChannelA
return array;
}
+QJsonObject QMetaObjectPublisher::wrapMap(const QVariantMap &map, QWebChannelAbstractTransport *transport, const QString &parentObjectId)
+{
+ QJsonObject obj;
+ for (QVariantMap::const_iterator i = map.begin(); i != map.end(); i++) {
+ obj.insert(i.key(), wrapResult(i.value(), transport, parentObjectId));
+ }
+ return obj;
+}
+
void QMetaObjectPublisher::deleteWrappedObject(QObject *object) const
{
if (!wrappedObjects.contains(registeredObjectIds.value(object))) {
@@ -703,10 +877,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 d02a933..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);
@@ -199,6 +237,14 @@ public:
const QString &parentObjectId = QString());
/**
+ * Convert a variant map for consumption by the client.
+ *
+ * This properly handles QML values and also wraps the result if required.
+ */
+ QJsonObject wrapMap(const QVariantMap &map, QWebChannelAbstractTransport *transport,
+ const QString &parentObjectId = QString());
+
+ /**
* Invoke delete later on @p object.
*/
void deleteWrappedObject(QObject *object) const;
@@ -248,15 +294,10 @@ private:
// Groups individually wrapped objects with their class information and the transports that have access to it.
struct ObjectInfo
{
- ObjectInfo()
- : object(Q_NULLPTR)
- {}
- ObjectInfo(QObject *o, const QJsonObject &i)
+ ObjectInfo(QObject *o = nullptr)
: object(o)
- , classinfo(i)
{}
QObject *object;
- QJsonObject classinfo;
QVector<QWebChannelAbstractTransport*> transports;
};
diff --git a/tests/auto/qml/testobject.cpp b/tests/auto/qml/testobject.cpp
index 2be7773..ad302e7 100644
--- a/tests/auto/qml/testobject.cpp
+++ b/tests/auto/qml/testobject.cpp
@@ -33,13 +33,22 @@ QT_BEGIN_NAMESPACE
TestObject::TestObject(QObject* parent)
: QObject(parent)
+ , embeddedObject(new QObject(this))
{
+ embeddedObject->setObjectName("embedded");
}
TestObject::~TestObject()
{
}
+QVariantMap TestObject::objectMap() const
+{
+ QVariantMap map;
+ map.insert("subObject", QVariant::fromValue(embeddedObject));
+ return map;
+}
+
void TestObject::triggerSignals()
{
emit testSignalBool(true);
@@ -50,4 +59,22 @@ void TestObject::triggerSignals()
emit testSignalInt(0);
}
+int TestObject::testOverload(int i)
+{
+ emit testOverloadSignal(i);
+ return i + 1;
+}
+
+QString TestObject::testOverload(const QString &str)
+{
+ emit testOverloadSignal(str);
+ return str.toUpper();
+}
+
+QString TestObject::testOverload(const QString &str, int i)
+{
+ emit testOverloadSignal(str, i);
+ return str.toUpper() + QString::number(i + 1);
+}
+
QT_END_NAMESPACE
diff --git a/tests/auto/qml/testobject.h b/tests/auto/qml/testobject.h
index e025ea6..b9c5ecc 100644
--- a/tests/auto/qml/testobject.h
+++ b/tests/auto/qml/testobject.h
@@ -31,22 +31,37 @@
#define TESTOBJECT_H
#include <QObject>
+#include <QVariantMap>
QT_BEGIN_NAMESPACE
class TestObject : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QVariantMap objectMap READ objectMap CONSTANT)
public:
explicit TestObject(QObject *parent = Q_NULLPTR);
~TestObject();
+ QVariantMap objectMap() const;
+
public slots:
void triggerSignals();
+ int testOverload(int i);
+ QString testOverload(const QString &str);
+ QString testOverload(const QString &str, int i);
+
signals:
void testSignalBool(bool testBool);
void testSignalInt(int testInt);
+
+ void testOverloadSignal(int i);
+ void testOverloadSignal(const QString &str);
+ void testOverloadSignal(const QString &str, int i);
+
+private:
+ QObject *embeddedObject;
};
QT_END_NAMESPACE
diff --git a/tests/auto/qml/tst_webchannel.qml b/tests/auto/qml/tst_webchannel.qml
index 6da6c4b..ed1c4a1 100644
--- a/tests/auto/qml/tst_webchannel.qml
+++ b/tests/auto/qml/tst_webchannel.qml
@@ -71,6 +71,9 @@ TestCase {
lastFactoryObj = component.createObject(myFactory, {objectName: id});
return lastFactoryObj;
}
+ function switchObject() {
+ otherObject = myOtherObj;
+ }
property var objectInProperty: QtObject {
objectName: "foo"
}
@@ -84,6 +87,7 @@ TestCase {
property var myProperty : 0
function myMethod(arg) {
lastMethodArg = arg;
+ return myProperty;
}
signal mySignal(var arg1, var arg2)
}
@@ -235,6 +239,7 @@ TestCase {
var testObjBeforeDeletion;
var testObjAfterDeletion;
var testObjId;
+ var testReturn;
var channel = client.createChannel(function(channel) {
channel.objects.myFactory.create("testObj", function(obj) {
testObjId = obj.__id__;
@@ -246,7 +251,9 @@ TestCase {
testObjAfterDeletion = obj;
});
obj.myProperty = 42;
- obj.myMethod("foobar");
+ obj.myMethod("foobar").then(function(result) {
+ testReturn = result;
+ });
});
});
client.awaitInit();
@@ -285,11 +292,12 @@ TestCase {
// the server should eventually notify the client about the property update
client.awaitPropertyUpdate();
- client.awaitIdle();
+ // check that the Promise from myMethod was resolved
+ // must happen after waiting for something so the Promise callback
+ // can execute
+ compare(testReturn, 42);
- // trigger a signal and ensure it gets transmitted
- lastFactoryObj.mySignal("foobar", 42);
- client.awaitSignal();
+ client.awaitIdle();
// property should be wrapped
compare(channel.objects.myFactory.objectInProperty.objectName, "foo");
@@ -297,9 +305,24 @@ TestCase {
compare(channel.objects.myFactory.objects.length, 2);
compare(channel.objects.myFactory.objects[0].objectName, "bar");
compare(channel.objects.myFactory.objects[1].objectName, "baz");
+ // map property as well
+ compare(channel.objects.testObject.objectMap.subObject.objectName,
+ "embedded");
// also works with properties that reference other registered objects
compare(channel.objects.myFactory.otherObject, channel.objects.myObj);
+ // change object property
+ channel.objects.myFactory.switchObject();
+ client.awaitMessage();
+ client.awaitResponse();
+ client.awaitIdle();
+ client.awaitPropertyUpdate();
+ compare(channel.objects.myFactory.otherObject, channel.objects.myOtherObj);
+
+ // trigger a signal and ensure it gets transmitted
+ lastFactoryObj.mySignal("foobar", 42);
+ client.awaitSignal();
+
// deleteLater call
msg = client.awaitMessage();
compare(msg.type, JSClient.QWebChannelMessageTypes.invokeMethod);
@@ -448,4 +471,89 @@ TestCase {
0
]);
}
+
+ function test_multiConnect()
+ {
+ var signalArgs = [];
+ function logSignalArgs(arg) {
+ signalArgs.push(arg);
+ }
+ var channel = client.createChannel(function(channel) {
+ var testObject = channel.objects.testObject;
+ testObject.testSignalInt.connect(logSignalArgs);
+ testObject.testSignalInt.connect(logSignalArgs);
+ testObject.triggerSignals();
+ });
+ client.awaitInit();
+
+ var msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.connectToSignal);
+ compare(msg.object, "testObject");
+
+ msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.invokeMethod);
+ client.awaitIdle();
+
+ compare(signalArgs, [42, 42, 1, 1, 0, 0]);
+ }
+
+ function test_overloading()
+ {
+ var signalArgs_implicit = [];
+ var signalArgs_explicit1 = [];
+ var signalArgs_explicit2 = [];
+ var signalArgs_explicit3 = [];
+ function logSignalArgs(container) {
+ return function(...args) {
+ container.push(args);
+ };
+ }
+ var returnValues = [];
+ function logReturnValue(value) {
+ returnValues.push(value);
+ }
+ var channel = client.createChannel(function(channel) {
+ var testObject = channel.objects.testObject;
+ testObject.testOverloadSignal.connect(logSignalArgs(signalArgs_implicit));
+ testObject["testOverloadSignal(int)"].connect(logSignalArgs(signalArgs_explicit1));
+ testObject["testOverloadSignal(QString)"].connect(logSignalArgs(signalArgs_explicit2));
+ testObject["testOverloadSignal(QString,int)"].connect(logSignalArgs(signalArgs_explicit3));
+ testObject.testOverload(99, logReturnValue);
+ testObject["testOverload(int)"](41, logReturnValue);
+ testObject["testOverload(QString)"]("hello world", logReturnValue);
+ testObject["testOverload(QString,int)"]("the answer is ", 41, logReturnValue);
+ });
+ client.awaitInit();
+
+ function awaitMessage(type)
+ {
+ var msg = client.awaitMessage();
+ compare(msg.type, type);
+ compare(msg.object, "testObject");
+ }
+
+ console.log("sig1");
+ awaitMessage(JSClient.QWebChannelMessageTypes.connectToSignal);
+ console.log("sig2");
+ awaitMessage(JSClient.QWebChannelMessageTypes.connectToSignal);
+ console.log("sig3");
+ awaitMessage(JSClient.QWebChannelMessageTypes.connectToSignal);
+
+ console.log("method1");
+ awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
+ console.log("method2");
+ awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
+ console.log("method3");
+ awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
+ console.log("method4");
+ awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
+
+ client.awaitIdle();
+
+ compare(signalArgs_implicit, [[99], [41]]);
+ compare(signalArgs_explicit1, signalArgs_implicit);
+ compare(signalArgs_explicit2, [["hello world"]]);
+ compare(signalArgs_explicit3, [["the answer is ", 41]]);
+ compare(returnValues, [100, 42, "HELLO WORLD", "THE ANSWER IS 42"]);
+ }
}
diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp
index 133655d..ffaebba 100644
--- a/tests/auto/webchannel/tst_webchannel.cpp
+++ b/tests/auto/webchannel/tst_webchannel.cpp
@@ -275,6 +275,21 @@ void TestWebChannel::setJsonArray(const QJsonArray& v)
emit lastJsonArrayChanged();
}
+int TestWebChannel::readOverload(int i)
+{
+ return i + 1;
+}
+
+QString TestWebChannel::readOverload(const QString &arg)
+{
+ return arg.toUpper();
+}
+
+QString TestWebChannel::readOverload(const QString &arg, int i)
+{
+ return arg.toUpper() + QString::number(i + 1);
+}
+
void TestWebChannel::testRegisterObjects()
{
QWebChannel channel;
@@ -351,85 +366,42 @@ void TestWebChannel::testInfoForObject()
QCOMPARE(info["enums"].toObject(), expected);
}
+ QJsonArray expected;
+ auto addMethod = [&expected, &obj](const QString &name, const char *signature, bool addName = true) {
+ const auto index = obj.metaObject()->indexOfMethod(signature);
+ QVERIFY2(index != -1, signature);
+ if (addName)
+ expected.append(QJsonArray{name, index});
+ expected.append(QJsonArray{QString::fromUtf8(signature), index});
+ };
{ // methods & slots
- QJsonArray expected;
- {
- QJsonArray method;
- method.append(QStringLiteral("deleteLater"));
- method.append(obj.metaObject()->indexOfMethod("deleteLater()"));
- expected.append(method);
- }
- {
- QJsonArray method;
- method.append(QStringLiteral("slot1"));
- method.append(obj.metaObject()->indexOfMethod("slot1()"));
- expected.append(method);
- }
- {
- QJsonArray method;
- method.append(QStringLiteral("slot2"));
- method.append(obj.metaObject()->indexOfMethod("slot2(QString)"));
- expected.append(method);
- }
- {
- QJsonArray method;
- method.append(QStringLiteral("setReturnedObject"));
- method.append(obj.metaObject()->indexOfMethod("setReturnedObject(TestObject*)"));
- expected.append(method);
- }
- {
- QJsonArray method;
- method.append(QStringLiteral("setObjectProperty"));
- method.append(obj.metaObject()->indexOfMethod("setObjectProperty(QObject*)"));
- expected.append(method);
- }
- {
- QJsonArray method;
- method.append(QStringLiteral("setProp"));
- method.append(obj.metaObject()->indexOfMethod("setProp(QString)"));
- expected.append(method);
- }
- {
- QJsonArray method;
- method.append(QStringLiteral("fire"));
- method.append(obj.metaObject()->indexOfMethod("fire()"));
- expected.append(method);
- }
- {
- QJsonArray method;
- method.append(QStringLiteral("method1"));
- method.append(obj.metaObject()->indexOfMethod("method1()"));
- expected.append(method);
- }
+ expected = {};
+ addMethod(QStringLiteral("deleteLater"), "deleteLater()");
+ addMethod(QStringLiteral("slot1"), "slot1()");
+ addMethod(QStringLiteral("slot2"), "slot2(QString)");
+ addMethod(QStringLiteral("setReturnedObject"), "setReturnedObject(TestObject*)");
+ addMethod(QStringLiteral("setObjectProperty"), "setObjectProperty(QObject*)");
+ addMethod(QStringLiteral("setProp"), "setProp(QString)");
+ addMethod(QStringLiteral("fire"), "fire()");
+ 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);
}
{ // signals
- QJsonArray expected;
- {
- QJsonArray signal;
- signal.append(QStringLiteral("destroyed"));
- signal.append(obj.metaObject()->indexOfMethod("destroyed(QObject*)"));
- expected.append(signal);
- }
- {
- QJsonArray signal;
- signal.append(QStringLiteral("sig1"));
- signal.append(obj.metaObject()->indexOfMethod("sig1()"));
- expected.append(signal);
- }
- {
- QJsonArray signal;
- signal.append(QStringLiteral("sig2"));
- signal.append(obj.metaObject()->indexOfMethod("sig2(QString)"));
- expected.append(signal);
- }
- {
- QJsonArray signal;
- signal.append(QStringLiteral("replay"));
- signal.append(obj.metaObject()->indexOfMethod("replay()"));
- expected.append(signal);
- }
+ expected = {};
+ addMethod(QStringLiteral("destroyed"), "destroyed(QObject*)");
+ addMethod(QStringLiteral("destroyed"), "destroyed()", false);
+ addMethod(QStringLiteral("sig1"), "sig1()");
+ addMethod(QStringLiteral("sig2"), "sig2(QString)");
+ addMethod(QStringLiteral("replay"), "replay()");
+ addMethod(QStringLiteral("overloadSignal"), "overloadSignal(int)");
+ addMethod(QStringLiteral("overloadSignal"), "overloadSignal(float)", false);
QCOMPARE(info["signals"].toArray(), expected);
}
@@ -537,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);
@@ -547,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);
@@ -559,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);
@@ -569,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);
@@ -579,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);
@@ -589,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);
@@ -603,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);
@@ -618,6 +576,35 @@ void TestWebChannel::testInvokeMethodConversion()
}
}
+void TestWebChannel::testFunctionOverloading()
+{
+ QWebChannel channel;
+ channel.connectTo(m_dummyTransport);
+
+ // all method calls will use the first method's index
+ const auto method1 = metaObject()->indexOfMethod("readOverload(int)");
+ QVERIFY(method1 != -1);
+ const auto method2 = metaObject()->indexOfMethod("readOverload(QString)");
+ QVERIFY(method2 != -1);
+ QVERIFY(method1 < method2);
+ const auto method3 = metaObject()->indexOfMethod("readOverload(QString,int)");
+ QVERIFY(method3 != -1);
+ QVERIFY(method2 < method3);
+
+ { // int
+ const auto retVal = channel.d_func()->publisher->invokeMethod(this, method1, QJsonArray{1000});
+ QCOMPARE(retVal.toInt(), 1001);
+ }
+ { // QString
+ const auto retVal = channel.d_func()->publisher->invokeMethod(this, method2, QJsonArray{QStringLiteral("hello world")});
+ QCOMPARE(retVal.toString(), QStringLiteral("HELLO WORLD"));
+ }
+ { // QString, int
+ const auto retVal = channel.d_func()->publisher->invokeMethod(this, method3, QJsonArray{QStringLiteral("the answer is "), 41});
+ QCOMPARE(retVal.toString(), QStringLiteral("THE ANSWER IS 42"));
+ }
+}
+
void TestWebChannel::testSetPropertyConversion()
{
QWebChannel channel;
@@ -676,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;
@@ -773,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);
@@ -867,12 +897,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());
}
@@ -1136,13 +1163,22 @@ void TestWebChannel::qtbug62388_wrapObjectMultipleTransports()
auto queryObjectInfo = [&channel](QObject *obj, QWebChannelAbstractTransport *transport) {
return channel.d_func()->publisher->wrapResult(QVariant::fromValue(obj), transport).toObject();
};
- const auto objectInfo = queryObjectInfo(&obj, m_dummyTransport);
- QCOMPARE(objectInfo.length(), 3);
- QVERIFY(objectInfo.contains("id"));
- QVERIFY(objectInfo.contains("__QObject*__"));
- QVERIFY(objectInfo.contains("data"));
- QVERIFY(objectInfo.value("__QObject*__").isBool() && objectInfo.value("__QObject*__").toBool());
+ auto verifyObjectInfo = [&obj](const QJsonObject &objectInfo) {
+
+ QCOMPARE(objectInfo.length(), 3);
+ QVERIFY(objectInfo.contains("id"));
+ QVERIFY(objectInfo.contains("__QObject*__"));
+ QVERIFY(objectInfo.contains("data"));
+ QVERIFY(objectInfo.value("__QObject*__").isBool() && objectInfo.value("__QObject*__").toBool());
+
+ const auto propIndex = obj.metaObject()->indexOfProperty("prop");
+ const auto prop = objectInfo["data"].toObject()["properties"].toArray()[propIndex].toArray()[3].toString();
+ QCOMPARE(prop, obj.prop());
+ };
+
+ const auto objectInfo = queryObjectInfo(&obj, m_dummyTransport);
+ verifyObjectInfo(objectInfo);
const auto id = objectInfo.value("id").toString();
@@ -1152,7 +1188,17 @@ void TestWebChannel::qtbug62388_wrapObjectMultipleTransports()
initTransport(&transport);
QCOMPARE(queryObjectInfo(&obj, &transport), objectInfo);
- // don't crash when the transport is destroyed
+ obj.setProp("asdf");
+
+ const auto objectInfo2 = queryObjectInfo(&obj, m_dummyTransport);
+ QVERIFY(objectInfo2 != objectInfo);
+ verifyObjectInfo(objectInfo2);
+
+ DummyTransport transport2;
+ initTransport(&transport2);
+ QCOMPARE(queryObjectInfo(&obj, &transport2), objectInfo2);
+
+ // don't crash when the transports are destroyed
}
QTEST_MAIN(TestWebChannel)
diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h
index 3d16f7b..8ca1cdd 100644
--- a/tests/auto/webchannel/tst_webchannel.h
+++ b/tests/auto/webchannel/tst_webchannel.h
@@ -136,6 +136,8 @@ signals:
void returnedObjectChanged();
void propChanged(const QString&);
void replay();
+ void overloadSignal(int);
+ void overloadSignal(float);
public slots:
void slot1() {}
@@ -156,6 +158,13 @@ public slots:
void setProp(const QString&prop) {emit propChanged(mProp=prop);}
void fire() {emit replay();}
+ 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() {}
@@ -293,6 +302,10 @@ public slots:
QJsonArray readJsonArray() const;
void setJsonArray(const QJsonArray &v);
+ int readOverload(int i);
+ QString readOverload(const QString &arg);
+ QString readOverload(const QString &arg, int i);
+
signals:
void lastIntChanged();
void lastBoolChanged();
@@ -308,7 +321,9 @@ private slots:
void testDeregisterObjectAtStart();
void testInfoForObject();
void testInvokeMethodConversion();
+ void testFunctionOverloading();
void testSetPropertyConversion();
+ void testInvokeMethodOverloadResolution();
void testDisconnect();
void testWrapRegisteredObject();
void testUnwrapObject();