Skip to content

Commit

Permalink
QFactoryLoader: add metaDataKeys() to return just the "Keys" entry
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
thiagomacieira committed Nov 28, 2023
1 parent 90df0f4 commit 878e334
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 10 deletions.
78 changes: 72 additions & 6 deletions src/corelib/plugin/qfactoryloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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());
}
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/corelib/plugin/qfactoryloader_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Q_CORE_EXPORT QFactoryLoader : public QObject
using MetaDataList = QList<QPluginParsedMetaData>;

MetaDataList metaData() const;
QList<QCborArray> metaDataKeys() const;
QObject *instance(int index) const;
};

Expand Down
50 changes: 46 additions & 4 deletions tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 878e334

Please sign in to comment.