Details
-
Bug
-
Resolution: Done
-
P2: Important
-
6.0.0 Alpha
-
43d0eae81e30ae8c8502e68d56c6c8b7e2c30215 (qt/qtdeclarative/dev)
Description
Interesting issue spotted while fixing QtPIM tests to work with Qt6. It seems like the QML engine has some change which means a different method overload will be chosen than in Qt5 times.
Consider the following QML file:
import QtQuick 2.0 Rectangle { color: "lightsteelblue" width: 200 height: 200 Timer { running: true repeat: false interval: 1000 onTriggered: { var strings = helper.getValues() console.log("JS got strings: " + strings) helper.printValues(strings) } } }
And the following C++:
#include <QtCore/QCoreApplication> #include <QtCore/QVariantMap> #include <QtCore/QString> #include <QtCore/QStringList> #include <QtCore/QTimer> #include <QtCore/QObject> #include <QtGui/QGuiApplication> #include <QtQml/QQmlEngine> #include <QtQml/QQmlComponent> #include <QtQml/QQmlContext> #include <QtQuick/QQuickView> #include <QtDebug> class CustomElement : public QObject { Q_OBJECT public: CustomElement(QObject *parent = nullptr) : QObject(parent) {} }; QML_DECLARE_TYPE(CustomElement) class Helper : public QObject { Q_OBJECT public: Helper(QQuickView *parent) : QObject(parent), m_view(parent) {} Q_INVOKABLE void doQuit() { QCoreApplication::instance()->quit(); } Q_INVOKABLE void doShow() { m_view->setSource(QUrl(QStringLiteral("main.qml"))); m_view->show(); } Q_INVOKABLE QStringList getValues() const { return QStringList() << QStringLiteral("one") << QStringLiteral("two"); } Q_INVOKABLE void printValues(const QStringList &strings) { qWarning() << "Printing strings from C++: " << strings; } Q_INVOKABLE void printValues(const QList<CustomElement> &elements) { qWarning() << "Printing elements from C++: "; for (const auto &e : elements) qWarning() << e.objectName(); } private: QQuickView *m_view; }; int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView view; Helper *h = new Helper(&view); QTimer::singleShot(1, h, &Helper::doShow); view.rootContext()->setContextProperty("helper", QVariant::fromValue<QObject*>(h)); return app.exec(); } #include "main.moc"
With Qt5 we get:
$ ./testqstringlist qml: JS got strings: one,two Printing strings from C++: ("one", "two")
With Qt6 we get:
$ ./testqstringlist qml: JS got strings: one,two "Could not convert argument 0 at" "onTriggered@file:///home/qinetic/Qt/testapps/testqstringlist/main.qml:15" file:///home/qinetic/Qt/testapps/testqstringlist/main.qml:15: TypeError: Passing incompatible arguments to C++ functions from JavaScript is not allowed.
despite the QML_DECLARE_TYPE(CustomElement), and the fact that the first overload (with QStringList parameter) should be chosen for this call, I think.
Note that if we change the declaration of the second printValues() overload to look like:
Q_INVOKABLE void printValues(const QList<CustomElement*> &elements) { qWarning() << "Printing elements from C++: "; for (auto e : elements) qWarning() << e->objectName(); }
Then we see the following in Qt5:
qml: JS got strings: one,two "Could not convert argument 0 at" "onTriggered@file:///home/qinetic/Qt/testapps/testqstringlist/main.qml:15" "Passing incompatible arguments to C++ functions from JavaScript is dangerous and deprecated." "This will throw a JavaScript TypeError in future releases of Qt!" Printing elements from C++:
i.e. triggering the issue described in https://stackoverflow.com/questions/60915460/call-c-function-from-qml-js-with-c-object-as-argument
In short: I know that the second printValues() overload is bad (and I'm not sure why an example like this existed in the QtPIM unit tests to begin with; my solution will definitely be to remove it entirely). However, I still think it is strange that the QML engine is choosing the second overload even when the call parameter type should perfectly match the signature of the first method.