diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-12-22 14:45:04 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-01-02 21:50:34 +0100 |
commit | 3fd3a2a9d06505d549cc4a7c18819a17c6622dfd (patch) | |
tree | aa266f8c45a9f81537a3820223f04387e57ddeb1 /tests/auto/qml/qqmllanguage | |
parent | e39b4572c272f8a0a7be048a24808cb028480e82 (diff) |
QObjectWrapper: Fix calling attached methods on different objects
You can generally store a method in a value and call it on a different
object. However, since we've ignored the thisObject basically forever,
we cannot just accept it right away. Add an opt-in mechanism via a
pragma that allows you to pass (implicitly via context or explicitly via
call()) specific thisObjects to QObject methods.
Fixes: QTBUG-109585
Pick-to: 6.5
Change-Id: I4c81b8ecf6317af55104ac9ebb62d98862ff24e7
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'tests/auto/qml/qqmllanguage')
4 files changed, 152 insertions, 0 deletions
diff --git a/tests/auto/qml/qqmllanguage/data/objectAndGadgetMethodCallsAcceptThisObject.qml b/tests/auto/qml/qqmllanguage/data/objectAndGadgetMethodCallsAcceptThisObject.qml new file mode 100644 index 0000000000..2af2e04352 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/objectAndGadgetMethodCallsAcceptThisObject.qml @@ -0,0 +1,33 @@ +pragma NativeMethodBehavior: AcceptThisObject +import ValueTypes +import StaticTest +import QtQml + +QtObject { + id: self + + property base v1 + property derived v2 + + property var valueTypeMethod: v1.report + + property var qtMethod: Qt.rect + + property rect badRect: self.qtMethod(1, 2, 3, 4) + property rect goodRect1: qtMethod.call(undefined, 1, 2, 3, 4) + property rect goodRect2: qtMethod.call(Qt, 1, 2, 3, 4) + + property string badString: self.valueTypeMethod() + property string goodString1: valueTypeMethod.call(undefined) + property string goodString2: valueTypeMethod.call(v1) + property string goodString3: valueTypeMethod.call(v2) + + property string goodString4: toString.call(Qt) + property string badString2: toString.call(goodRect1) + + property var mm: OriginalSingleton.mm + property int badInt: self.mm() + property int goodInt1: mm.call(undefined) + property int goodInt2: mm.call(OriginalSingleton) + property int goodInt3: mm.call(DerivedSingleton) +} diff --git a/tests/auto/qml/qqmllanguage/data/objectAndGadgetMethodCallsRejectThisObject.qml b/tests/auto/qml/qqmllanguage/data/objectAndGadgetMethodCallsRejectThisObject.qml new file mode 100644 index 0000000000..aab4bf8451 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/objectAndGadgetMethodCallsRejectThisObject.qml @@ -0,0 +1,32 @@ +import ValueTypes +import StaticTest +import QtQml + +QtObject { + id: self + + property base v1 + property derived v2 + + property var valueTypeMethod: v1.report + + property var qtMethod: Qt.rect + + property rect badRect: self.qtMethod(1, 2, 3, 4) + property rect goodRect1: qtMethod.call(undefined, 1, 2, 3, 4) + property rect goodRect2: qtMethod.call(Qt, 1, 2, 3, 4) + + property string badString: self.valueTypeMethod() + property string goodString1: valueTypeMethod.call(undefined) + property string goodString2: valueTypeMethod.call(v1) + property string goodString3: valueTypeMethod.call(v2) + + property string goodString4: toString.call(Qt) + property string badString2: toString.call(goodRect1) + + property var mm: OriginalSingleton.mm + property int badInt: self.mm() + property int goodInt1: mm.call(undefined) + property int goodInt2: mm.call(OriginalSingleton) + property int goodInt3: mm.call(DerivedSingleton) +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 9ba5132bb4..0777ed103d 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -2305,6 +2305,7 @@ signals: void abcChanged(const QString &); public: + Q_INVOKABLE int mm() { return 5; } QString abc() const { return m_abc; } void setAbc(const QString &abc) { diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index efe89f61ac..fee65fcb17 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -403,6 +403,9 @@ private slots: void nullIsNull(); void multiRequired(); + void objectAndGadgetMethodCallsRejectThisObject(); + void objectAndGadgetMethodCallsAcceptThisObject(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -7763,6 +7766,89 @@ void tst_qqmllanguage::multiRequired() qPrintable(url.toString() + ":5 Required property description was not initialized\n")); } +void tst_qqmllanguage::objectAndGadgetMethodCallsRejectThisObject() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("objectAndGadgetMethodCallsRejectThisObject.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + const QList<int> lines = { 15, 19, 21, 22, 24, 25, 28, 31 }; + for (int line : lines) { + const QString message + = ".*:%1: Calling C.. methods with 'this' objects different from the one " + "they were retrieved from is broken, due to historical reasons. The " + "original object is used as 'this' object. You can allow the given " + "'this' object to be used by setting " + "'pragma NativeMethodBehavior: AcceptThisObject'"_L1.arg(QString::number(line)); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(message)); + } + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("badRect"), QRectF(1, 2, 3, 4)); + QCOMPARE(o->property("goodRect1"), QRectF(1, 2, 3, 4)); + QCOMPARE(o->property("goodRect2"), QRectF(1, 2, 3, 4)); + + QCOMPARE(o->property("badString"), QStringLiteral("27")); + QCOMPARE(o->property("goodString1"), QStringLiteral("27")); + QCOMPARE(o->property("goodString2"), QStringLiteral("27")); + QCOMPARE(o->property("goodString3"), QStringLiteral("27")); + + QVERIFY(o->property("goodString4").value<QString>().startsWith("QObject_QML_"_L1)); + QVERIFY(o->property("badString2").value<QString>().startsWith("QObject_QML_"_L1)); + + QCOMPARE(o->property("badInt"), 5); + QCOMPARE(o->property("goodInt1"), 5); + QCOMPARE(o->property("goodInt2"), 5); + QCOMPARE(o->property("goodInt3"), 5); +} + +void tst_qqmllanguage::objectAndGadgetMethodCallsAcceptThisObject() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("objectAndGadgetMethodCallsAcceptThisObject.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, QRegularExpression( + "objectAndGadgetMethodCallsAcceptThisObject.qml:16: Error: " + "Cannot call method QtObject::rect on QObject_QML_")); + QTest::ignoreMessage( + QtWarningMsg, QRegularExpression( + "objectAndGadgetMethodCallsAcceptThisObject.qml:20: Error: " + "Cannot call method BaseValueType::report on QObject_QML_")); + QTest::ignoreMessage( + QtWarningMsg, QRegularExpression( + "objectAndGadgetMethodCallsAcceptThisObject.qml:26: Error: " + "Cannot call method toString on QRectF")); + QTest::ignoreMessage( + QtWarningMsg, QRegularExpression( + "objectAndGadgetMethodCallsAcceptThisObject.qml:29: Error: " + "Cannot call method OriginalSingleton::mm on QObject_QML_")); + + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("badRect"), QRectF()); + QCOMPARE(o->property("goodRect1"), QRectF(1, 2, 3, 4)); + QCOMPARE(o->property("goodRect2"), QRectF(1, 2, 3, 4)); + + QCOMPARE(o->property("badString"), QString()); + QCOMPARE(o->property("goodString1"), QStringLiteral("27")); + QCOMPARE(o->property("goodString2"), QStringLiteral("27")); + QCOMPARE(o->property("goodString3"), QStringLiteral("28")); + + QVERIFY(o->property("goodString4").value<QString>().startsWith("QtObject"_L1)); + QCOMPARE(o->property("badString2"), QString()); + + QCOMPARE(o->property("badInt"), 0); + QCOMPARE(o->property("goodInt1"), 5); + QCOMPARE(o->property("goodInt2"), 5); + QCOMPARE(o->property("goodInt3"), 5); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" |