diff options
Diffstat (limited to 'src/corelib/plugin/qfactoryloader.cpp')
-rw-r--r-- | src/corelib/plugin/qfactoryloader.cpp | 651 |
1 files changed, 421 insertions, 230 deletions
diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 02c9cafbed..e2d9a40cb4 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -1,119 +1,243 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2018 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2022 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qfactoryloader_p.h" #ifndef QT_NO_QOBJECT -#include "qfactoryinterface.h" - #include "private/qcoreapplication_p.h" #include "private/qduplicatetracker_p.h" +#include "private/qloggingregistry_p.h" #include "private/qobject_p.h" +#include "qcborarray.h" #include "qcbormap.h" +#include "qcborstreamreader.h" #include "qcborvalue.h" +#include "qdirlisting.h" +#include "qfileinfo.h" #include "qjsonarray.h" #include "qjsondocument.h" #include "qjsonobject.h" -#include "qjsonvalue.h" -#include "qmap.h" #include "qmutex.h" #include "qplugin.h" #include "qplugin_p.h" #include "qpluginloader.h" -#include <qdebug.h> -#include <qdir.h> + +#if QT_CONFIG(library) +# include "qlibrary_p.h" +#endif #include <qtcore_tracepoints_p.h> +#include <map> +#include <vector> + QT_BEGIN_NAMESPACE -QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype size, QString *errMsg) +using namespace Qt::StringLiterals; + +Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName); + +namespace { +struct IterationResult { - // extract the keys not stored in CBOR - QPluginMetaData::Header header; - Q_ASSERT(size >= qsizetype(sizeof(header))); - memcpy(&header, raw, sizeof(header)); - if (Q_UNLIKELY(header.version > QPluginMetaData::CurrentMetaDataVersion)) { - *errMsg = QStringLiteral("Invalid metadata version"); - return QJsonDocument(); + enum Result { + FinishedSearch = 0, + ContinueSearch, + + // parse errors + ParsingError = -1, + InvalidMetaDataVersion = -2, + InvalidTopLevelItem = -3, + InvalidHeaderItem = -4, + }; + Result result; + QCborError error = { QCborError::NoError }; + + Q_IMPLICIT IterationResult(Result r) : result(r) {} + Q_IMPLICIT IterationResult(QCborError e) : result(ParsingError), error(e) {} +}; + +struct QFactoryLoaderIidSearch +{ + QLatin1StringView iid; + bool matchesIid = false; + QFactoryLoaderIidSearch(QLatin1StringView iid) : iid(iid) + { Q_ASSERT(!iid.isEmpty()); } + + static IterationResult::Result skip(QCborStreamReader &reader) + { + // skip this, whatever it is + reader.next(); + return IterationResult::ContinueSearch; } - raw += sizeof(header); - size -= sizeof(header); - QByteArray ba = QByteArray::fromRawData(raw, int(size)); - QCborParserError err; - QCborValue metadata = QCborValue::fromCbor(ba, &err); + IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) + { + if (key != QtPluginMetaDataKeys::IID) + return skip(reader); + matchesIid = (reader.readAllString() == iid); + return IterationResult::FinishedSearch; + } + IterationResult::Result operator()(QUtf8StringView, QCborStreamReader &reader) + { + return skip(reader); + } +}; - if (err.error != QCborError::NoError) { - *errMsg = QLatin1String("Metadata parsing error: ") + err.error.toString(); - return QJsonDocument(); +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 + QByteArray key = reader.readAllUtf8String(); + if (key == "Keys") { + 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) +{ + QPluginMetaData::Header header; + Q_ASSERT(raw.size() >= qsizetype(sizeof(header))); + memcpy(&header, raw.data(), sizeof(header)); + if (Q_UNLIKELY(header.version > QPluginMetaData::CurrentMetaDataVersion)) + return IterationResult::InvalidMetaDataVersion; + + // use fromRawData to keep QCborStreamReader from copying + raw = raw.sliced(sizeof(header)); + QByteArray ba = QByteArray::fromRawData(raw.data(), raw.size()); + QCborStreamReader reader(ba); + if (reader.isInvalid()) + return reader.lastError(); + if (!reader.isMap()) + return IterationResult::InvalidTopLevelItem; + if (!reader.enterContainer()) + return reader.lastError(); + while (reader.isValid()) { + IterationResult::Result r; + if (reader.isInteger()) { + // integer key, one of ours + qint64 value = reader.toInteger(); + auto key = QtPluginMetaDataKeys(value); + if (qint64(key) != value) + return IterationResult::InvalidHeaderItem; + if (!reader.next()) + return reader.lastError(); + r = f(key, reader); + } else if (reader.isString()) { + QByteArray key = reader.readAllUtf8String(); + if (key.isNull()) + return reader.lastError(); + r = f(QUtf8StringView(key), reader); + } else { + return IterationResult::InvalidTopLevelItem; + } + + if (QCborError e = reader.lastError()) + return e; + if (r != IterationResult::ContinueSearch) + return r; } - if (!metadata.isMap()) { - *errMsg = QStringLiteral("Unexpected metadata contents"); - return QJsonDocument(); + if (!reader.leaveContainer()) + return reader.lastError(); + return IterationResult::FinishedSearch; +} + +static bool isIidMatch(QByteArrayView raw, QLatin1StringView iid) +{ + QFactoryLoaderIidSearch search(iid); + iterateInPluginMetaData(raw, search); + return search.matchesIid; +} + +bool QPluginParsedMetaData::parse(QByteArrayView raw) +{ + QCborMap map; + auto r = iterateInPluginMetaData(raw, [&](const auto &key, QCborStreamReader &reader) { + QCborValue item = QCborValue::fromCbor(reader); + if (item.isInvalid()) + return IterationResult::ParsingError; + if constexpr (std::is_enum_v<std::decay_t<decltype(key)>>) + map[int(key)] = item; + else + map[QString::fromUtf8(key)] = item; + return IterationResult::ContinueSearch; + }); + + switch (r.result) { + case IterationResult::FinishedSearch: + case IterationResult::ContinueSearch: + break; + + // parse errors + case IterationResult::ParsingError: + return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(r.error.toString())); + case IterationResult::InvalidMetaDataVersion: + return setError(QFactoryLoader::tr("Invalid metadata version")); + case IterationResult::InvalidTopLevelItem: + case IterationResult::InvalidHeaderItem: + return setError(QFactoryLoader::tr("Unexpected metadata contents")); } + // header was validated + auto header = qFromUnaligned<QPluginMetaData::Header>(raw.data()); + DecodedArchRequirements archReq = header.version == 0 ? decodeVersion0ArchRequirements(header.plugin_arch_requirements) : decodeVersion1ArchRequirements(header.plugin_arch_requirements); - QJsonObject o; - o.insert(QLatin1String("version"), - QT_VERSION_CHECK(header.qt_major_version, header.qt_minor_version, 0)); - o.insert(QLatin1String("debug"), archReq.isDebug); - o.insert(QLatin1String("archlevel"), archReq.level); + // insert the keys not stored in the top-level CBOR map + map[int(QtPluginMetaDataKeys::QtVersion)] = + QT_VERSION_CHECK(header.qt_major_version, header.qt_minor_version, 0); + map[int(QtPluginMetaDataKeys::IsDebug)] = archReq.isDebug; + map[int(QtPluginMetaDataKeys::Requirements)] = archReq.level; + + data = std::move(map); + return true; +} - // convert the top-level map integer keys - for (auto it : metadata.toMap()) { +QJsonObject QPluginParsedMetaData::toJson() const +{ + // convert from the internal CBOR representation to an external JSON one + QJsonObject o; + for (auto it : data.toMap()) { QString key; if (it.first.isInteger()) { switch (it.first.toInteger()) { #define CONVERT_TO_STRING(IntKey, StringKey, Description) \ case int(IntKey): key = QStringLiteral(StringKey); break; QT_PLUGIN_FOREACH_METADATA(CONVERT_TO_STRING) -#undef CONVERT_TO_STRING - - case int(QtPluginMetaDataKeys::Requirements): - // ignore, handled above - break; } } else { key = it.first.toString(); @@ -122,186 +246,195 @@ QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype size, QStri if (!key.isEmpty()) o.insert(key, it.second.toJsonValue()); } - return QJsonDocument(o); + return o; } class QFactoryLoaderPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QFactoryLoader) + Q_DISABLE_COPY_MOVE(QFactoryLoaderPrivate) public: QFactoryLoaderPrivate() { } QByteArray iid; #if QT_CONFIG(library) ~QFactoryLoaderPrivate(); mutable QMutex mutex; - QList<QLibraryPrivate*> libraryList; - QMap<QString,QLibraryPrivate*> keyMap; + QDuplicateTracker<QString> loadedPaths; + std::vector<QLibraryPrivate::UniquePtr> libraries; + std::map<QString, QLibraryPrivate*> keyMap; QString suffix; + QString extraSearchPath; Qt::CaseSensitivity cs; - QDuplicateTracker<QString> loadedPaths; + + void updateSinglePath(const QString &pluginDir); #endif }; #if QT_CONFIG(library) -Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders) +static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS", + "qt.core.plugin.factoryloader") -Q_GLOBAL_STATIC(QRecursiveMutex, qt_factoryloader_mutex) - -QFactoryLoaderPrivate::~QFactoryLoaderPrivate() +namespace { +struct QFactoryLoaderGlobals { - for (QLibraryPrivate *library : qAsConst(libraryList)) - library->release(); + // needs to be recursive because loading one plugin could cause another + // factory to be initialized + QRecursiveMutex mutex; + QList<QFactoryLoader *> loaders; +}; } -void QFactoryLoader::update() -{ -#ifdef QT_SHARED - Q_D(QFactoryLoader); - QStringList paths = QCoreApplication::libraryPaths(); - for (int i = 0; i < paths.count(); ++i) { - const QString &pluginDir = paths.at(i); - // Already loaded, skip it... - if (d->loadedPaths.hasSeen(pluginDir)) - continue; +Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global) -#ifdef Q_OS_ANDROID - QString path = pluginDir; -#else - QString path = pluginDir + d->suffix; -#endif +QFactoryLoaderPrivate::~QFactoryLoaderPrivate() + = default; - if (qt_debug_component()) - qDebug() << "QFactoryLoader::QFactoryLoader() checking directory path" << path << "..."; +inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) +{ + struct LibraryReleaser { + void operator()(QLibraryPrivate *library) + { if (library) library->release(); } + }; - if (!QDir(path).exists(QLatin1String("."))) - continue; + // If we've already loaded, skip it... + if (loadedPaths.hasSeen(path)) + return; - QStringList plugins = QDir(path).entryList( + qCDebug(lcFactoryLoader) << "checking directory path" << path << "..."; + + QDirListing plugins(path, #if defined(Q_OS_WIN) - QStringList(QStringLiteral("*.dll")), + QStringList(QStringLiteral("*.dll")), #elif defined(Q_OS_ANDROID) - QStringList(QLatin1String("libplugins_%1_*.so").arg(d->suffix)), + QStringList("libplugins_%1_*.so"_L1.arg(suffix)), #endif - QDir::Files); - QLibraryPrivate *library = nullptr; - - for (int j = 0; j < plugins.count(); ++j) { - QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j)); - -#ifdef Q_OS_MAC - const bool isDebugPlugin = fileName.endsWith(QLatin1String("_debug.dylib")); - const bool isDebugLibrary = - #ifdef QT_DEBUG - true; - #else - false; - #endif - - // Skip mismatching plugins so that we don't end up loading both debug and release - // versions of the same Qt libraries (due to the plugin's dependencies). - if (isDebugPlugin != isDebugLibrary) - continue; + QDir::Files); + + for (const auto &dirEntry : plugins) { + const QString &fileName = dirEntry.fileName(); +#ifdef Q_OS_DARWIN + const bool isDebugPlugin = fileName.endsWith("_debug.dylib"_L1); + const bool isDebugLibrary = + #ifdef QT_DEBUG + true; + #else + false; + #endif + + // Skip mismatching plugins so that we don't end up loading both debug and release + // versions of the same Qt libraries (due to the plugin's dependencies). + if (isDebugPlugin != isDebugLibrary) + continue; #elif defined(Q_PROCESSOR_X86) - if (fileName.endsWith(QLatin1String(".avx2")) || fileName.endsWith(QLatin1String(".avx512"))) { - // ignore AVX2-optimized file, we'll do a bait-and-switch to it later - continue; - } + if (fileName.endsWith(".avx2"_L1) || fileName.endsWith(".avx512"_L1)) { + // ignore AVX2-optimized file, we'll do a bait-and-switch to it later + continue; + } #endif - if (qt_debug_component()) { - qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName; - } + qCDebug(lcFactoryLoader) << "looking at" << fileName; - Q_TRACE(QFactoryLoader_update, fileName); + Q_TRACE(QFactoryLoader_update, fileName); - library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath()); - if (!library->isPlugin()) { - if (qt_debug_component()) { - qDebug() << library->errorString << Qt::endl - << " not a plugin"; - } - library->release(); - continue; - } + QLibraryPrivate::UniquePtr library; + library.reset(QLibraryPrivate::findOrCreate(dirEntry.canonicalFilePath())); + if (!library->isPlugin()) { + qCDebug(lcFactoryLoader) << library->errorString << Qt::endl + << " not a plugin"; + continue; + } - QStringList keys; - bool metaDataOk = false; + QStringList keys; + bool metaDataOk = false; - QString iid = library->metaData.value(QLatin1String("IID")).toString(); - if (iid == QLatin1String(d->iid.constData(), d->iid.size())) { - QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject(); - metaDataOk = true; - - QJsonArray k = object.value(QLatin1String("Keys")).toArray(); - for (int i = 0; i < k.size(); ++i) - keys += d->cs ? k.at(i).toString() : k.at(i).toString().toLower(); - } - if (qt_debug_component()) - qDebug() << "Got keys from plugin meta data" << keys; + QString iid = library->metaData.value(QtPluginMetaDataKeys::IID).toString(); + if (iid == QLatin1StringView(this->iid.constData(), this->iid.size())) { + QCborMap object = library->metaData.value(QtPluginMetaDataKeys::MetaData).toMap(); + metaDataOk = true; + const QCborArray k = object.value("Keys"_L1).toArray(); + for (QCborValueConstRef v : k) + keys += cs ? v.toString() : v.toString().toLower(); + } + qCDebug(lcFactoryLoader) << "Got keys from plugin meta data" << keys; - if (!metaDataOk) { - library->release(); - continue; - } + if (!metaDataOk) + continue; - int keyUsageCount = 0; - for (int k = 0; k < keys.count(); ++k) { - // first come first serve, unless the first - // library was built with a future Qt version, - // whereas the new one has a Qt version that fits - // better - const QString &key = keys.at(k); - QLibraryPrivate *previous = d->keyMap.value(key); - int prev_qt_version = 0; - if (previous) { - prev_qt_version = (int)previous->metaData.value(QLatin1String("version")).toDouble(); - } - int qt_version = (int)library->metaData.value(QLatin1String("version")).toDouble(); - if (!previous || (prev_qt_version > QT_VERSION && qt_version <= QT_VERSION)) { - d->keyMap[key] = library; - ++keyUsageCount; - } - } - if (keyUsageCount || keys.isEmpty()) { - library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload - QMutexLocker locker(&d->mutex); - d->libraryList += library; - } else { - library->release(); + int keyUsageCount = 0; + for (const QString &key : std::as_const(keys)) { + // first come first serve, unless the first + // library was built with a future Qt version, + // whereas the new one has a Qt version that fits + // better + constexpr int QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0); + QLibraryPrivate *&previous = keyMap[key]; + int prev_qt_version = 0; + if (previous) + prev_qt_version = int(previous->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger()); + int qt_version = int(library->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger()); + if (!previous || (prev_qt_version > QtVersionNoPatch && qt_version <= QtVersionNoPatch)) { + previous = library.get(); // we WILL .release() + ++keyUsageCount; } } + if (keyUsageCount || keys.isEmpty()) { + library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload + QMutexLocker locker(&mutex); + libraries.push_back(std::move(library)); + } + }; +} + +void QFactoryLoader::update() +{ +#ifdef QT_SHARED + Q_D(QFactoryLoader); + + const QStringList paths = QCoreApplication::libraryPaths(); + for (const QString &pluginDir : paths) { +#ifdef Q_OS_ANDROID + QString path = pluginDir; +#else + QString path = pluginDir + d->suffix; +#endif + + d->updateSinglePath(path); } + if (!d->extraSearchPath.isEmpty()) + d->updateSinglePath(d->extraSearchPath); #else Q_D(QFactoryLoader); - if (qt_debug_component()) { - qDebug() << "QFactoryLoader::QFactoryLoader() ignoring" << d->iid - << "since plugins are disabled in static builds"; - } + qCDebug(lcFactoryLoader) << "ignoring" << d->iid + << "since plugins are disabled in static builds"; #endif } QFactoryLoader::~QFactoryLoader() { - QMutexLocker locker(qt_factoryloader_mutex()); - qt_factory_loaders()->removeAll(this); + if (!qt_factoryloader_global.isDestroyed()) { + QMutexLocker locker(&qt_factoryloader_global->mutex); + qt_factoryloader_global->loaders.removeOne(this); + } } -#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) +#if defined(Q_OS_UNIX) && !defined (Q_OS_DARWIN) QLibraryPrivate *QFactoryLoader::library(const QString &key) const { Q_D(const QFactoryLoader); - return d->keyMap.value(d->cs ? key : key.toLower()); + const auto it = d->keyMap.find(d->cs ? key : key.toLower()); + if (it == d->keyMap.cend()) + return nullptr; + return it->second; } #endif void QFactoryLoader::refreshAll() { - QMutexLocker locker(qt_factoryloader_mutex()); - QList<QFactoryLoader *> *loaders = qt_factory_loaders(); - for (QList<QFactoryLoader *>::const_iterator it = loaders->constBegin(); - it != loaders->constEnd(); ++it) { - (*it)->update(); + if (qt_factoryloader_global.exists()) { + QMutexLocker locker(&qt_factoryloader_global->mutex); + for (QFactoryLoader *loader : std::as_const(qt_factoryloader_global->loaders)) + loader->update(); } } @@ -312,6 +445,9 @@ QFactoryLoader::QFactoryLoader(const char *iid, Qt::CaseSensitivity cs) : QObject(*new QFactoryLoaderPrivate) { + Q_ASSERT_X(suffix.startsWith(u'/'), "QFactoryLoader", + "For historical reasons, the suffix must start with '/' (and it can't be empty)"); + moveToThread(QCoreApplicationPrivate::mainThread()); Q_D(QFactoryLoader); d->iid = iid; @@ -319,36 +455,93 @@ QFactoryLoader::QFactoryLoader(const char *iid, d->cs = cs; d->suffix = suffix; # ifdef Q_OS_ANDROID - if (!d->suffix.isEmpty() && d->suffix.at(0) == QLatin1Char('/')) + if (!d->suffix.isEmpty() && d->suffix.at(0) == u'/') d->suffix.remove(0, 1); # endif - QMutexLocker locker(qt_factoryloader_mutex()); + QMutexLocker locker(&qt_factoryloader_global->mutex); update(); - qt_factory_loaders()->append(this); + qt_factoryloader_global->loaders.append(this); #else Q_UNUSED(suffix); Q_UNUSED(cs); #endif } -QList<QJsonObject> QFactoryLoader::metaData() const +void QFactoryLoader::setExtraSearchPath(const QString &path) +{ +#if QT_CONFIG(library) + Q_D(QFactoryLoader); + if (d->extraSearchPath == path) + return; // nothing to do + + QMutexLocker locker(&qt_factoryloader_global->mutex); + QString oldPath = std::exchange(d->extraSearchPath, path); + if (oldPath.isEmpty()) { + // easy case, just update this directory + d->updateSinglePath(d->extraSearchPath); + } else { + // must re-scan everything + d->loadedPaths.clear(); + d->libraries.clear(); + d->keyMap.clear(); + update(); + } +#else + Q_UNUSED(path); +#endif +} + +QFactoryLoader::MetaDataList QFactoryLoader::metaData() const { Q_D(const QFactoryLoader); - QList<QJsonObject> metaData; + QList<QPluginParsedMetaData> metaData; #if QT_CONFIG(library) QMutexLocker locker(&d->mutex); - for (int i = 0; i < d->libraryList.size(); ++i) - metaData.append(d->libraryList.at(i)->metaData); + for (const auto &library : d->libraries) + metaData.append(library->metaData); #endif + QLatin1StringView iid(d->iid.constData(), d->iid.size()); const auto staticPlugins = QPluginLoader::staticPlugins(); for (const QStaticPlugin &plugin : staticPlugins) { - const QJsonObject object = plugin.metaData(); - if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size())) + QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize); + QPluginParsedMetaData parsed(pluginData); + if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid) continue; - metaData.append(object); + 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; } @@ -360,8 +553,8 @@ QObject *QFactoryLoader::instance(int index) const #if QT_CONFIG(library) QMutexLocker lock(&d->mutex); - if (index < d->libraryList.size()) { - QLibraryPrivate *library = d->libraryList.at(index); + if (size_t(index) < d->libraries.size()) { + QLibraryPrivate *library = d->libraries[index].get(); if (QObject *obj = library->pluginInstance()) { if (!obj->parent()) obj->moveToThread(QCoreApplicationPrivate::mainThread()); @@ -369,18 +562,20 @@ QObject *QFactoryLoader::instance(int index) const } return nullptr; } - index -= d->libraryList.size(); + // we know d->libraries.size() <= index <= numeric_limits<decltype(index)>::max() → no overflow + index -= static_cast<int>(d->libraries.size()); lock.unlock(); #endif - QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins(); - for (int i = 0; i < staticPlugins.count(); ++i) { - const QJsonObject object = staticPlugins.at(i).metaData(); - if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size())) + QLatin1StringView iid(d->iid.constData(), d->iid.size()); + const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins(); + for (QStaticPlugin plugin : staticPlugins) { + QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize); + if (!isIidMatch(pluginData, iid)) continue; if (index == 0) - return staticPlugins.at(i).instance(); + return plugin.instance(); --index; } @@ -390,26 +585,22 @@ QObject *QFactoryLoader::instance(int index) const QMultiMap<int, QString> QFactoryLoader::keyMap() const { QMultiMap<int, QString> result; - const QList<QJsonObject> metaDataList = metaData(); - for (int i = 0; i < metaDataList.size(); ++i) { - const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject(); - const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray(); - const int keyCount = keys.size(); - for (int k = 0; k < keyCount; ++k) - result.insert(i, keys.at(k).toString()); + const QList<QCborArray> metaDataList = metaDataKeys(); + for (int i = 0; i < int(metaDataList.size()); ++i) { + const QCborArray &keys = metaDataList[i]; + for (QCborValueConstRef key : keys) + result.insert(i, key.toString()); } return result; } int QFactoryLoader::indexOf(const QString &needle) const { - const QList<QJsonObject> metaDataList = metaData(); - for (int i = 0; i < metaDataList.size(); ++i) { - const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject(); - const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray(); - const int keyCount = keys.size(); - for (int k = 0; k < keyCount; ++k) { - if (!keys.at(k).toString().compare(needle, Qt::CaseInsensitive)) + const QList<QCborArray> metaDataList = metaDataKeys(); + for (int i = 0; i < int(metaDataList.size()); ++i) { + const QCborArray &keys = metaDataList[i]; + for (QCborValueConstRef key : keys) { + if (key.toString().compare(needle, Qt::CaseInsensitive) == 0) return i; } } |