summaryrefslogtreecommitdiffstats
path: root/src/corelib/plugin/qfactoryloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/plugin/qfactoryloader.cpp')
-rw-r--r--src/corelib/plugin/qfactoryloader.cpp678
1 files changed, 430 insertions, 248 deletions
diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp
index 7e4a9f06fd..e2d9a40cb4 100644
--- a/src/corelib/plugin/qfactoryloader.cpp
+++ b/src/corelib/plugin/qfactoryloader.cpp
@@ -1,120 +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 "qmap.h"
-#include <qdir.h>
-#include <qdebug.h>
-#include "qmutex.h"
-#include "qplugin.h"
-#include "qplugin_p.h"
-#include "qpluginloader.h"
-#include "private/qobject_p.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 "qjsonvalue.h"
#include "qjsonobject.h"
-#include "qjsonarray.h"
-#include "private/qduplicatetracker_p.h"
+#include "qmutex.h"
+#include "qplugin.h"
+#include "qplugin_p.h"
+#include "qpluginloader.h"
+
+#if QT_CONFIG(library)
+# include "qlibrary_p.h"
+#endif
#include <qtcore_tracepoints_p.h>
+#include <map>
+#include <vector>
+
QT_BEGIN_NAMESPACE
-static inline int metaDataSignatureLength()
+using namespace Qt::StringLiterals;
+
+Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName);
+
+namespace {
+struct IterationResult
{
- return sizeof("QTMETADATA ") - 1;
-}
+ 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;
+ }
-static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QString *errMsg)
+ 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);
+ }
+};
+
+struct QFactoryLoaderMetaDataKeysExtractor : QFactoryLoaderIidSearch
{
- // extract the keys not stored in CBOR
- int qt_metadataVersion = quint8(raw[0]);
- int qt_version = qFromBigEndian<quint16>(raw + 1);
- int qt_archRequirements = quint8(raw[3]);
- if (Q_UNLIKELY(raw[-1] != '!' || qt_metadataVersion != 0)) {
- *errMsg = QStringLiteral("Invalid metadata version");
- return QJsonDocument();
+ 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
- raw += 4;
- size -= 4;
- QByteArray ba = QByteArray::fromRawData(raw, int(size));
- QCborParserError err;
- QCborValue metadata = QCborValue::fromCbor(ba, &err);
+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 (err.error != QCborError::NoError) {
- *errMsg = QLatin1String("Metadata parsing error: ") + err.error.toString();
- return QJsonDocument();
+ 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"));
}
- QJsonObject o;
- o.insert(QLatin1String("version"), qt_version << 8);
- o.insert(QLatin1String("debug"), bool(qt_archRequirements & 1));
- o.insert(QLatin1String("archreq"), qt_archRequirements);
+ // 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);
+
+ // 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;
- // convert the top-level map integer keys
- for (auto it : metadata.toMap()) {
+ data = std::move(map);
+ return true;
+}
+
+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):
- // special case: recreate the debug key
- o.insert(QLatin1String("debug"), bool(it.second.toInteger() & 1));
- key = QStringLiteral("archreq");
- break;
}
} else {
key = it.first.toString();
@@ -123,194 +246,195 @@ static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QStri
if (!key.isEmpty())
o.insert(key, it.second.toJsonValue());
}
- return QJsonDocument(o);
-}
-
-QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype sectionSize, QString *errMsg)
-{
- raw += metaDataSignatureLength();
- sectionSize -= metaDataSignatureLength();
-
- return jsonFromCborMetaData(raw, sectionSize, errMsg);
+ 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)
-
-Q_GLOBAL_STATIC(QRecursiveMutex, qt_factoryloader_mutex)
+static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS",
+ "qt.core.plugin.factoryloader")
-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);
-
- library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath());
- if (!library->isPlugin()) {
- if (qt_debug_component()) {
- qDebug() << library->errorString << Qt::endl
- << " not a plugin";
- }
- library->release();
- continue;
- }
+ Q_TRACE(QFactoryLoader_update, fileName);
- QStringList keys;
- bool metaDataOk = false;
+ QLibraryPrivate::UniquePtr library;
+ library.reset(QLibraryPrivate::findOrCreate(dirEntry.canonicalFilePath()));
+ if (!library->isPlugin()) {
+ qCDebug(lcFactoryLoader) << library->errorString << Qt::endl
+ << " not a plugin";
+ continue;
+ }
- 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;
+ QStringList keys;
+ bool metaDataOk = false;
- 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();
}
}
@@ -321,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;
@@ -328,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;
}
@@ -369,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());
@@ -378,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;
}
@@ -399,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;
}
}