QPlugin: keep the CBOR data as CBOR

Since QJsonValue and QCborValue use the same backend, we may as well use
the CBOR frontend classes, which means we avoid an unnecessary
conversion until later.

Change-Id: I2de1b4dfacd443148279fffd16a3e2f56cd74c0b
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2021-09-11 16:50:33 -05:00
parent 3c493b89df
commit 2bea9b74ba
6 changed files with 109 additions and 77 deletions

View File

@ -46,8 +46,12 @@
#include "private/qcoreapplication_p.h"
#include "private/qduplicatetracker_p.h"
#include "private/qobject_p.h"
#include "qcborarray.h"
#include "qcbormap.h"
#include "qcborvalue.h"
#include "qcborvalue.h"
#include "qdir.h"
#include "qfileinfo.h"
#include "qjsonarray.h"
#include "qjsondocument.h"
#include "qjsonobject.h"
@ -57,63 +61,61 @@
#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>
QT_BEGIN_NAMESPACE
QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype size, QString *errMsg)
bool QPluginParsedMetaData::parse(QByteArrayView raw)
{
// 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();
}
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"));
raw += sizeof(header);
size -= sizeof(header);
QByteArray ba = QByteArray::fromRawData(raw, int(size));
// 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);
if (err.error != QCborError::NoError) {
*errMsg = QLatin1String("Metadata parsing error: ") + err.error.toString();
return QJsonDocument();
}
if (!metadata.isMap()) {
*errMsg = QStringLiteral("Unexpected metadata contents");
return QJsonDocument();
}
if (err.error != QCborError::NoError)
return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(err.error.toString()));
if (!metadata.isMap())
return setError(QFactoryLoader::tr("Unexpected metadata contents"));
QCborMap map = metadata.toMap();
metadata = {};
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;
// 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):
// ignore, handled above
break;
}
} else {
key = it.first.toString();
@ -122,7 +124,7 @@ 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
@ -227,12 +229,12 @@ void QFactoryLoader::update()
QStringList keys;
bool metaDataOk = false;
QString iid = library->metaData.value(QLatin1String("IID")).toString();
QString iid = library->metaData.value(QtPluginMetaDataKeys::IID).toString();
if (iid == QLatin1String(d->iid.constData(), d->iid.size())) {
QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject();
QCborMap object = library->metaData.value(QtPluginMetaDataKeys::MetaData).toMap();
metaDataOk = true;
QJsonArray k = object.value(QLatin1String("Keys")).toArray();
QCborArray 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();
}
@ -251,14 +253,14 @@ void QFactoryLoader::update()
// 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);
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)) {
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)) {
d->keyMap[key] = library;
++keyUsageCount;
}
@ -339,7 +341,7 @@ QList<QJsonObject> QFactoryLoader::metaData() const
#if QT_CONFIG(library)
QMutexLocker locker(&d->mutex);
for (int i = 0; i < d->libraryList.size(); ++i)
metaData.append(d->libraryList.at(i)->metaData);
metaData.append(d->libraryList.at(i)->metaData.toJson());
#endif
const auto staticPlugins = QPluginLoader::staticPlugins();

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -54,19 +55,44 @@
#include "QtCore/qglobal.h"
#ifndef QT_NO_QOBJECT
#include "QtCore/private/qplugin_p.h"
#include "QtCore/qcbormap.h"
#include "QtCore/qcborvalue.h"
#include "QtCore/qmap.h"
#include "QtCore/qobject.h"
#if QT_CONFIG(library)
# include "qlibrary_p.h"
#endif
#include "QtCore/qplugin.h"
QT_BEGIN_NAMESPACE
class QJsonDocument;
class QJsonObject;
class QLibraryPrivate;
QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype size, QString *errMsg);
class QPluginParsedMetaData
{
QCborValue data;
bool setError(const QString &errorString) Q_DECL_COLD_FUNCTION
{
data = errorString;
return false;
}
public:
QPluginParsedMetaData() = default;
QPluginParsedMetaData(QByteArrayView input) { parse(input); }
bool isError() const { return !data.isMap(); }
QString errorString() const { return data.toString(); }
bool parse(QByteArrayView input);
bool parse(QPluginMetaData metaData)
{ return parse(QByteArrayView(reinterpret_cast<const char *>(metaData.data), metaData.size)); }
QJsonObject toJson() const;
// if data is not a map, toMap() returns empty, so shall these functions
QCborMap toCbor() const { return data.toMap(); }
QCborValue value(QtPluginMetaDataKeys k) const { return toCbor()[int(k)]; }
};
class QFactoryLoaderPrivate;
class Q_CORE_EXPORT QFactoryLoader : public QObject

View File

