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.cpp280
1 files changed, 233 insertions, 47 deletions
diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp
index 4ee87d0dab..e2d9a40cb4 100644
--- a/src/corelib/plugin/qfactoryloader.cpp
+++ b/src/corelib/plugin/qfactoryloader.cpp
@@ -5,22 +5,19 @@
#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"
@@ -32,30 +29,189 @@
#include <qtcore_tracepoints_p.h>
+#include <map>
+#include <vector>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-bool QPluginParsedMetaData::parse(QByteArrayView raw)
+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 (err.error != QCborError::NoError)
- return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(err.error.toString()));
- if (!metadata.isMap())
+ if (QCborError e = reader.lastError())
+ return e;
+ if (r != IterationResult::ContinueSearch)
+ return r;
+ }
+
+ 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)
@@ -96,6 +252,7 @@ QJsonObject QPluginParsedMetaData::toJson() const
class QFactoryLoaderPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QFactoryLoader)
+ Q_DISABLE_COPY_MOVE(QFactoryLoaderPrivate)
public:
QFactoryLoaderPrivate() { }
QByteArray iid;
@@ -103,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;
@@ -131,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)
{
@@ -149,7 +303,7 @@ 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)
@@ -157,9 +311,9 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
#endif
QDir::Files);
- while (plugins.hasNext()) {
- QString fileName = plugins.next();
-#ifdef Q_OS_MAC
+ 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
@@ -182,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";
@@ -214,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));
}
};
}
@@ -264,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
@@ -319,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();
}
@@ -341,7 +498,7 @@ 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
@@ -354,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;
}
@@ -365,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());
@@ -374,7 +562,8 @@ 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
@@ -382,8 +571,7 @@ QObject *QFactoryLoader::instance(int index) const
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)
@@ -397,10 +585,9 @@ 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("Keys"_L1).toArray();
+ 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());
}
@@ -409,10 +596,9 @@ 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("Keys"_L1).toArray();
+ 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;