1 | // Copyright (C) 2021 The Qt Company Ltd. |
---|---|
2 | // Copyright (C) 2022 Intel Corporation. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include "qfactoryloader_p.h" |
6 | |
7 | #ifndef QT_NO_QOBJECT |
8 | #include "private/qcoreapplication_p.h" |
9 | #include "private/qduplicatetracker_p.h" |
10 | #include "private/qloggingregistry_p.h" |
11 | #include "private/qobject_p.h" |
12 | #include "qcborarray.h" |
13 | #include "qcbormap.h" |
14 | #include "qcborstreamreader.h" |
15 | #include "qcborvalue.h" |
16 | #include "qdirlisting.h" |
17 | #include "qfileinfo.h" |
18 | #include "qjsonarray.h" |
19 | #include "qjsondocument.h" |
20 | #include "qjsonobject.h" |
21 | #include "qmutex.h" |
22 | #include "qplugin.h" |
23 | #include "qplugin_p.h" |
24 | #include "qpluginloader.h" |
25 | |
26 | #if QT_CONFIG(library) |
27 | # include "qlibrary_p.h" |
28 | #endif |
29 | |
30 | #include <qtcore_tracepoints_p.h> |
31 | |
32 | #include <map> |
33 | #include <vector> |
34 | |
35 | QT_BEGIN_NAMESPACE |
36 | |
37 | using namespace Qt::StringLiterals; |
38 | |
39 | Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName); |
40 | |
41 | namespace { |
42 | struct IterationResult |
43 | { |
44 | enum Result { |
45 | FinishedSearch = 0, |
46 | ContinueSearch, |
47 | |
48 | // parse errors |
49 | ParsingError = -1, |
50 | InvalidMetaDataVersion = -2, |
51 | InvalidTopLevelItem = -3, |
52 | InvalidHeaderItem = -4, |
53 | }; |
54 | Result result; |
55 | QCborError error = { .c: QCborError::NoError }; |
56 | |
57 | Q_IMPLICIT IterationResult(Result r) : result(r) {} |
58 | Q_IMPLICIT IterationResult(QCborError e) : result(ParsingError), error(e) {} |
59 | }; |
60 | |
61 | struct QFactoryLoaderIidSearch |
62 | { |
63 | QLatin1StringView iid; |
64 | bool matchesIid = false; |
65 | QFactoryLoaderIidSearch(QLatin1StringView iid) : iid(iid) |
66 | { Q_ASSERT(!iid.isEmpty()); } |
67 | |
68 | static IterationResult::Result skip(QCborStreamReader &reader) |
69 | { |
70 | // skip this, whatever it is |
71 | reader.next(); |
72 | return IterationResult::ContinueSearch; |
73 | } |
74 | |
75 | IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) |
76 | { |
77 | if (key != QtPluginMetaDataKeys::IID) |
78 | return skip(reader); |
79 | matchesIid = (reader.readAllString() == iid); |
80 | return IterationResult::FinishedSearch; |
81 | } |
82 | IterationResult::Result operator()(QUtf8StringView, QCborStreamReader &reader) |
83 | { |
84 | return skip(reader); |
85 | } |
86 | }; |
87 | |
88 | struct QFactoryLoaderMetaDataKeysExtractor : QFactoryLoaderIidSearch |
89 | { |
90 | QCborArray keys; |
91 | QFactoryLoaderMetaDataKeysExtractor(QLatin1StringView iid) |
92 | : QFactoryLoaderIidSearch(iid) |
93 | {} |
94 | |
95 | IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) |
96 | { |
97 | if (key == QtPluginMetaDataKeys::IID) { |
98 | QFactoryLoaderIidSearch::operator()(key, reader); |
99 | return IterationResult::ContinueSearch; |
100 | } |
101 | if (key != QtPluginMetaDataKeys::MetaData) |
102 | return skip(reader); |
103 | |
104 | if (!matchesIid) |
105 | return IterationResult::FinishedSearch; |
106 | if (!reader.isMap() || !reader.isLengthKnown()) |
107 | return IterationResult::InvalidHeaderItem; |
108 | if (!reader.enterContainer()) |
109 | return IterationResult::ParsingError; |
110 | while (reader.isValid()) { |
111 | // the metadata is JSON, so keys are all strings |
112 | QByteArray key = reader.readAllUtf8String(); |
113 | if (key == "Keys") { |
114 | if (!reader.isArray() || !reader.isLengthKnown()) |
115 | return IterationResult::InvalidHeaderItem; |
116 | keys = QCborValue::fromCbor(reader).toArray(); |
117 | break; |
118 | } |
119 | skip(reader); |
120 | } |
121 | // warning: we may not have finished iterating over the header |
122 | return IterationResult::FinishedSearch; |
123 | } |
124 | using QFactoryLoaderIidSearch::operator(); |
125 | }; |
126 | } // unnamed namespace |
127 | |
128 | template <typename F> static IterationResult iterateInPluginMetaData(QByteArrayView raw, F &&f) |
129 | { |
130 | QPluginMetaData::Header header; |
131 | Q_ASSERT(raw.size() >= qsizetype(sizeof(header))); |
132 | memcpy(dest: &header, src: raw.data(), n: sizeof(header)); |
133 | if (Q_UNLIKELY(header.version > QPluginMetaData::CurrentMetaDataVersion)) |
134 | return IterationResult::InvalidMetaDataVersion; |
135 | |
136 | // use fromRawData to keep QCborStreamReader from copying |
137 | raw = raw.sliced(pos: sizeof(header)); |
138 | QByteArray ba = QByteArray::fromRawData(data: raw.data(), size: raw.size()); |
139 | QCborStreamReader reader(ba); |
140 | if (reader.isInvalid()) |
141 | return reader.lastError(); |
142 | if (!reader.isMap()) |
143 | return IterationResult::InvalidTopLevelItem; |
144 | if (!reader.enterContainer()) |
145 | return reader.lastError(); |
146 | while (reader.isValid()) { |
147 | IterationResult::Result r; |
148 | if (reader.isInteger()) { |
149 | // integer key, one of ours |
150 | qint64 value = reader.toInteger(); |
151 | auto key = QtPluginMetaDataKeys(value); |
152 | if (qint64(key) != value) |
153 | return IterationResult::InvalidHeaderItem; |
154 | if (!reader.next()) |
155 | return reader.lastError(); |
156 | r = f(key, reader); |
157 | } else if (reader.isString()) { |
158 | QByteArray key = reader.readAllUtf8String(); |
159 | if (key.isNull()) |
160 | return reader.lastError(); |
161 | r = f(QUtf8StringView(key), reader); |
162 | } else { |
163 | return IterationResult::InvalidTopLevelItem; |
164 | } |
165 | |
166 | if (QCborError e = reader.lastError()) |
167 | return e; |
168 | if (r != IterationResult::ContinueSearch) |
169 | return r; |
170 | } |
171 | |
172 | if (!reader.leaveContainer()) |
173 | return reader.lastError(); |
174 | return IterationResult::FinishedSearch; |
175 | } |
176 | |
177 | static bool isIidMatch(QByteArrayView raw, QLatin1StringView iid) |
178 | { |
179 | QFactoryLoaderIidSearch search(iid); |
180 | iterateInPluginMetaData(raw, f&: search); |
181 | return search.matchesIid; |
182 | } |
183 | |
184 | bool QPluginParsedMetaData::parse(QByteArrayView raw) |
185 | { |
186 | QCborMap map; |
187 | auto r = iterateInPluginMetaData(raw, f: [&](const auto &key, QCborStreamReader &reader) { |
188 | QCborValue item = QCborValue::fromCbor(reader); |
189 | if (item.isInvalid()) |
190 | return IterationResult::ParsingError; |
191 | if constexpr (std::is_enum_v<std::decay_t<decltype(key)>>) |
192 | map[int(key)] = item; |
193 | else |
194 | map[QString::fromUtf8(key)] = item; |
195 | return IterationResult::ContinueSearch; |
196 | }); |
197 | |
198 | switch (r.result) { |
199 | case IterationResult::FinishedSearch: |
200 | case IterationResult::ContinueSearch: |
201 | break; |
202 | |
203 | // parse errors |
204 | case IterationResult::ParsingError: |
205 | return setError(QFactoryLoader::tr(s: "Metadata parsing error: %1").arg(a: r.error.toString())); |
206 | case IterationResult::InvalidMetaDataVersion: |
207 | return setError(QFactoryLoader::tr(s: "Invalid metadata version")); |
208 | case IterationResult::InvalidTopLevelItem: |
209 | case IterationResult::InvalidHeaderItem: |
210 | return setError(QFactoryLoader::tr(s: "Unexpected metadata contents")); |
211 | } |
212 | |
213 | // header was validated |
214 | auto header = qFromUnaligned<QPluginMetaData::Header>(src: raw.data()); |
215 | |
216 | DecodedArchRequirements archReq = |
217 | header.version == 0 ? decodeVersion0ArchRequirements(value: header.plugin_arch_requirements) |
218 | : decodeVersion1ArchRequirements(value: header.plugin_arch_requirements); |
219 | |
220 | // insert the keys not stored in the top-level CBOR map |
221 | map[int(QtPluginMetaDataKeys::QtVersion)] = |
222 | QT_VERSION_CHECK(header.qt_major_version, header.qt_minor_version, 0); |
223 | map[int(QtPluginMetaDataKeys::IsDebug)] = archReq.isDebug; |
224 | map[int(QtPluginMetaDataKeys::Requirements)] = archReq.level; |
225 | |
226 | data = std::move(map); |
227 | return true; |
228 | } |
229 | |
230 | QJsonObject QPluginParsedMetaData::toJson() const |
231 | { |
232 | // convert from the internal CBOR representation to an external JSON one |
233 | QJsonObject o; |
234 | for (auto it : data.toMap()) { |
235 | QString key; |
236 | if (it.first.isInteger()) { |
237 | switch (it.first.toInteger()) { |
238 | #define CONVERT_TO_STRING(IntKey, StringKey, Description) \ |
239 | case int(IntKey): key = QStringLiteral(StringKey); break; |
240 | QT_PLUGIN_FOREACH_METADATA(CONVERT_TO_STRING) |
241 | } |
242 | } else { |
243 | key = it.first.toString(); |
244 | } |
245 | |
246 | if (!key.isEmpty()) |
247 | o.insert(key, value: it.second.toJsonValue()); |
248 | } |
249 | return o; |
250 | } |
251 | |
252 | class QFactoryLoaderPrivate : public QObjectPrivate |
253 | { |
254 | Q_DECLARE_PUBLIC(QFactoryLoader) |
255 | Q_DISABLE_COPY_MOVE(QFactoryLoaderPrivate) |
256 | public: |
257 | QFactoryLoaderPrivate() { } |
258 | QByteArray iid; |
259 | #if QT_CONFIG(library) |
260 | ~QFactoryLoaderPrivate(); |
261 | mutable QMutex mutex; |
262 | QDuplicateTracker<QString> loadedPaths; |
263 | std::vector<QLibraryPrivate::UniquePtr> libraries; |
264 | std::map<QString, QLibraryPrivate*> keyMap; |
265 | QString suffix; |
266 | QString extraSearchPath; |
267 | Qt::CaseSensitivity cs; |
268 | |
269 | void updateSinglePath(const QString &pluginDir); |
270 | #endif |
271 | }; |
272 | |
273 | #if QT_CONFIG(library) |
274 | |
275 | static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS", |
276 | "qt.core.plugin.factoryloader") |
277 | |
278 | namespace { |
279 | struct QFactoryLoaderGlobals |
280 | { |
281 | // needs to be recursive because loading one plugin could cause another |
282 | // factory to be initialized |
283 | QRecursiveMutex mutex; |
284 | QList<QFactoryLoader *> loaders; |
285 | }; |
286 | } |
287 | |
288 | Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global) |
289 | |
290 | QFactoryLoaderPrivate::~QFactoryLoaderPrivate() |
291 | = default; |
292 | |
293 | inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) |
294 | { |
295 | struct LibraryReleaser { |
296 | void operator()(QLibraryPrivate *library) |
297 | { if (library) library->release(); } |
298 | }; |
299 | |
300 | // If we've already loaded, skip it... |
301 | if (loadedPaths.hasSeen(s: path)) |
302 | return; |
303 | |
304 | qCDebug(lcFactoryLoader) << "checking directory path"<< path << "..."; |
305 | |
306 | QDirListing plugins(path, |
307 | #if defined(Q_OS_WIN) |
308 | QStringList(QStringLiteral("*.dll")), |
309 | #elif defined(Q_OS_ANDROID) |
310 | QStringList("libplugins_%1_*.so"_L1.arg(suffix)), |
311 | #endif |
312 | QDirListing::IteratorFlag::FilesOnly | QDirListing::IteratorFlag::ResolveSymlinks); |
313 | |
314 | for (const auto &dirEntry : plugins) { |
315 | const QString &fileName = dirEntry.fileName(); |
316 | #if defined(Q_PROCESSOR_X86) |
317 | if (fileName.endsWith(s: ".avx2"_L1) || fileName.endsWith(s: ".avx512"_L1)) { |
318 | // ignore AVX2-optimized file, we'll do a bait-and-switch to it later |
319 | continue; |
320 | } |
321 | #endif |
322 | qCDebug(lcFactoryLoader) << "looking at"<< fileName; |
323 | |
324 | Q_TRACE(QFactoryLoader_update, fileName); |
325 | |
326 | QLibraryPrivate::UniquePtr library; |
327 | library.reset(p: QLibraryPrivate::findOrCreate(fileName: dirEntry.canonicalFilePath())); |
328 | if (!library->isPlugin()) { |
329 | qCDebug(lcFactoryLoader) << library->errorString << Qt::endl |
330 | << " not a plugin"; |
331 | continue; |
332 | } |
333 | |
334 | QStringList keys; |
335 | bool metaDataOk = false; |
336 | |
337 | QString iid = library->metaData.value(k: QtPluginMetaDataKeys::IID).toString(); |
338 | if (iid == QLatin1StringView(this->iid.constData(), this->iid.size())) { |
339 | QCborMap object = library->metaData.value(k: QtPluginMetaDataKeys::MetaData).toMap(); |
340 | metaDataOk = true; |
341 | |
342 | const QCborArray k = object.value(key: "Keys"_L1).toArray(); |
343 | for (QCborValueConstRef v : k) |
344 | keys += cs ? v.toString() : v.toString().toLower(); |
345 | } |
346 | qCDebug(lcFactoryLoader) << "Got keys from plugin meta data"<< keys; |
347 | |
348 | if (!metaDataOk) |
349 | continue; |
350 | |
351 | static constexpr qint64 QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0); |
352 | int thisVersion = library->metaData.value(k: QtPluginMetaDataKeys::QtVersion).toInteger(); |
353 | if (iid.startsWith(QStringLiteral("org.qt-project.Qt.QPA"))) { |
354 | // QPA plugins must match Qt Major.Minor |
355 | if (thisVersion != QtVersionNoPatch) { |
356 | qCDebug(lcFactoryLoader) << "Ignoring QPA plugin due to mismatching Qt versions"<< QtVersionNoPatch << thisVersion; |
357 | continue; |
358 | } |
359 | } |
360 | |
361 | int keyUsageCount = 0; |
362 | for (const QString &key : std::as_const(t&: keys)) { |
363 | QLibraryPrivate *&keyMapEntry = keyMap[key]; |
364 | if (QLibraryPrivate *existingLibrary = keyMapEntry) { |
365 | static constexpr bool QtBuildIsDebug = QT_CONFIG(debug); |
366 | bool existingIsDebug = existingLibrary->metaData.value(k: QtPluginMetaDataKeys::IsDebug).toBool(); |
367 | bool thisIsDebug = library->metaData.value(k: QtPluginMetaDataKeys::IsDebug).toBool(); |
368 | bool configsAreDifferent = thisIsDebug != existingIsDebug; |
369 | bool thisConfigDoesNotMatchQt = thisIsDebug != QtBuildIsDebug; |
370 | if (configsAreDifferent && thisConfigDoesNotMatchQt) |
371 | continue; // Existing library matches Qt's build config |
372 | |
373 | // If the existing library was built with a future Qt version, |
374 | // whereas the one we're considering has a Qt version that fits |
375 | // better, we prioritize the better match. |
376 | int existingVersion = existingLibrary->metaData.value(k: QtPluginMetaDataKeys::QtVersion).toInteger(); |
377 | if (!(existingVersion > QtVersionNoPatch && thisVersion <= QtVersionNoPatch)) |
378 | continue; // Existing version is a better match |
379 | } |
380 | |
381 | keyMapEntry = library.get(); |
382 | ++keyUsageCount; |
383 | } |
384 | if (keyUsageCount || keys.isEmpty()) { |
385 | library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload |
386 | QMutexLocker locker(&mutex); |
387 | libraries.push_back(x: std::move(library)); |
388 | } |
389 | }; |
390 | } |
391 | |
392 | void QFactoryLoader::update() |
393 | { |
394 | #ifdef QT_SHARED |
395 | Q_D(QFactoryLoader); |
396 | |
397 | const QStringList paths = QCoreApplication::libraryPaths(); |
398 | for (const QString &pluginDir : paths) { |
399 | #ifdef Q_OS_ANDROID |
400 | QString path = pluginDir; |
401 | #else |
402 | QString path = pluginDir + d->suffix; |
403 | #endif |
404 | |
405 | d->updateSinglePath(path); |
406 | } |
407 | if (!d->extraSearchPath.isEmpty()) |
408 | d->updateSinglePath(path: d->extraSearchPath); |
409 | #else |
410 | Q_D(QFactoryLoader); |
411 | qCDebug(lcFactoryLoader) << "ignoring"<< d->iid |
412 | << "since plugins are disabled in static builds"; |
413 | #endif |
414 | } |
415 | |
416 | QFactoryLoader::~QFactoryLoader() |
417 | { |
418 | if (!qt_factoryloader_global.isDestroyed()) { |
419 | QMutexLocker locker(&qt_factoryloader_global->mutex); |
420 | qt_factoryloader_global->loaders.removeOne(t: this); |
421 | } |
422 | } |
423 | |
424 | #if defined(Q_OS_UNIX) && !defined (Q_OS_DARWIN) |
425 | QLibraryPrivate *QFactoryLoader::library(const QString &key) const |
426 | { |
427 | Q_D(const QFactoryLoader); |
428 | const auto it = d->keyMap.find(x: d->cs ? key : key.toLower()); |
429 | if (it == d->keyMap.cend()) |
430 | return nullptr; |
431 | return it->second; |
432 | } |
433 | #endif |
434 | |
435 | void QFactoryLoader::refreshAll() |
436 | { |
437 | if (qt_factoryloader_global.exists()) { |
438 | QMutexLocker locker(&qt_factoryloader_global->mutex); |
439 | for (QFactoryLoader *loader : std::as_const(t&: qt_factoryloader_global->loaders)) |
440 | loader->update(); |
441 | } |
442 | } |
443 | |
444 | #endif // QT_CONFIG(library) |
445 | |
446 | QFactoryLoader::QFactoryLoader(const char *iid, |
447 | const QString &suffix, |
448 | Qt::CaseSensitivity cs) |
449 | : QObject(*new QFactoryLoaderPrivate) |
450 | { |
451 | Q_ASSERT_X(suffix.startsWith(u'/'), "QFactoryLoader", |
452 | "For historical reasons, the suffix must start with '/' (and it can't be empty)"); |
453 | |
454 | moveToThread(thread: QCoreApplicationPrivate::mainThread()); |
455 | Q_D(QFactoryLoader); |
456 | d->iid = iid; |
457 | #if QT_CONFIG(library) |
458 | d->cs = cs; |
459 | d->suffix = suffix; |
460 | # ifdef Q_OS_ANDROID |
461 | if (!d->suffix.isEmpty() && d->suffix.at(0) == u'/') |
462 | d->suffix.remove(0, 1); |
463 | # endif |
464 | |
465 | QMutexLocker locker(&qt_factoryloader_global->mutex); |
466 | update(); |
467 | qt_factoryloader_global->loaders.append(t: this); |
468 | #else |
469 | Q_UNUSED(suffix); |
470 | Q_UNUSED(cs); |
471 | #endif |
472 | } |
473 | |
474 | void QFactoryLoader::setExtraSearchPath(const QString &path) |
475 | { |
476 | #if QT_CONFIG(library) |
477 | Q_D(QFactoryLoader); |
478 | if (d->extraSearchPath == path) |
479 | return; // nothing to do |
480 | |
481 | QMutexLocker locker(&qt_factoryloader_global->mutex); |
482 | QString oldPath = std::exchange(obj&: d->extraSearchPath, new_val: path); |
483 | if (oldPath.isEmpty()) { |
484 | // easy case, just update this directory |
485 | d->updateSinglePath(path: d->extraSearchPath); |
486 | } else { |
487 | // must re-scan everything |
488 | d->loadedPaths.clear(); |
489 | d->libraries.clear(); |
490 | d->keyMap.clear(); |
491 | update(); |
492 | } |
493 | #else |
494 | Q_UNUSED(path); |
495 | #endif |
496 | } |
497 | |
498 | QFactoryLoader::MetaDataList QFactoryLoader::metaData() const |
499 | { |
500 | Q_D(const QFactoryLoader); |
501 | QList<QPluginParsedMetaData> metaData; |
502 | #if QT_CONFIG(library) |
503 | QMutexLocker locker(&d->mutex); |
504 | for (const auto &library : d->libraries) |
505 | metaData.append(t: library->metaData); |
506 | #endif |
507 | |
508 | QLatin1StringView iid(d->iid.constData(), d->iid.size()); |
509 | const auto staticPlugins = QPluginLoader::staticPlugins(); |
510 | for (const QStaticPlugin &plugin : staticPlugins) { |
511 | QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize); |
512 | QPluginParsedMetaData parsed(pluginData); |
513 | if (parsed.isError() || parsed.value(k: QtPluginMetaDataKeys::IID) != iid) |
514 | continue; |
515 | metaData.append(t: std::move(parsed)); |
516 | } |
517 | |
518 | // other portions of the code will cast to int (e.g., keyMap()) |
519 | Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); |
520 | return metaData; |
521 | } |
522 | |
523 | QList<QCborArray> QFactoryLoader::metaDataKeys() const |
524 | { |
525 | Q_D(const QFactoryLoader); |
526 | QList<QCborArray> metaData; |
527 | #if QT_CONFIG(library) |
528 | QMutexLocker locker(&d->mutex); |
529 | for (const auto &library : d->libraries) { |
530 | const QCborValue md = library->metaData.value(k: QtPluginMetaDataKeys::MetaData); |
531 | metaData.append(t: md["Keys"_L1].toArray()); |
532 | } |
533 | #endif |
534 | |
535 | QLatin1StringView iid(d->iid.constData(), d->iid.size()); |
536 | const auto staticPlugins = QPluginLoader::staticPlugins(); |
537 | for (const QStaticPlugin &plugin : staticPlugins) { |
538 | QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), |
539 | plugin.rawMetaDataSize); |
540 | QFactoryLoaderMetaDataKeysExtractor extractor{ iid }; |
541 | iterateInPluginMetaData(raw: pluginData, f&: extractor); |
542 | if (extractor.matchesIid) |
543 | metaData += std::move(extractor.keys); |
544 | } |
545 | |
546 | // other portions of the code will cast to int (e.g., keyMap()) |
547 | Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); |
548 | return metaData; |
549 | } |
550 | |
551 | QObject *QFactoryLoader::instance(int index) const |
552 | { |
553 | Q_D(const QFactoryLoader); |
554 | if (index < 0) |
555 | return nullptr; |
556 | |
557 | #if QT_CONFIG(library) |
558 | QMutexLocker lock(&d->mutex); |
559 | if (size_t(index) < d->libraries.size()) { |
560 | QLibraryPrivate *library = d->libraries[index].get(); |
561 | if (QObject *obj = library->pluginInstance()) { |
562 | if (!obj->parent()) |
563 | obj->moveToThread(thread: QCoreApplicationPrivate::mainThread()); |
564 | return obj; |
565 | } |
566 | return nullptr; |
567 | } |
568 | // we know d->libraries.size() <= index <= numeric_limits<decltype(index)>::max() → no overflow |
569 | index -= static_cast<int>(d->libraries.size()); |
570 | lock.unlock(); |
571 | #endif |
572 | |
573 | QLatin1StringView iid(d->iid.constData(), d->iid.size()); |
574 | const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins(); |
575 | for (QStaticPlugin plugin : staticPlugins) { |
576 | QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize); |
577 | if (!isIidMatch(raw: pluginData, iid)) |
578 | continue; |
579 | |
580 | if (index == 0) |
581 | return plugin.instance(); |
582 | --index; |
583 | } |
584 | |
585 | return nullptr; |
586 | } |
587 | |
588 | QMultiMap<int, QString> QFactoryLoader::keyMap() const |
589 | { |
590 | QMultiMap<int, QString> result; |
591 | const QList<QCborArray> metaDataList = metaDataKeys(); |
592 | for (int i = 0; i < int(metaDataList.size()); ++i) { |
593 | const QCborArray &keys = metaDataList[i]; |
594 | for (QCborValueConstRef key : keys) |
595 | result.insert(key: i, value: key.toString()); |
596 | } |
597 | return result; |
598 | } |
599 | |
600 | int QFactoryLoader::indexOf(const QString &needle) const |
601 | { |
602 | const QList<QCborArray> metaDataList = metaDataKeys(); |
603 | for (int i = 0; i < int(metaDataList.size()); ++i) { |
604 | const QCborArray &keys = metaDataList[i]; |
605 | for (QCborValueConstRef key : keys) { |
606 | if (key.toString().compare(s: needle, cs: Qt::CaseInsensitive) == 0) |
607 | return i; |
608 | } |
609 | } |
610 | return -1; |
611 | } |
612 | |
613 | QT_END_NAMESPACE |
614 | |
615 | #include "moc_qfactoryloader_p.cpp" |
616 | |
617 | #endif // QT_NO_QOBJECT |
618 |
Definitions
- IterationResult
- Result
- IterationResult
- IterationResult
- QFactoryLoaderIidSearch
- QFactoryLoaderIidSearch
- skip
- operator()
- operator()
- QFactoryLoaderMetaDataKeysExtractor
- QFactoryLoaderMetaDataKeysExtractor
- operator()
- iterateInPluginMetaData
- isIidMatch
- parse
- toJson
- QFactoryLoaderPrivate
- QFactoryLoaderPrivate
- QFactoryLoaderPrivate
- lcFactoryLoader
- QFactoryLoaderGlobals
- qt_factoryloader_global
- ~QFactoryLoaderPrivate
- updateSinglePath
- update
- ~QFactoryLoader
- library
- refreshAll
- QFactoryLoader
- setExtraSearchPath
- metaData
- metaDataKeys
- instance
- keyMap
Learn to use CMake with our Intro Training
Find out more