@ -270,13 +270,13 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
QString errMsg = library;
QLibraryScanResult r = qt_find_pattern(filedata, fdlen, &errMsg);
if (r.length) {
QJsonDocument doc = qJsonFromRawLibraryMetaData(filedata + r.pos, r.length, &errMsg);
if (doc.isNull()) {
if (!lib->metaData.parse(QByteArrayView(filedata + r.pos, r.length))) {
errMsg = lib->metaData.errorString();
qWarning("Found invalid metadata in lib %ls: %ls",
qUtf16Printable(library), qUtf16Printable(errMsg));
} else {
lib->metaData = doc.object();
if (qt_debug_component()) {
QJsonDocument doc(lib->metaData.toJson());
qWarning("Found metadata in lib %s, metadata=\n%s\n",
library.toLocal8Bit().constData(), doc.toJson().constData());
}
@ -711,13 +711,10 @@ static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg)
if (metaData.size < sizeof(QPluginMetaData::Header))
return error(QLibrary::tr("metadata too small"));
QJsonDocument doc = qJsonFromRawLibraryMetaData(reinterpret_cast<const char *>(metaData.data),
metaData.size, errMsg);
if (doc.isNull())
return false; // error message already set
priv->metaData = doc.object();
return true;
if (priv->metaData.parse(metaData))
return true;
*errMsg = priv->metaData.errorString();
return false;
}
bool QLibraryPrivate::isPlugin()
@ -773,8 +770,8 @@ void QLibraryPrivate::updatePluginState()
pluginState = IsNotAPlugin; // be pessimistic
uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
bool debug = metaData.value(QLatin1String("debug")).toBool();
uint qt_version = uint(metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
bool debug = metaData.value(QtPluginMetaDataKeys::IsDebug).toBool();
if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
if (qt_debug_component()) {
qWarning("In %s:\n"

View File

@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -52,9 +52,9 @@
// We mean it.
//
#include <QtCore/private/qglobal_p.h>
#include "QtCore/qlibrary.h"
#include "QtCore/private/qfactoryloader_p.h"
#include "QtCore/qmutex.h"
#include "QtCore/qplugin.h"
#include "QtCore/qpointer.h"
@ -111,7 +111,7 @@ public:
// the mutex protects the fields below
QMutex mutex;
QPointer<QObject> inst; // used by QFactoryLoader
QJsonObject metaData;
QPluginParsedMetaData metaData;
QString errorString;
QString qualifiedFileName;

View File

@ -61,7 +61,8 @@ enum class QtPluginMetaDataKeys {
IID,
ClassName,
MetaData,
URI
URI,
IsDebug,
};
// F(IntKey, StringKey, Description)
@ -70,7 +71,12 @@ enum class QtPluginMetaDataKeys {
F(QtPluginMetaDataKeys::IID, "IID", "Plugin's Interface ID") \
F(QtPluginMetaDataKeys::ClassName, "className", "Plugin class name") \
F(QtPluginMetaDataKeys::MetaData, "MetaData", "Other meta data") \
F(QtPluginMetaDataKeys::URI, "URI", "Plugin URI")
F(QtPluginMetaDataKeys::URI, "URI", "Plugin URI") \
/* not output by moc in CBOR */ \
F(QtPluginMetaDataKeys::QtVersion, "version", "Qt version") \
F(QtPluginMetaDataKeys::Requirements, "archlevel", "Architectural level") \
F(QtPluginMetaDataKeys::IsDebug, "debug", "Debug-mode plugin") \
/**/
namespace {
struct DecodedArchRequirements

View File

@ -47,6 +47,10 @@
#include "qfileinfo.h"
#include "qjsondocument.h"
#if QT_CONFIG(library)
# include "qlibrary_p.h"
#endif
QT_BEGIN_NAMESPACE
#if QT_CONFIG(library)
@ -185,7 +189,7 @@ QJsonObject QPluginLoader::metaData() const
{
if (!d)
return QJsonObject();
return d->metaData;
return d->metaData.toJson();
}
/*!
@ -477,13 +481,10 @@ QList<QStaticPlugin> QPluginLoader::staticPlugins()
*/
QJsonObject QStaticPlugin::metaData() const
{
auto ptr = static_cast<const char *>(rawMetaData);
QString errMsg;
QJsonDocument doc = qJsonFromRawLibraryMetaData(ptr, rawMetaDataSize, &errMsg);
Q_ASSERT(doc.isObject());
Q_ASSERT(errMsg.isEmpty());
return doc.object();
QByteArrayView data(static_cast<const char *>(rawMetaData), rawMetaDataSize);
QPluginParsedMetaData parsed(data);
Q_ASSERT(!parsed.isError());
return parsed.toJson();
}
QT_END_NAMESPACE