diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2023-11-13 15:43:17 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-11-28 03:14:03 -0800 |
commit | 878e3342e1c9bd8a4da6a2e9e1508dacbe0c7ab6 (patch) | |
tree | 76d12e9076237db635aecd9075b54153bcd7fdb5 | |
parent | 90df0f48062310e29494df3449fd68c12e6694b5 (diff) |
QFactoryLoader: add metaDataKeys() to return just the "Keys" entry
Without parsing the whole metadata structure into a QCborValue.
QFactoryLoader::indexOf() is only used in the icon engine and
accessibility loaders. QFactoryLoader::keyMap() has more users, but
QFactoryLoader::metaData() is still by far the most used interface.
Task-number: QTBUG-114253
Change-Id: I8bd6bb457b9c42218247fffd179753524fc9b6a5
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/plugin/qfactoryloader.cpp | 78 | ||||
-rw-r--r-- | src/corelib/plugin/qfactoryloader_p.h | 1 | ||||
-rw-r--r-- | tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp | 50 |
3 files changed, 119 insertions, 10 deletions
diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 722fcce978..55d5bd4f9c 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -99,6 +99,45 @@ struct QFactoryLoaderIidSearch return skip(reader); } }; + +struct QFactoryLoaderMetaDataKeysExtractor : QFactoryLoaderIidSearch +{ + QCborArray keys; + QFactoryLoaderMetaDataKeysExtractor(QLatin1StringView iid) + : QFactoryLoaderIidSearch(iid) + {} + + IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) + { + if (key == QtPluginMetaDataKeys::IID) { + QFactoryLoaderIidSearch::operator()(key, reader); + return IterationResult::ContinueSearch; + } + if (key != QtPluginMetaDataKeys::MetaData) + return skip(reader); + + if (!matchesIid) + return IterationResult::FinishedSearch; + if (!reader.isMap() || !reader.isLengthKnown()) + return IterationResult::InvalidHeaderItem; + if (!reader.enterContainer()) + return IterationResult::ParsingError; + while (reader.isValid()) { + // the metadata is JSON, so keys are all strings + QString key = reader.toString(); + if (key == "Keys"_L1) { + if (!reader.isArray() || !reader.isLengthKnown()) + return IterationResult::InvalidHeaderItem; + keys = QCborValue::fromCbor(reader).toArray(); + break; + } + skip(reader); + } + // warning: we may not have finished iterating over the header + return IterationResult::FinishedSearch; + } + using QFactoryLoaderIidSearch::operator(); +}; } // unnamed namespace template <typename F> static IterationResult iterateInPluginMetaData(QByteArrayView raw, F &&f) @@ -485,6 +524,35 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const metaData.append(std::move(parsed)); } + // other portions of the code will cast to int (e.g., keyMap()) + Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); + return metaData; +} + +QList<QCborArray> QFactoryLoader::metaDataKeys() const +{ + Q_D(const QFactoryLoader); + QList<QCborArray> metaData; +#if QT_CONFIG(library) + QMutexLocker locker(&d->mutex); + for (const auto &library : d->libraries) { + const QCborValue md = library->metaData.value(QtPluginMetaDataKeys::MetaData); + metaData.append(md["Keys"_L1].toArray()); + } +#endif + + QLatin1StringView iid(d->iid.constData(), d->iid.size()); + const auto staticPlugins = QPluginLoader::staticPlugins(); + for (const QStaticPlugin &plugin : staticPlugins) { + QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), + plugin.rawMetaDataSize); + QFactoryLoaderMetaDataKeysExtractor extractor{ iid }; + iterateInPluginMetaData(pluginData, extractor); + if (extractor.matchesIid) + metaData += std::move(extractor.keys); + } + + // other portions of the code will cast to int (e.g., keyMap()) Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); return metaData; } @@ -529,10 +597,9 @@ QObject *QFactoryLoader::instance(int index) const QMultiMap<int, QString> QFactoryLoader::keyMap() const { QMultiMap<int, QString> result; - const QList<QPluginParsedMetaData> metaDataList = metaData(); + const QList<QCborArray> metaDataList = metaDataKeys(); for (int i = 0; i < int(metaDataList.size()); ++i) { - const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap(); - const QCborArray keys = metaData.value("Keys"_L1).toArray(); + const QCborArray &keys = metaDataList[i]; for (QCborValueConstRef key : keys) result.insert(i, key.toString()); } @@ -541,10 +608,9 @@ QMultiMap<int, QString> QFactoryLoader::keyMap() const int QFactoryLoader::indexOf(const QString &needle) const { - const QList<QPluginParsedMetaData> metaDataList = metaData(); + const QList<QCborArray> metaDataList = metaDataKeys(); for (int i = 0; i < int(metaDataList.size()); ++i) { - const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap(); - const QCborArray keys = metaData.value("Keys"_L1).toArray(); + const QCborArray &keys = metaDataList[i]; for (QCborValueConstRef key : keys) { if (key.toString().compare(needle, Qt::CaseInsensitive) == 0) return i; diff --git a/src/corelib/plugin/qfactoryloader_p.h b/src/corelib/plugin/qfactoryloader_p.h index fcfb4cf597..56dc7e6ad1 100644 --- a/src/corelib/plugin/qfactoryloader_p.h +++ b/src/corelib/plugin/qfactoryloader_p.h @@ -86,6 +86,7 @@ public: using MetaDataList = QList<QPluginParsedMetaData>; MetaDataList metaData() const; + QList<QCborArray> metaDataKeys() const; QObject *instance(int index) const; }; diff --git a/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp b/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp index 32f14e7a0a..a17ecd73bb 100644 --- a/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp +++ b/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp @@ -5,6 +5,7 @@ #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> #include <QtCore/qplugin.h> +#include <QtCore/qversionnumber.h> #include <private/qfactoryloader_p.h> #include <private/qlibrary_p.h> #include "plugin1/plugininterface1.h" @@ -48,21 +49,61 @@ void tst_QFactoryLoader::usingTwoFactoriesFromSameDir() // set the library path to contain the directory where the 'bin' dir is located QCoreApplication::setLibraryPaths( { QFileInfo(binFolder).absolutePath() }); #endif + auto versionNumber = [](const QCborValue &value) { + // Qt plugins only store major & minor versions in the metadata, so + // the low 8 bits are always zero. + qint64 v = value.toInteger(); + return QVersionNumber(v >> 16, uchar(v >> 8)); + }; + QVersionNumber qtVersion(QT_VERSION_MAJOR, 0); const QString suffix = QLatin1Char('/') + QLatin1String(binFolderC); QFactoryLoader loader1(PluginInterface1_iid, suffix); + const QFactoryLoader::MetaDataList list1 = loader1.metaData(); + const QList<QCborArray> keys1 = loader1.metaDataKeys(); + QCOMPARE(list1.size(), 1); + QCOMPARE(keys1.size(), 1); + QCOMPARE_GE(versionNumber(list1[0].value(QtPluginMetaDataKeys::QtVersion)), qtVersion); + QCOMPARE(list1[0].value(QtPluginMetaDataKeys::IID), PluginInterface1_iid); + QCOMPARE(list1[0].value(QtPluginMetaDataKeys::ClassName), "Plugin1"); + + // plugin1's Q_PLUGIN_METADATA has FILE "plugin1.json" + QCborValue metadata1 = list1[0].value(QtPluginMetaDataKeys::MetaData); + QCOMPARE(metadata1.type(), QCborValue::Map); + QCOMPARE(metadata1["Keys"], QCborArray{ "plugin1" }); + QCOMPARE(keys1[0], QCborArray{ "plugin1" }); + QCOMPARE(loader1.indexOf("Plugin1"), 0); + QCOMPARE(loader1.indexOf("PLUGIN1"), 0); + QCOMPARE(loader1.indexOf("Plugin2"), -1); - PluginInterface1 *plugin1 = qobject_cast<PluginInterface1 *>(loader1.instance(0)); + QFactoryLoader loader2(PluginInterface2_iid, suffix); + const QFactoryLoader::MetaDataList list2 = loader2.metaData(); + const QList<QCborArray> keys2 = loader2.metaDataKeys(); + QCOMPARE(list2.size(), 1); + QCOMPARE(keys2.size(), 1); + QCOMPARE_GE(versionNumber(list2[0].value(QtPluginMetaDataKeys::QtVersion)), qtVersion); + QCOMPARE(list2[0].value(QtPluginMetaDataKeys::IID), PluginInterface2_iid); + QCOMPARE(list2[0].value(QtPluginMetaDataKeys::ClassName), "Plugin2"); + + // plugin2's Q_PLUGIN_METADATA does not have FILE + QCOMPARE(list2[0].value(QtPluginMetaDataKeys::MetaData), QCborValue()); + QCOMPARE(keys2[0], QCborArray()); + QCOMPARE(loader2.indexOf("Plugin1"), -1); + QCOMPARE(loader2.indexOf("Plugin2"), -1); + + QObject *obj1 = loader1.instance(0); + PluginInterface1 *plugin1 = qobject_cast<PluginInterface1 *>(obj1); QVERIFY2(plugin1, qPrintable(QString::fromLatin1("Cannot load plugin '%1'") .arg(QLatin1String(PluginInterface1_iid)))); + QCOMPARE(obj1->metaObject()->className(), "Plugin1"); - QFactoryLoader loader2(PluginInterface2_iid, suffix); - - PluginInterface2 *plugin2 = qobject_cast<PluginInterface2 *>(loader2.instance(0)); + QObject *obj2 = loader2.instance(0); + PluginInterface2 *plugin2 = qobject_cast<PluginInterface2 *>(obj2); QVERIFY2(plugin2, qPrintable(QString::fromLatin1("Cannot load plugin '%1'") .arg(QLatin1String(PluginInterface2_iid)))); + QCOMPARE(obj2->metaObject()->className(), "Plugin2"); QCOMPARE(plugin1->pluginName(), QLatin1String("Plugin1 ok")); QCOMPARE(plugin2->pluginName(), QLatin1String("Plugin2 ok")); @@ -167,6 +208,7 @@ void tst_QFactoryLoader::staticPlugin() QCborValue metaData = map[int(QtPluginMetaDataKeys::MetaData)]; QVERIFY(metaData.isMap()); QCOMPARE(metaData["Keys"], QCborArray{ "Value" }); + QCOMPARE(loader.metaDataKeys(), QList{ QCborArray{ "Value" } }); QCOMPARE(loader.indexOf("Value"), 0); // instantiate |