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>
bb10
Thiago Macieira 2023-11-13 15:43:17 -08:00
parent 90df0f4806
commit 878e3342e1
3 changed files with 119 additions and 10 deletions

View File

@ -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;

View File

@ -86,6 +86,7 @@ public:
using MetaDataList = QList<QPluginParsedMetaData>;
MetaDataList metaData() const;
QList<QCborArray> metaDataKeys() const;
QObject *instance(int index) const;
};

View File

@ -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");
PluginInterface1 *plugin1 = qobject_cast<PluginInterface1 *>(loader1.instance(0));
// 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);
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