diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2023-08-18 21:51:31 -0600 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-08-22 10:17:33 -0700 |
commit | 4368179c37cae05d16109e319fc1c77490754c7d (patch) | |
tree | e61dfe6f8c6952ca5a6aba0d73a48a72c7de4ad5 | |
parent | 3c56f561e48b6feae39a75b2f40906c1be819f56 (diff) |
QTimer: fix regression on singleShot-invoking non-mormalized SLOT()s
The was introduced with the rewrite of QMetaObject::invokeMethod() in
commit 0f76e55bc440a70f5d9530a192c9ce6334a8f06, because we have an
optimization for zero timers to avoid creating a temporary
QSingleShotTimer object. The old implementation did attempt to normalize
the target slot name, but did so because it looked metamethods up using
QMetaObject::indexOfMethod:
int idx = meta->indexOfMethod(sig.constData());
if (idx < 0) {
QByteArray norm =
QMetaObject::normalizedSignature(sig.constData());
idx = meta->indexOfMethod(norm.constData());
}
The new implementation does not use this method so it didn't need to
attempt to normalize.
I am fixing this only in QTimer and not in QMetaObject::invokeMethodImpl
(even though it is trivial to do so) because I don't believe spaces in a
pure string to invokeMethod were ever expected to work:
QMetaObject::invokeMethod(obj, "slotName ", Qt::QueuedConnection);
The Q_ARG and Q_RETURN_ARG (for code not recompiled) still does
normalization inside QMetaType::fromName().
Fixes: QTBUG-116060
Pick-to: 6.5 6.6
Change-Id: I964c2b1e6b834feb9710fffd177cac60c83ef413
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
-rw-r--r-- | src/corelib/kernel/qtimer.cpp | 3 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp | 57 |
2 files changed, 59 insertions, 1 deletions
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp index b30522de1c..6487eb472e 100644 --- a/src/corelib/kernel/qtimer.cpp +++ b/src/corelib/kernel/qtimer.cpp @@ -434,7 +434,8 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv return; } QByteArray methodName(member+1, bracketPosition - 1 - member); // extract method name - QMetaObject::invokeMethod(const_cast<QObject *>(receiver), methodName.constData(), Qt::QueuedConnection); + QMetaObject::invokeMethod(const_cast<QObject *>(receiver), methodName.trimmed().constData(), + Qt::QueuedConnection); return; } (void) new QSingleShotTimer(msec, timerType, receiver, member); diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index fdd3cb53a8..dcce526a35 100644 --- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp +++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp @@ -46,6 +46,8 @@ private slots: void zeroTimer(); void singleShotTimeout(); void timeout(); + void singleShotNormalizes_data(); + void singleShotNormalizes(); void sequentialTimers_data(); void sequentialTimers(); void singleShotSequentialTimers_data(); @@ -138,6 +140,61 @@ void tst_QTimer::timeout() QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > oldCount, TIMEOUT_TIMEOUT); } +void tst_QTimer::singleShotNormalizes_data() +{ + QTest::addColumn<QByteArray>("slotName"); + + QTest::newRow("normalized") << QByteArray(SLOT(exitLoop())); + + QTest::newRow("space-before") << QByteArray(SLOT( exitLoop())); + QTest::newRow("space-after") << QByteArray(SLOT(exitLoop ())); + QTest::newRow("space-around") << QByteArray(SLOT( exitLoop ())); + QTest::newRow("spaces-before") << QByteArray(SLOT( exitLoop())); + QTest::newRow("spaces-after") << QByteArray(SLOT(exitLoop ())); + QTest::newRow("spaces-around") << QByteArray(SLOT( exitLoop ())); + + QTest::newRow("space-in-parens") << QByteArray(SLOT(exitLoop( ))); + QTest::newRow("spaces-in-parens") << QByteArray(SLOT(exitLoop( ))); + QTest::newRow("space-after-parens") << QByteArray(SLOT(exitLoop() )); + QTest::newRow("spaces-after-parens") << QByteArray(SLOT(exitLoop() )); +} + +void tst_QTimer::singleShotNormalizes() +{ + using namespace std::chrono_literals; + static constexpr auto TestTimeout = 250ms; + QFETCH(QByteArray, slotName); + QEventLoop loop; + + // control test: regular connection + { + QTimer timer; + QVERIFY(QObject::connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), slotName)); + timer.setSingleShot(true); + timer.start(1); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + + // non-zero time + QTimer::singleShot(1, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QTimer::singleShot(1ms, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // zero time + QTimer::singleShot(0, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QTimer::singleShot(0ms, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + void tst_QTimer::sequentialTimers_data() { #ifdef Q_OS_WIN |