QPluginLoader: don't instantiante multiple, identical instances

This can happen if the same project has two or more Q_IMPORT_PLUGIN
macros in their source. And that can happen when converting from qmake-
based builds to CMake, as qmake didn't generate a source file with the
macro but CMake does.

[ChangeLog][QtCore][QPluginLoader] staticInstances() will not call
duplicated registrations of the same instantiation function, which can
only happen as a result of duplicated Q_IMPORT_PLUGIN for the same
plugin name.

Fixes: QTBUG-102745
Pick-to: 6.2 6.5
Change-Id: Idd5e1bb52be047d7b4fffffd174fb9dd62d8583d
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
bb10
Thiago Macieira 2023-03-25 13:32:26 -04:00
parent a74059f2bd
commit 2f226336a2
2 changed files with 32 additions and 8 deletions

View File

@ -379,7 +379,19 @@ Q_GLOBAL_STATIC(StaticPluginList, staticPluginList)
*/
void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
{
staticPluginList()->append(plugin);
// using operator* because we shouldn't be registering plugins while
// unloading the application!
StaticPluginList &plugins = *staticPluginList;
// insert the plugin in the list, sorted by address, so we can detect
// duplicate registrations
auto comparator = [=](const QStaticPlugin &p1, const QStaticPlugin &p2) {
using Less = std::less<decltype(plugin.instance)>;
return Less{}(p1.instance, p2.instance);
};
auto pos = std::lower_bound(plugins.constBegin(), plugins.constEnd(), plugin, comparator);
if (pos == plugins.constEnd() || pos->instance != plugin.instance)
plugins.insert(pos, plugin);
}
/*!

View File

@ -206,6 +206,7 @@ private slots:
void preloadedPlugin_data();
void preloadedPlugin();
void staticPlugins();
void reregisteredStaticPlugins();
};
Q_IMPORT_PLUGIN(StaticPlugin)
@ -1080,19 +1081,18 @@ void tst_QPluginLoader::staticPlugins()
const QObjectList instances = QPluginLoader::staticInstances();
QVERIFY(instances.size());
bool found = false;
for (QObject *obj : instances) {
found = obj->metaObject()->className() == QLatin1String("StaticPlugin");
if (found)
break;
}
QVERIFY(found);
// ensure the our plugin only shows up once
int foundCount = std::count_if(instances.begin(), instances.end(), [](QObject *obj) {
return obj->metaObject()->className() == QLatin1String("StaticPlugin");
});
QCOMPARE(foundCount, 1);
const auto plugins = QPluginLoader::staticPlugins();
QCOMPARE(plugins.size(), instances.size());
// find the metadata
QJsonObject metaData;
bool found = false;
for (const auto &p : plugins) {
metaData = p.metaData();
found = metaData.value("className").toString() == QLatin1String("StaticPlugin");
@ -1108,6 +1108,18 @@ void tst_QPluginLoader::staticPlugins()
QCOMPARE(metaData.value("URI").toString(), "qt.test.pluginloader.staticplugin");
}
void tst_QPluginLoader::reregisteredStaticPlugins()
{
// the Q_IMPORT_PLUGIN macro will have already done this
qRegisterStaticPluginFunction(qt_static_plugin_StaticPlugin());
staticPlugins();
if (QTest::currentTestFailed())
return;
qRegisterStaticPluginFunction(qt_static_plugin_StaticPlugin());
staticPlugins();
}
QTEST_MAIN(tst_QPluginLoader)
#include "tst_qpluginloader.moc"