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.cpp346
1 files changed, 249 insertions, 97 deletions
diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp
index 2c269aa183..e2d9a40cb4 100644
--- a/src/corelib/plugin/qfactoryloader.cpp
+++ b/src/corelib/plugin/qfactoryloader.cpp
@@ -1,62 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2022 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 "qcborvalue.h"
-#include "qdiriterator.h"
+#include "qdirlisting.h"
#include "qfileinfo.h"
#include "qjsonarray.h"
#include "qjsondocument.h"
#include "qjsonobject.h"
-#include "qmap.h"
#include "qmutex.h"
#include "qplugin.h"
#include "qplugin_p.h"
@@ -68,28 +29,189 @@
#include <qtcore_tracepoints_p.h>
+#include <map>
+#include <vector>
+
QT_BEGIN_NAMESPACE
-bool QPluginParsedMetaData::parse(QByteArrayView raw)
+using namespace Qt::StringLiterals;
+
+Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName);
+
+namespace {
+struct IterationResult
+{
+ 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;
+ }
+
+ 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
+{
+ 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 setError(QFactoryLoader::tr("Invalid metadata version"));
+ return IterationResult::InvalidMetaDataVersion;
// use fromRawData to keep QCborStreamReader from copying
raw = raw.sliced(sizeof(header));
QByteArray ba = QByteArray::fromRawData(raw.data(), raw.size());
- QCborParserError err;
- QCborValue metadata = QCborValue::fromCbor(ba, &err);
+ 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 (err.error != QCborError::NoError)
- return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(err.error.toString()));
- if (!metadata.isMap())
+ 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"));
- QCborMap map = metadata.toMap();
- metadata = {};
+ }
+
+ // header was validated
+ auto header = qFromUnaligned<QPluginMetaData::Header>(raw.data());
DecodedArchRequirements archReq =
header.version == 0 ? decodeVersion0ArchRequirements(header.plugin_arch_requirements)
@@ -130,6 +252,7 @@ QJsonObject QPluginParsedMetaData::toJson() const
class QFactoryLoaderPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QFactoryLoader)
+ Q_DISABLE_COPY_MOVE(QFactoryLoaderPrivate)
public:
QFactoryLoaderPrivate() { }
QByteArray iid;
@@ -137,8 +260,8 @@ public:
~QFactoryLoaderPrivate();
mutable QMutex mutex;
QDuplicateTracker<QString> loadedPaths;
- QList<QLibraryPrivate*> libraryList;
- QMap<QString,QLibraryPrivate*> keyMap;
+ std::vector<QLibraryPrivate::UniquePtr> libraries;
+ std::map<QString, QLibraryPrivate*> keyMap;
QString suffix;
QString extraSearchPath;
Qt::CaseSensitivity cs;
@@ -165,10 +288,7 @@ struct QFactoryLoaderGlobals
Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global)
QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
-{
- for (QLibraryPrivate *library : qAsConst(libraryList))
- library->release();
-}
+ = default;
inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
{
@@ -183,18 +303,18 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
qCDebug(lcFactoryLoader) << "checking directory path" << path << "...";
- QDirIterator plugins(path,
+ QDirListing plugins(path,
#if defined(Q_OS_WIN)
QStringList(QStringLiteral("*.dll")),
#elif defined(Q_OS_ANDROID)
- QStringList(QLatin1String("libplugins_%1_*.so").arg(suffix)),
+ QStringList("libplugins_%1_*.so"_L1.arg(suffix)),
#endif
QDir::Files);
- while (plugins.hasNext()) {
- QString fileName = plugins.next();
-#ifdef Q_OS_MAC
- const bool isDebugPlugin = fileName.endsWith(QLatin1String("_debug.dylib"));
+ 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;
@@ -207,7 +327,7 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
if (isDebugPlugin != isDebugLibrary)
continue;
#elif defined(Q_PROCESSOR_X86)
- if (fileName.endsWith(QLatin1String(".avx2")) || fileName.endsWith(QLatin1String(".avx512"))) {
+ if (fileName.endsWith(".avx2"_L1) || fileName.endsWith(".avx512"_L1)) {
// ignore AVX2-optimized file, we'll do a bait-and-switch to it later
continue;
}
@@ -216,8 +336,8 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
Q_TRACE(QFactoryLoader_update, fileName);
- std::unique_ptr<QLibraryPrivate, LibraryReleaser> library;
- library.reset(QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath()));
+ QLibraryPrivate::UniquePtr library;
+ library.reset(QLibraryPrivate::findOrCreate(dirEntry.canonicalFilePath()));
if (!library->isPlugin()) {
qCDebug(lcFactoryLoader) << library->errorString << Qt::endl
<< " not a plugin";
@@ -228,12 +348,12 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
bool metaDataOk = false;
QString iid = library->metaData.value(QtPluginMetaDataKeys::IID).toString();
- if (iid == QLatin1String(this->iid.constData(), this->iid.size())) {
+ if (iid == QLatin1StringView(this->iid.constData(), this->iid.size())) {
QCborMap object = library->metaData.value(QtPluginMetaDataKeys::MetaData).toMap();
metaDataOk = true;
- const QCborArray k = object.value(QLatin1String("Keys")).toArray();
- for (QCborValueRef v : k)
+ 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;
@@ -248,20 +368,20 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
// 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.value(key);
+ 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)) {
- keyMap[key] = library.get(); // we WILL .release()
+ previous = library.get(); // we WILL .release()
++keyUsageCount;
}
}
if (keyUsageCount || keys.isEmpty()) {
library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
QMutexLocker locker(&mutex);
- libraryList += library.release();
+ libraries.push_back(std::move(library));
}
};
}
@@ -298,11 +418,14 @@ QFactoryLoader::~QFactoryLoader()
}
}
-#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
@@ -332,7 +455,7 @@ 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
@@ -353,14 +476,14 @@ void QFactoryLoader::setExtraSearchPath(const QString &path)
return; // nothing to do
QMutexLocker locker(&qt_factoryloader_global->mutex);
- QString oldPath = qExchange(d->extraSearchPath, path);
+ 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->libraryList.clear();
+ d->libraries.clear();
d->keyMap.clear();
update();
}
@@ -375,11 +498,11 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const
QList<QPluginParsedMetaData> metaData;
#if QT_CONFIG(library)
QMutexLocker locker(&d->mutex);
- for (QLibraryPrivate *library : std::as_const(d->libraryList))
+ for (const auto &library : d->libraries)
metaData.append(library->metaData);
#endif
- QLatin1String iid(d->iid.constData(), d->iid.size());
+ 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);
@@ -388,6 +511,37 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const
continue;
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;
}
@@ -399,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());
@@ -408,16 +562,16 @@ 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
- QLatin1String iid(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);
- QPluginParsedMetaData parsed(pluginData);
- if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid)
+ if (!isIidMatch(pluginData, iid))
continue;
if (index == 0)
@@ -431,11 +585,10 @@ QObject *QFactoryLoader::instance(int index) const
QMultiMap<int, QString> QFactoryLoader::keyMap() const
{
QMultiMap<int, QString> result;
- const QList<QPluginParsedMetaData> metaDataList = metaData();
- for (int i = 0; i < metaDataList.size(); ++i) {
- const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap();
- const QCborArray keys = metaData.value(QLatin1String("Keys")).toArray();
- for (QCborValueRef key : keys)
+ 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;
@@ -443,11 +596,10 @@ QMultiMap<int, QString> QFactoryLoader::keyMap() const
int QFactoryLoader::indexOf(const QString &needle) const
{
- const QList<QPluginParsedMetaData> metaDataList = metaData();
- for (int i = 0; i < metaDataList.size(); ++i) {
- const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap();
- const QCborArray keys = metaData.value(QLatin1String("Keys")).toArray();
- for (QCborValueRef key : keys) {
+ 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;
}