summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2023-08-18 21:51:31 -0600
committerThiago Macieira <thiago.macieira@intel.com>2023-08-22 10:17:33 -0700
commit4368179c37cae05d16109e319fc1c77490754c7d (patch)
treee61dfe6f8c6952ca5a6aba0d73a48a72c7de4ad5
parent3c56f561e48b6feae39a75b2f40906c1be819f56 (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.cpp3
-rw-r--r--tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp57
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