1/****************************************************************************
2**
3** Copyright (C) 2017 Crimson AS <info@crimson.no>
4** Copyright (C) 2016 The Qt Company Ltd.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtQml module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qqmlimport_p.h"
42
43#include <QtCore/qdebug.h>
44#include <QtCore/qdir.h>
45#include <QtQml/qqmlfile.h>
46#include <QtCore/qfileinfo.h>
47#include <QtCore/qpluginloader.h>
48#include <QtCore/qlibraryinfo.h>
49#include <QtCore/qreadwritelock.h>
50#include <QtQml/qqmlextensioninterface.h>
51#include <QtQml/qqmlextensionplugin.h>
52#include <private/qqmlextensionplugin_p.h>
53#include <private/qqmlglobal_p.h>
54#include <private/qqmltypenamecache_p.h>
55#include <private/qqmlengine_p.h>
56#include <private/qfieldlist_p.h>
57#include <private/qqmltypemodule_p.h>
58#include <private/qqmltypeloaderqmldircontent_p.h>
59#include <QtCore/qjsonobject.h>
60#include <QtCore/qjsonarray.h>
61#include <QtQml/private/qqmltype_p_p.h>
62
63#include <algorithm>
64#include <functional>
65
66QT_BEGIN_NAMESPACE
67
68DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
69DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
70
71static const QLatin1Char Dot('.');
72static const QLatin1Char Slash('/');
73static const QLatin1Char Backslash('\\');
74static const QLatin1Char Colon(':');
75static const QLatin1String Slash_qmldir("/qmldir");
76static const QLatin1String String_qmldir("qmldir");
77static const QString dotqml_string(QStringLiteral(".qml"));
78static const QString dotuidotqml_string(QStringLiteral(".ui.qml"));
79static bool designerSupportRequired = false;
80
81namespace {
82
83QString resolveLocalUrl(const QString &url, const QString &relative)
84{
85 if (relative.contains(c: Colon)) {
86 // contains a host name
87 return QUrl(url).resolved(relative: QUrl(relative)).toString();
88 } else if (relative.isEmpty()) {
89 return url;
90 } else if (relative.at(i: 0) == Slash || !url.contains(c: Slash)) {
91 return relative;
92 } else {
93 const QStringRef baseRef = url.leftRef(n: url.lastIndexOf(c: Slash) + 1);
94 if (relative == QLatin1String("."))
95 return baseRef.toString();
96
97 QString base = baseRef + relative;
98
99 // Remove any relative directory elements in the path
100 int length = base.length();
101 int index = 0;
102 while ((index = base.indexOf(s: QLatin1String("/."), from: index)) != -1) {
103 if ((length > (index + 2)) && (base.at(i: index + 2) == Dot) &&
104 (length == (index + 3) || (base.at(i: index + 3) == Slash))) {
105 // Either "/../" or "/..<END>"
106 int previous = base.lastIndexOf(c: Slash, from: index - 1);
107 if (previous == -1)
108 break;
109
110 int removeLength = (index - previous) + 3;
111 base.remove(i: previous + 1, len: removeLength);
112 length -= removeLength;
113 index = previous;
114 } else if ((length == (index + 2)) || (base.at(i: index + 2) == Slash)) {
115 // Either "/./" or "/.<END>"
116 base.remove(i: index, len: 2);
117 length -= 2;
118 } else {
119 ++index;
120 }
121 }
122
123 return base;
124 }
125}
126
127bool isPathAbsolute(const QString &path)
128{
129#if defined(Q_OS_UNIX)
130 return (path.at(i: 0) == Slash);
131#else
132 QFileInfo fi(path);
133 return fi.isAbsolute();
134#endif
135}
136
137} // namespace
138
139struct RegisteredPlugin {
140 QString uri;
141 QPluginLoader* loader;
142};
143
144struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> {
145 QMutex mutex;
146
147 ~StringRegisteredPluginMap()
148 {
149 QMutexLocker lock(&mutex);
150 for (const RegisteredPlugin &plugin : qAsConst(t&: *this))
151 delete plugin.loader;
152 }
153};
154
155Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders
156
157void qmlClearEnginePlugins()
158{
159 StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
160 QMutexLocker lock(&plugins->mutex);
161#if QT_CONFIG(library)
162 for (auto &plugin : qAsConst(t&: *plugins)) {
163 QPluginLoader* loader = plugin.loader;
164 if (loader && !loader->unload())
165 qWarning(msg: "Unloading %s failed: %s", qPrintable(plugin.uri), qPrintable(loader->errorString()));
166 delete loader;
167 }
168#endif
169 plugins->clear();
170}
171
172typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair;
173
174/*!
175 \internal
176 \class QQmlImportInstance
177
178 A QQmlImportType represents a single import of a document, held within a
179 namespace.
180
181 \note The uri here may not necessarily be unique (e.g. for file imports).
182
183 \note Version numbers may be -1 for file imports: this means that no
184 version was specified as part of the import. Type resolution will be
185 responsible for attempting to find the "best" possible version.
186*/
187
188/*!
189 \internal
190 \class QQmlImportNamespace
191
192 A QQmlImportNamespace is a way of seperating imports into a local namespace.
193
194 Within a QML document, there is at least one namespace (the
195 "unqualified set") where imports without a qualifier are placed, i.e:
196
197 import QtQuick 2.6
198
199 will have a single namespace (the unqualified set) containing a single import
200 for QtQuick 2.6. However, there may be others if an import statement gives
201 a qualifier, i.e the following will result in an additional new
202 QQmlImportNamespace in the qualified set:
203
204 import MyFoo 1.0 as Foo
205*/
206
207class QQmlImportsPrivate
208{
209public:
210 QQmlImportsPrivate(QQmlTypeLoader *loader);
211 ~QQmlImportsPrivate();
212
213 QQmlImportNamespace *importNamespace(const QString &prefix) const;
214
215 bool addLibraryImport(const QString& uri, const QString &prefix,
216 int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
217 QQmlImportDatabase *database,
218 QList<QQmlError> *errors);
219
220 bool addFileImport(const QString &uri, const QString &prefix,
221 int vmaj, int vmin,
222 bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
223 QList<QQmlError> *errors);
224
225 bool updateQmldirContent(const QString &uri, const QString &prefix,
226 const QString &qmldirIdentifier, const QString& qmldirUrl,
227 QQmlImportDatabase *database,
228 QList<QQmlError> *errors);
229
230 bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor,
231 QQmlType *type_return, QList<QQmlError> *errors,
232 QQmlType::RegistrationType registrationType,
233 bool *typeRecursionDetected = nullptr);
234
235 QUrl baseUrl;
236 QString base;
237 int ref;
238
239 // storage of data related to imports without a namespace
240 mutable QQmlImportNamespace unqualifiedset;
241
242 QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const;
243
244 // storage of data related to imports with a namespace
245 mutable QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> qualifiedSets;
246
247 QQmlTypeLoader *typeLoader;
248
249 static QQmlImports::LocalQmldirResult locateLocalQmldir(
250 const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
251 QString *outQmldirFilePath, QString *outUrl);
252
253 static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin,
254 QList<QQmlError> *errors);
255
256 bool importExtension(const QString &absoluteFilePath, const QString &uri,
257 int vmaj, int vmin,
258 QQmlImportDatabase *database,
259 const QQmlTypeLoaderQmldirContent &qmldir,
260 QList<QQmlError> *errors);
261
262 bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
263 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors);
264
265 QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
266
267 QQmlImportInstance *addImportToNamespace(QQmlImportNamespace *nameSpace,
268 const QString &uri, const QString &url,
269 int vmaj, int vmin, QV4::CompiledData::Import::ImportType type,
270 QList<QQmlError> *errors, bool lowPrecedence = false);
271
272 bool populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QStringList &versionUris,
273 const QString &qmldirPath, QList<QQmlError> *errors);
274};
275
276/*!
277\class QQmlImports
278\brief The QQmlImports class encapsulates one QML document's import statements.
279\internal
280*/
281QQmlImports::QQmlImports(const QQmlImports &copy)
282: d(copy.d)
283{
284 ++d->ref;
285}
286
287QQmlImports &
288QQmlImports::operator =(const QQmlImports &copy)
289{
290 ++copy.d->ref;
291 if (--d->ref == 0)
292 delete d;
293 d = copy.d;
294 return *this;
295}
296
297QQmlImports::QQmlImports(QQmlTypeLoader *typeLoader)
298 : d(new QQmlImportsPrivate(typeLoader))
299{
300}
301
302QQmlImports::~QQmlImports()
303{
304 if (--d->ref == 0)
305 delete d;
306}
307
308/*!
309 Sets the base URL to be used for all relative file imports added.
310*/
311void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
312{
313 d->baseUrl = url;
314
315 if (urlString.isEmpty()) {
316 d->base = url.toString();
317 } else {
318 //Q_ASSERT(url.toString() == urlString);
319 d->base = urlString;
320 }
321}
322
323/*!
324 Returns the base URL to be used for all relative file imports added.
325*/
326QUrl QQmlImports::baseUrl() const
327{
328 return d->baseUrl;
329}
330
331/*
332 \internal
333
334 This method is responsible for populating data of all types visible in this
335 document's imports into the \a cache for resolution elsewhere (e.g. in JS,
336 or when loading additional types).
337
338 \note This is for C++ types only. Composite types are handled separately,
339 as they do not have a QQmlTypeModule.
340*/
341void QQmlImports::populateCache(QQmlTypeNameCache *cache) const
342{
343 const QQmlImportNamespace &set = d->unqualifiedset;
344
345 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
346 const QQmlImportInstance *import = set.imports.at(i: ii);
347 QQmlTypeModule *module = QQmlMetaType::typeModule(uri: import->uri, majorVersion: import->majversion);
348 if (module) {
349 cache->m_anonymousImports.append(t: QQmlTypeModuleVersion(module, import->minversion));
350 }
351 }
352
353 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(v: ns)) {
354
355 const QQmlImportNamespace &set = *ns;
356
357 // positioning is important; we must create the namespace even if there is no module.
358 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
359 typeimport.m_qualifier = set.prefix;
360
361 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
362 const QQmlImportInstance *import = set.imports.at(i: ii);
363 QQmlTypeModule *module = QQmlMetaType::typeModule(uri: import->uri, majorVersion: import->majversion);
364 if (module) {
365 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
366 typeimport.modules.append(t: QQmlTypeModuleVersion(module, import->minversion));
367 }
368 }
369 }
370}
371
372// We need to exclude the entry for the current baseUrl. This can happen for example
373// when handling qmldir files on the remote dir case and the current type is marked as
374// singleton.
375bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl)
376{
377 if (importUrl.isEmpty())
378 return false;
379
380 if (baseUrl.startsWith(s: importUrl))
381 {
382 if (fileName == baseUrl.midRef(position: importUrl.size()))
383 return false;
384 }
385
386 return true;
387}
388
389void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::CompositeSingletonReference> &resultList, const QUrl &baseUrl)
390{
391 typedef QQmlDirComponents::const_iterator ConstIterator;
392
393 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
394 const QQmlImportInstance *import = set.imports.at(i: ii);
395
396 const QQmlDirComponents &components = import->qmlDirComponents;
397
398 const int importMajorVersion = import->majversion;
399 const int importMinorVersion = import->minversion;
400 auto shouldSkipSingleton = [importMajorVersion, importMinorVersion](int singletonMajorVersion, int singletonMinorVersion) -> bool {
401 return importMajorVersion != -1 &&
402 (singletonMajorVersion > importMajorVersion || (singletonMajorVersion == importMajorVersion && singletonMinorVersion > importMinorVersion));
403 };
404
405 ConstIterator cend = components.constEnd();
406 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
407 if (cit->singleton && excludeBaseUrl(importUrl: import->url, fileName: cit->fileName, baseUrl: baseUrl.toString())) {
408 if (shouldSkipSingleton(cit->majorVersion, cit->minorVersion))
409 continue;
410 QQmlImports::CompositeSingletonReference ref;
411 ref.typeName = cit->typeName;
412 ref.prefix = set.prefix;
413 ref.majorVersion = cit->majorVersion;
414 ref.minorVersion = cit->minorVersion;
415 resultList.append(t: ref);
416 }
417 }
418
419 if (QQmlTypeModule *module = QQmlMetaType::typeModule(uri: import->uri, majorVersion: import->majversion)) {
420 module->walkCompositeSingletons(callback: [&resultList, &set, &shouldSkipSingleton](const QQmlType &singleton) {
421 if (shouldSkipSingleton(singleton.majorVersion(), singleton.minorVersion()))
422 return;
423 QQmlImports::CompositeSingletonReference ref;
424 ref.typeName = singleton.elementName();
425 ref.prefix = set.prefix;
426 ref.majorVersion = singleton.majorVersion();
427 ref.minorVersion = singleton.minorVersion();
428 resultList.append(t: ref);
429 });
430 }
431 }
432}
433
434/*
435 \internal
436
437 Returns a list of all composite singletons present in this document's
438 imports.
439
440 This information is used by QQmlTypeLoader to ensure that composite singletons
441 are marked as dependencies during type loading.
442*/
443QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons() const
444{
445 QList<QQmlImports::CompositeSingletonReference> compositeSingletons;
446
447 const QQmlImportNamespace &set = d->unqualifiedset;
448 findCompositeSingletons(set, resultList&: compositeSingletons, baseUrl: baseUrl());
449
450 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(v: ns)) {
451 const QQmlImportNamespace &set = *ns;
452 findCompositeSingletons(set, resultList&: compositeSingletons, baseUrl: baseUrl());
453 }
454
455 std::stable_sort(first: compositeSingletons.begin(), last: compositeSingletons.end(),
456 comp: [](const QQmlImports::CompositeSingletonReference &lhs,
457 const QQmlImports::CompositeSingletonReference &rhs) {
458 if (lhs.prefix != rhs.prefix)
459 return lhs.prefix < rhs.prefix;
460
461 if (lhs.typeName != rhs.typeName)
462 return lhs.typeName < rhs.typeName;
463
464 return lhs.majorVersion != rhs.majorVersion
465 ? lhs.majorVersion < rhs.majorVersion
466 : lhs.minorVersion < rhs.minorVersion;
467 });
468
469 return compositeSingletons;
470}
471
472/*
473 \internal
474
475 Returns a list of scripts imported by this document. This is used by
476 QQmlTypeLoader to properly handle dependencies on imported scripts.
477*/
478QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
479{
480 QList<QQmlImports::ScriptReference> scripts;
481
482 const QQmlImportNamespace &set = d->unqualifiedset;
483
484 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
485 const QQmlImportInstance *import = set.imports.at(i: ii);
486
487 for (const QQmlDirParser::Script &script : import->qmlDirScripts) {
488 ScriptReference ref;
489 ref.nameSpace = script.nameSpace;
490 ref.location = QUrl(import->url).resolved(relative: QUrl(script.fileName));
491 scripts.append(t: ref);
492 }
493 }
494
495 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(v: ns)) {
496 const QQmlImportNamespace &set = *ns;
497
498 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
499 const QQmlImportInstance *import = set.imports.at(i: ii);
500
501 for (const QQmlDirParser::Script &script : import->qmlDirScripts) {
502 ScriptReference ref;
503 ref.nameSpace = script.nameSpace;
504 ref.qualifier = set.prefix;
505 ref.location = QUrl(import->url).resolved(relative: QUrl(script.fileName));
506 scripts.append(t: ref);
507 }
508 }
509 }
510
511 return scripts;
512}
513
514static QString joinStringRefs(const QVector<QStringRef> &refs, const QChar &sep)
515{
516 QString str;
517 for (auto it = refs.cbegin(); it != refs.cend(); ++it) {
518 if (it != refs.cbegin())
519 str += sep;
520 str += *it;
521 }
522 return str;
523}
524
525/*!
526 Forms complete paths to a qmldir file, from a base URL, a module URI and version specification.
527
528 For example, QtQml.Models 2.0:
529 - base/QtQml/Models.2.0/qmldir
530 - base/QtQml.2.0/Models/qmldir
531 - base/QtQml/Models.2/qmldir
532 - base/QtQml.2/Models/qmldir
533 - base/QtQml/Models/qmldir
534*/
535QStringList QQmlImports::completeQmldirPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin)
536{
537 const QVector<QStringRef> parts = uri.splitRef(sep: Dot, behavior: Qt::SkipEmptyParts);
538
539 QStringList qmlDirPathsPaths;
540 // fully & partially versioned parts + 1 unversioned for each base path
541 qmlDirPathsPaths.reserve(alloc: basePaths.count() * (2 * parts.count() + 1));
542
543 for (int version = FullyVersioned; version <= Unversioned; ++version) {
544 const QString ver = versionString(vmaj, vmin, version: static_cast<QQmlImports::ImportVersion>(version));
545
546 for (const QString &path : basePaths) {
547 QString dir = path;
548 if (!dir.endsWith(c: Slash) && !dir.endsWith(c: Backslash))
549 dir += Slash;
550
551 // append to the end
552 qmlDirPathsPaths += dir + joinStringRefs(refs: parts, sep: Slash) + ver + Slash_qmldir;
553
554 if (version != Unversioned) {
555 // insert in the middle
556 for (int index = parts.count() - 2; index >= 0; --index) {
557 qmlDirPathsPaths += dir + joinStringRefs(refs: parts.mid(pos: 0, len: index + 1), sep: Slash)
558 + ver + Slash
559 + joinStringRefs(refs: parts.mid(pos: index + 1), sep: Slash) + Slash_qmldir;
560 }
561 }
562 }
563 }
564
565 return qmlDirPathsPaths;
566}
567
568QString QQmlImports::versionString(int vmaj, int vmin, ImportVersion version)
569{
570 if (version == QQmlImports::FullyVersioned) {
571 // extension with fully encoded version number (eg. MyModule.3.2)
572 return QString::asprintf(format: ".%d.%d", vmaj, vmin);
573 } else if (version == QQmlImports::PartiallyVersioned) {
574 // extension with encoded version major (eg. MyModule.3)
575 return QString::asprintf(format: ".%d", vmaj);
576 } // else extension without version number (eg. MyModule)
577 return QString();
578}
579
580/*!
581 \internal
582
583 The given (namespace qualified) \a type is resolved to either
584 \list
585 \li a QQmlImportNamespace stored at \a ns_return, or
586 \li a QQmlType stored at \a type_return,
587 \endlist
588
589 If any return pointer is 0, the corresponding search is not done.
590
591 \sa addFileImport(), addLibraryImport
592*/
593bool QQmlImports::resolveType(const QHashedStringRef &type,
594 QQmlType *type_return, int *vmaj, int *vmin,
595 QQmlImportNamespace** ns_return, QList<QQmlError> *errors,
596 QQmlType::RegistrationType registrationType,
597 bool *typeRecursionDetected) const
598{
599 QQmlImportNamespace* ns = d->findQualifiedNamespace(type);
600 if (ns) {
601 if (ns_return)
602 *ns_return = ns;
603 return true;
604 }
605 if (type_return) {
606 if (d->resolveType(type, vmajor: vmaj, vminor: vmin, type_return, errors, registrationType,
607 typeRecursionDetected)) {
608 if (qmlImportTrace()) {
609#define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
610 << ')' << "::resolveType: " << type.toString() << " => "
611
612 if (type_return && type_return->isValid()) {
613 if (type_return->isCompositeSingleton())
614 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL-SINGLETON";
615 else if (type_return->isComposite())
616 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL";
617 else if (type_return->isInlineComponentType())
618 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE(INLINECOMPONENT)";
619 else
620 RESOLVE_TYPE_DEBUG << type_return->typeName() << " TYPE";
621 }
622#undef RESOLVE_TYPE_DEBUG
623 }
624 return true;
625 }
626 }
627 return false;
628}
629
630bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
631{
632 Q_ASSERT(resolvedUrl.endsWith(Slash));
633 url = resolvedUrl;
634 localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
635
636 qmlDirComponents = qmldir.components();
637
638 const QQmlDirScripts &scripts = qmldir.scripts();
639 if (!scripts.isEmpty()) {
640 // Verify that we haven't imported these scripts already
641 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
642 it != nameSpace->imports.constEnd(); ++it) {
643 if ((*it != this) && ((*it)->uri == uri)) {
644 QQmlError error;
645 error.setDescription(QQmlImportDatabase::tr(sourceText: "\"%1\" is ambiguous. Found in %2 and in %3").arg(a: uri).arg(a: url).arg(a: (*it)->url));
646 errors->prepend(t: error);
647 return false;
648 }
649 }
650
651 qmlDirScripts = getVersionedScripts(qmldirscripts: scripts, vmaj: majversion, vmin: minversion);
652 }
653
654 return true;
655}
656
657QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin)
658{
659 QMap<QString, QQmlDirParser::Script> versioned;
660
661 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
662 sit != qmldirscripts.constEnd(); ++sit) {
663 // Only include scripts that match our requested version
664 if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
665 ((vmin == -1) || (sit->minorVersion <= vmin))) {
666 // Load the highest version that matches
667 QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(akey: sit->nameSpace);
668 if (vit == versioned.end() || (vit->minorVersion < sit->minorVersion)) {
669 versioned.insert(akey: sit->nameSpace, avalue: *sit);
670 }
671 }
672 }
673
674 return versioned.values();
675}
676
677/*!
678 \internal
679
680 Searching \e only in the namespace \a ns (previously returned in a call to
681 resolveType(), \a type is found and returned to
682 a QQmlType stored at \a type_return. If the type is from a QML file, the returned
683 type will be a CompositeType.
684
685 If the return pointer is 0, the corresponding search is not done.
686*/
687bool QQmlImports::resolveType(QQmlImportNamespace *ns, const QHashedStringRef &type,
688 QQmlType *type_return, int *vmaj, int *vmin,
689 QQmlType::RegistrationType registrationType) const
690{
691 return ns->resolveType(typeLoader: d->typeLoader, type, vmajor: vmaj, vminor: vmin, type_return, base: nullptr, errors: nullptr, registrationType);
692}
693
694bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type,
695 int *vmajor, int *vminor, QQmlType *type_return, QString *base,
696 bool *typeRecursionDetected,
697 QQmlType::RegistrationType registrationType,
698 QQmlImport::RecursionRestriction recursionRestriction,
699 QList<QQmlError> *errors) const
700{
701 if (majversion >= 0 && minversion >= 0) {
702 QQmlType t = QQmlMetaType::qmlType(name: type, module: uri, majversion, minversion);
703 if (t.isValid()) {
704 if (vmajor)
705 *vmajor = majversion;
706 if (vminor)
707 *vminor = minversion;
708 if (type_return)
709 *type_return = t;
710 return true;
711 }
712 }
713
714 const QString typeStr = type.toString();
715 if (isInlineComponent) {
716 Q_ASSERT(type_return);
717 bool ret = uri == typeStr;
718 if (ret) {
719 Q_ASSERT(!type_return->isValid());
720 auto createICType = [&]() {
721 auto typePriv = new QQmlTypePrivate {QQmlType::RegistrationType::InlineComponentType};
722 bool ok = false;
723 typePriv->extraData.id->objectId = QUrl(this->url).fragment().toInt(ok: &ok);
724 Q_ASSERT(ok);
725 typePriv->extraData.id->url = QUrl(this->url);
726 auto icType = QQmlType(typePriv);
727 typePriv->release();
728 return icType;
729 };
730 if (containingType.isValid()) {
731 // we currently cannot reference a Singleton inside itself
732 // in that case, containingType is still invalid
733 int icID = containingType.lookupInlineComponentIdByName(name: typeStr);
734 if (icID != -1) {
735 *type_return = containingType.lookupInlineComponentById(objectid: icID);
736 } else {
737 auto icType = createICType();
738 int placeholderId = containingType.generatePlaceHolderICId();
739 const_cast<QQmlImportInstance*>(this)->containingType.associateInlineComponent(name: typeStr, objectID: placeholderId, metaTypeIds: CompositeMetaTypeIds {}, existingType: icType);
740 *type_return = QQmlType(icType);
741 }
742 } else {
743 *type_return = createICType();
744 }
745 }
746 return ret;
747 }
748 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(akey: typeStr), end = qmlDirComponents.end();
749 if (it != end) {
750 QString componentUrl;
751 bool isCompositeSingleton = false;
752 QQmlDirComponents::ConstIterator candidate = end;
753 for ( ; it != end && it.key() == typeStr; ++it) {
754 const QQmlDirParser::Component &c = *it;
755 switch (registrationType) {
756 case QQmlType::AnyRegistrationType:
757 break;
758 case QQmlType::CompositeSingletonType:
759 if (!c.singleton)
760 continue;
761 break;
762 default:
763 if (c.singleton)
764 continue;
765 break;
766 }
767
768 // importing version -1 means import ALL versions
769 if ((majversion == -1) ||
770 (implicitlyImported && c.internal) || // allow the implicit import of internal types
771 (c.majorVersion == majversion && c.minorVersion <= minversion)) {
772 // Is this better than the previous candidate?
773 if ((candidate == end) ||
774 (c.majorVersion > candidate->majorVersion) ||
775 ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) {
776 if (base) {
777 componentUrl = resolveLocalUrl(url: QString(url + c.typeName + dotqml_string), relative: c.fileName);
778 if (c.internal) {
779 if (resolveLocalUrl(url: *base, relative: c.fileName) != componentUrl)
780 continue; // failed attempt to access an internal type
781 }
782
783 const bool recursion = *base == componentUrl;
784 if (typeRecursionDetected)
785 *typeRecursionDetected = recursion;
786
787 if (recursionRestriction == QQmlImport::PreventRecursion && recursion) {
788 continue; // no recursion
789 }
790 }
791
792 // This is our best candidate so far
793 candidate = it;
794 isCompositeSingleton = c.singleton;
795 }
796 }
797 }
798
799 if (candidate != end) {
800 if (!base) // ensure we have a componentUrl
801 componentUrl = resolveLocalUrl(url: QString(url + candidate->typeName + dotqml_string), relative: candidate->fileName);
802 QQmlType returnType = QQmlMetaType::typeForUrl(urlString: componentUrl, typeName: type, isCompositeSingleton,
803 errors: nullptr, majorVersion: candidate->majorVersion,
804 minorVersion: candidate->minorVersion);
805 if (vmajor)
806 *vmajor = candidate->majorVersion;
807 if (vminor)
808 *vminor = candidate->minorVersion;
809 if (type_return)
810 *type_return = returnType;
811 return returnType.isValid();
812 }
813 } else if (!isLibrary && !localDirectoryPath.isEmpty()) {
814 QString qmlUrl;
815 bool exists = false;
816
817 const QString urlsToTry[2] = {
818 typeStr + dotqml_string, // Type -> Type.qml
819 typeStr + dotuidotqml_string // Type -> Type.ui.qml
820 };
821 for (const QString &urlToTry : urlsToTry) {
822 exists = typeLoader->fileExists(path: localDirectoryPath, file: urlToTry);
823 if (exists) {
824#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
825 // don't let function.qml confuse the use of "new Function(...)" for example.
826 if (!QQml_isFileCaseCorrect(localDirectoryPath + urlToTry)) {
827 exists = false;
828 if (errors) {
829 QQmlError caseError;
830 caseError.setDescription(QLatin1String("File name case mismatch"));
831 errors->append(caseError);
832 }
833 break;
834 }
835#else
836 Q_UNUSED(errors);
837#endif
838 qmlUrl = url + urlToTry;
839 break;
840 }
841 }
842
843 if (exists) {
844 const bool recursion = base && *base == qmlUrl;
845 if (typeRecursionDetected)
846 *typeRecursionDetected = recursion;
847 if (recursionRestriction == QQmlImport::AllowRecursion || !recursion) {
848 QQmlType returnType = QQmlMetaType::typeForUrl(
849 urlString: qmlUrl, typeName: type, isCompositeSingleton: registrationType == QQmlType::CompositeSingletonType, errors);
850 if (type_return)
851 *type_return = returnType;
852 return returnType.isValid();
853 }
854 }
855 }
856
857 return false;
858}
859
860bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor,
861 QQmlType *type_return, QList<QQmlError> *errors,
862 QQmlType::RegistrationType registrationType,
863 bool *typeRecursionDetected)
864{
865 const QVector<QHashedStringRef> splitName = type.split(sep: Dot);
866 auto resolveTypeInNamespace = [&](QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) -> bool {
867 if (nameSpace->resolveType(typeLoader, type: unqualifiedtype, vmajor, vminor, type_return, base: &base, errors,
868 registrationType, typeRecursionDeteced: typeRecursionDetected))
869 return true;
870 if (nameSpace->imports.count() == 1 && !nameSpace->imports.at(i: 0)->isLibrary && type_return && nameSpace != &unqualifiedset) {
871 // qualified, and only 1 url
872 *type_return = QQmlMetaType::typeForUrl(
873 urlString: resolveLocalUrl(url: nameSpace->imports.at(i: 0)->url,
874 relative: unqualifiedtype.toString() + QLatin1String(".qml")),
875 typeName: type, isCompositeSingleton: false, errors);
876 return type_return->isValid();
877 }
878 return false;
879 };
880 switch (splitName.size()) {
881 case 1: {
882 // must be a simple type
883 return resolveTypeInNamespace(type, &unqualifiedset, errors);
884 }
885 case 2: {
886 // either namespace + simple type OR simple type + inline component
887 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(i: 0));
888 if (s) {
889 // namespace + simple type
890 return resolveTypeInNamespace(splitName.at(i: 1), s, errors);
891 } else {
892 if (resolveTypeInNamespace(splitName.at(i: 0), &unqualifiedset, nullptr)) {
893 // either simple type + inline component
894 auto const icName = splitName.at(i: 1).toString();
895 auto objectIndex = type_return->lookupInlineComponentIdByName(name: icName);
896 if (objectIndex != -1) {
897 *type_return = type_return->lookupInlineComponentById(objectid: objectIndex);
898 } else {
899 auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
900 icTypePriv->setContainingType(type_return);
901 icTypePriv->extraData.id->url = type_return->sourceUrl();
902 int placeholderId = type_return->generatePlaceHolderICId();
903 icTypePriv->extraData.id->url.setFragment(fragment: QString::number(placeholderId));
904 auto icType = QQmlType(icTypePriv);
905 icTypePriv->release();
906 type_return->associateInlineComponent(name: icName, objectID: placeholderId, metaTypeIds: CompositeMetaTypeIds {}, existingType: icType);
907 *type_return = icType;
908 }
909 Q_ASSERT(type_return->containingType().isValid());
910 type_return->setPendingResolutionName(icName);
911 return true;
912 } else {
913 // or a failure
914 if (errors) {
915 QQmlError error;
916 error.setDescription(QQmlImportDatabase::tr(sourceText: "- %1 is neither a type nor a namespace").arg(a: splitName.at(i: 0).toString()));
917 errors->prepend(t: error);
918 }
919 return false;
920 }
921 }
922 }
923 case 3: {
924 // must be namespace + simple type + inline component
925 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(i: 0));
926 QQmlError error;
927 if (!s) {
928 error.setDescription(QQmlImportDatabase::tr(sourceText: "- %1 is not a namespace").arg(a: splitName.at(i: 0).toString()));
929 } else {
930 if (resolveTypeInNamespace(splitName.at(i: 1), s, nullptr)) {
931 auto const icName = splitName.at(i: 2).toString();
932 auto objectIndex = type_return->lookupInlineComponentIdByName(name: icName);
933 if (objectIndex != -1)
934 *type_return = type_return->lookupInlineComponentById(objectid: objectIndex);
935 else {
936 auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
937 icTypePriv->setContainingType(type_return);
938 icTypePriv->extraData.id->url = type_return->sourceUrl();
939 int placeholderId = type_return->generatePlaceHolderICId();
940 icTypePriv->extraData.id->url.setFragment(fragment: QString::number(placeholderId));
941 auto icType = QQmlType(icTypePriv);
942 icTypePriv->release();
943 type_return->associateInlineComponent(name: icName, objectID: placeholderId, metaTypeIds: CompositeMetaTypeIds {}, existingType: icType);
944 *type_return = icType;
945 }
946 type_return->setPendingResolutionName(icName);
947 return true;
948 } else {
949 error.setDescription(QQmlImportDatabase::tr(sourceText: "- %1 is not a type").arg(a: splitName.at(i: 1).toString()));
950 }
951 }
952 if (errors) {
953 errors->prepend(t: error);
954 }
955 return false;
956 }
957 default: {
958 // all other numbers suggest a user error
959 if (errors) {
960 QQmlError error;
961 error.setDescription(QQmlImportDatabase::tr(sourceText: "- nested namespaces not allowed"));
962 errors->prepend(t: error);
963 }
964 return false;
965 }
966 }
967 Q_UNREACHABLE();
968}
969
970QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const
971{
972 for (QQmlImportInstance *import : imports) {
973 if (import->uri == uri)
974 return import;
975 }
976 return nullptr;
977}
978
979bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
980 int *vmajor, int *vminor, QQmlType *type_return,
981 QString *base, QList<QQmlError> *errors,
982 QQmlType::RegistrationType registrationType,
983 bool *typeRecursionDetected)
984{
985 QQmlImport::RecursionRestriction recursionRestriction =
986 typeRecursionDetected ? QQmlImport::AllowRecursion : QQmlImport::PreventRecursion;
987
988 bool localTypeRecursionDetected = false;
989 if (!typeRecursionDetected)
990 typeRecursionDetected = &localTypeRecursionDetected;
991
992 if (needsSorting()) {
993 std::stable_partition(first: imports.begin(), last: imports.end(), pred: [](QQmlImportInstance *import) {
994 return import->isInlineComponent;
995 });
996 setNeedsSorting(false);
997 }
998 for (int i=0; i<imports.count(); ++i) {
999 const QQmlImportInstance *import = imports.at(i);
1000 if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base,
1001 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
1002 if (qmlCheckTypes()) {
1003 // check for type clashes
1004 for (int j = i+1; j<imports.count(); ++j) {
1005 const QQmlImportInstance *import2 = imports.at(i: j);
1006 if (import2->resolveType(typeLoader, type, vmajor, vminor, type_return: nullptr, base,
1007 typeRecursionDetected: nullptr, registrationType)) {
1008 if (errors) {
1009 QString u1 = import->url;
1010 QString u2 = import2->url;
1011 if (base) {
1012 QStringRef b(base);
1013 int dot = b.lastIndexOf(ch: Dot);
1014 if (dot >= 0) {
1015 b = b.left(n: dot+1);
1016 QStringRef l = b.left(n: dot);
1017 if (u1.startsWith(s: b))
1018 u1 = u1.mid(position: b.count());
1019 else if (u1 == l)
1020 u1 = QQmlImportDatabase::tr(sourceText: "local directory");
1021 if (u2.startsWith(s: b))
1022 u2 = u2.mid(position: b.count());
1023 else if (u2 == l)
1024 u2 = QQmlImportDatabase::tr(sourceText: "local directory");
1025 }
1026 }
1027
1028 QQmlError error;
1029 if (u1 != u2) {
1030 error.setDescription(QQmlImportDatabase::tr(sourceText: "is ambiguous. Found in %1 and in %2").arg(a: u1).arg(a: u2));
1031 } else {
1032 error.setDescription(QQmlImportDatabase::tr(sourceText: "is ambiguous. Found in %1 in version %2.%3 and %4.%5")
1033 .arg(a: u1)
1034 .arg(a: import->majversion).arg(a: import->minversion)
1035 .arg(a: import2->majversion).arg(a: import2->minversion));
1036 }
1037 errors->prepend(t: error);
1038 }
1039 return false;
1040 }
1041 }
1042 }
1043 return true;
1044 }
1045 }
1046 if (errors) {
1047 QQmlError error;
1048 if (*typeRecursionDetected)
1049 error.setDescription(QQmlImportDatabase::tr(sourceText: "is instantiated recursively"));
1050 else
1051 error.setDescription(QQmlImportDatabase::tr(sourceText: "is not a type"));
1052 errors->prepend(t: error);
1053 }
1054 return false;
1055}
1056
1057bool QQmlImportNamespace::needsSorting() const
1058{
1059 return nextNamespace == this;
1060}
1061
1062void QQmlImportNamespace::setNeedsSorting(bool needsSorting)
1063{
1064 Q_ASSERT(nextNamespace == this || nextNamespace == nullptr);
1065 nextNamespace = needsSorting ? this : nullptr;
1066}
1067
1068QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
1069: ref(1), typeLoader(loader) {
1070}
1071
1072QQmlImportsPrivate::~QQmlImportsPrivate()
1073{
1074 while (QQmlImportNamespace *ns = qualifiedSets.takeFirst())
1075 delete ns;
1076}
1077
1078QQmlImportNamespace *QQmlImportsPrivate::findQualifiedNamespace(const QHashedStringRef &prefix) const
1079{
1080 for (QQmlImportNamespace *ns = qualifiedSets.first(); ns; ns = qualifiedSets.next(v: ns)) {
1081 if (prefix == ns->prefix)
1082 return ns;
1083 }
1084 return nullptr;
1085}
1086
1087/*
1088 Returns the list of possible versioned URI combinations. For example, if \a uri is
1089 QtQml.Models, \a vmaj is 2, and \a vmin is 0, this method returns the following:
1090 [QtQml.Models.2.0, QtQml.2.0.Models, QtQml.Models.2, QtQml.2.Models, QtQml.Models]
1091 */
1092static QStringList versionUriList(const QString &uri, int vmaj, int vmin)
1093{
1094 QStringList result;
1095 for (int version = QQmlImports::FullyVersioned; version <= QQmlImports::Unversioned; ++version) {
1096 int index = uri.length();
1097 do {
1098 QString versionUri = uri;
1099 versionUri.insert(i: index, s: QQmlImports::versionString(vmaj, vmin, version: static_cast<QQmlImports::ImportVersion>(version)));
1100 result += versionUri;
1101
1102 index = uri.lastIndexOf(c: Dot, from: index - 1);
1103 } while (index > 0 && version != QQmlImports::Unversioned);
1104 }
1105 return result;
1106}
1107
1108static QVector<QStaticPlugin> makePlugins()
1109{
1110 QVector<QStaticPlugin> plugins;
1111 // To avoid traversing all static plugins for all imports, we cut down
1112 // the list the first time called to only contain QML plugins:
1113 const auto staticPlugins = QPluginLoader::staticPlugins();
1114 for (const QStaticPlugin &plugin : staticPlugins) {
1115 const QString iid = plugin.metaData().value(key: QLatin1String("IID")).toString();
1116 if (iid == QLatin1String(QQmlEngineExtensionInterface_iid)
1117 || iid == QLatin1String(QQmlExtensionInterface_iid)
1118 || iid == QLatin1String(QQmlExtensionInterface_iid_old)) {
1119 plugins.append(t: plugin);
1120 }
1121 }
1122 return plugins;
1123}
1124
1125/*
1126 Get all static plugins that are QML plugins and has a meta data URI that matches with one of
1127 \a versionUris, which is a list of all possible versioned URI combinations - see versionUriList()
1128 above.
1129 */
1130bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QStringList &versionUris,
1131 const QString &qmldirPath, QList<QQmlError> *errors)
1132{
1133 static const QVector<QStaticPlugin> plugins = makePlugins();
1134 for (const QStaticPlugin &plugin : plugins) {
1135 // Since a module can list more than one plugin, we keep iterating even after we found a match.
1136 QObject *instance = plugin.instance();
1137 if (qobject_cast<QQmlEngineExtensionPlugin *>(object: instance)
1138 || qobject_cast<QQmlExtensionPlugin *>(object: instance)) {
1139 const QJsonArray metaTagsUriList = plugin.metaData().value(key: QLatin1String("uri")).toArray();
1140 if (metaTagsUriList.isEmpty()) {
1141 if (errors) {
1142 QQmlError error;
1143 error.setDescription(QQmlImportDatabase::tr(sourceText: "static plugin for module \"%1\" with name \"%2\" has no metadata URI")
1144 .arg(a: uri).arg(a: QString::fromUtf8(str: instance->metaObject()->className())));
1145 error.setUrl(QUrl::fromLocalFile(localfile: qmldirPath));
1146 errors->prepend(t: error);
1147 }
1148 return false;
1149 }
1150 // A plugin can be set up to handle multiple URIs, so go through the list:
1151 for (const QJsonValue &metaTagUri : metaTagsUriList) {
1152 if (versionUris.contains(str: metaTagUri.toString())) {
1153 result.append(t: qMakePair(x: plugin, y: metaTagsUriList));
1154 break;
1155 }
1156 }
1157 }
1158 }
1159 return true;
1160}
1161
1162/*
1163Import an extension defined by a qmldir file.
1164
1165\a qmldirFilePath is a raw file path.
1166*/
1167bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
1168 const QString &uri,
1169 int vmaj, int vmin,
1170 QQmlImportDatabase *database,
1171 const QQmlTypeLoaderQmldirContent &qmldir,
1172 QList<QQmlError> *errors)
1173{
1174 Q_ASSERT(qmldir.hasContent());
1175
1176 if (qmlImportTrace())
1177 qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: "
1178 << "loaded " << qmldirFilePath;
1179
1180 if (designerSupportRequired && !qmldir.designerSupported()) {
1181 if (errors) {
1182 QQmlError error;
1183 error.setDescription(QQmlImportDatabase::tr(sourceText: "module does not support the designer \"%1\"").arg(a: qmldir.typeNamespace()));
1184 error.setUrl(QUrl::fromLocalFile(localfile: qmldirFilePath));
1185 errors->prepend(t: error);
1186 }
1187 return false;
1188 }
1189
1190 int qmldirPluginCount = qmldir.plugins().count();
1191 if (qmldirPluginCount == 0)
1192 return true;
1193
1194 if (database->qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(value: qmldirFilePath)) {
1195 if ((vmaj >= 0 && vmin >= 0)
1196 ? !QQmlMetaType::isModule(module: uri, versionMajor: vmaj, versionMinor: vmin)
1197 : !QQmlMetaType::isAnyModule(uri)) {
1198 QQmlMetaType::qmlRegisterModuleTypes(uri, majorVersion: vmaj);
1199 }
1200 } else {
1201 // First search for listed qmldir plugins dynamically. If we cannot resolve them all, we continue
1202 // searching static plugins that has correct metadata uri. Note that since we only know the uri
1203 // for a static plugin, and not the filename, we cannot know which static plugin belongs to which
1204 // listed plugin inside qmldir. And for this reason, mixing dynamic and static plugins inside a
1205 // single module is not recommended.
1206
1207 QString typeNamespace = qmldir.typeNamespace();
1208 QString qmldirPath = qmldirFilePath;
1209 int slash = qmldirPath.lastIndexOf(c: Slash);
1210 if (slash > 0)
1211 qmldirPath.truncate(pos: slash);
1212
1213 int dynamicPluginsFound = 0;
1214 int staticPluginsFound = 0;
1215
1216#if QT_CONFIG(library)
1217 const auto qmldirPlugins = qmldir.plugins();
1218 for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) {
1219 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath: plugin.path, baseName: plugin.name);
1220 if (!resolvedFilePath.isEmpty()) {
1221 dynamicPluginsFound++;
1222 if (!database->importDynamicPlugin(filePath: resolvedFilePath, uri, importNamespace: typeNamespace, vmaj, errors)) {
1223 if (errors) {
1224 // XXX TODO: should we leave the import plugin error alone?
1225 // Here, we pop it off the top and coalesce it into this error's message.
1226 // The reason is that the lower level may add url and line/column numbering information.
1227 QQmlError error;
1228 error.setDescription(
1229 QQmlImportDatabase::tr(
1230 sourceText: "plugin cannot be loaded for module \"%1\": %2")
1231 .arg(args: uri, args: errors->takeFirst().description()));
1232 error.setUrl(QUrl::fromLocalFile(localfile: qmldirFilePath));
1233 errors->prepend(t: error);
1234 }
1235 return false;
1236 }
1237 }
1238 }
1239#endif // QT_CONFIG(library)
1240
1241 if (dynamicPluginsFound < qmldirPluginCount) {
1242 // Check if the missing plugins can be resolved statically. We do this by looking at
1243 // the URIs embedded in a plugins meta data. Since those URIs can be anything from fully
1244 // versioned to unversioned, we need to compare with differnt version strings. If a module
1245 // has several plugins, they must all have the same version. Start by populating pluginPairs
1246 // with relevant plugins to cut the list short early on:
1247 const QStringList versionUris = versionUriList(uri, vmaj, vmin);
1248 QVector<StaticPluginPair> pluginPairs;
1249 if (!populatePluginPairVector(result&: pluginPairs, uri, versionUris, qmldirPath: qmldirFilePath, errors))
1250 return false;
1251
1252 const QString basePath = QFileInfo(qmldirPath).absoluteFilePath();
1253 for (const QString &versionUri : versionUris) {
1254 for (const StaticPluginPair &pair : qAsConst(t&: pluginPairs)) {
1255 for (const QJsonValue &metaTagUri : pair.second) {
1256 if (versionUri == metaTagUri.toString()) {
1257 staticPluginsFound++;
1258 QObject *instance = pair.first.instance();
1259 if (!database->importStaticPlugin(instance, basePath, uri, typeNamespace, vmaj, errors)) {
1260 if (errors) {
1261 QQmlError poppedError = errors->takeFirst();
1262 QQmlError error;
1263 error.setDescription(QQmlImportDatabase::tr(sourceText: "static plugin for module \"%1\" with name \"%2\" cannot be loaded: %3")
1264 .arg(a: uri).arg(a: QString::fromUtf8(str: instance->metaObject()->className())).arg(a: poppedError.description()));
1265 error.setUrl(QUrl::fromLocalFile(localfile: qmldirFilePath));
1266 errors->prepend(t: error);
1267 }
1268 return false;
1269 }
1270 break;
1271 }
1272 }
1273 }
1274 if (staticPluginsFound > 0)
1275 break;
1276 }
1277 }
1278
1279 if ((dynamicPluginsFound + staticPluginsFound) < qmldirPluginCount) {
1280 if (errors) {
1281 QQmlError error;
1282 if (qmldirPluginCount > 1 && staticPluginsFound > 0)
1283 error.setDescription(QQmlImportDatabase::tr(sourceText: "could not resolve all plugins for module \"%1\"").arg(a: uri));
1284 else
1285 error.setDescription(QQmlImportDatabase::tr(sourceText: "module \"%1\" plugin \"%2\" not found").arg(a: uri).arg(a: qmldir.plugins()[dynamicPluginsFound].name));
1286 error.setUrl(QUrl::fromLocalFile(localfile: qmldirFilePath));
1287 errors->prepend(t: error);
1288 }
1289 return false;
1290 }
1291
1292 database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(value: qmldirFilePath);
1293 }
1294 return true;
1295}
1296
1297bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
1298 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
1299{
1300 Q_ASSERT(errors);
1301 Q_ASSERT(qmldir);
1302
1303 *qmldir = typeLoader->qmldirContent(filePath: qmldirIdentifier);
1304 if ((*qmldir).hasContent()) {
1305 // Ensure that parsing was successful
1306 if ((*qmldir).hasError()) {
1307 QUrl url = QUrl::fromLocalFile(localfile: qmldirIdentifier);
1308 const QList<QQmlError> qmldirErrors = (*qmldir).errors(uri);
1309 for (int i = 0; i < qmldirErrors.size(); ++i) {
1310 QQmlError error = qmldirErrors.at(i);
1311 error.setUrl(url);
1312 errors->append(t: error);
1313 }
1314 return false;
1315 }
1316 }
1317
1318 return true;
1319}
1320
1321QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
1322{
1323 QString dir = dir_arg;
1324 if (dir.endsWith(c: Slash) || dir.endsWith(c: Backslash))
1325 dir.chop(n: 1);
1326
1327 QStringList paths = database->fileImportPath;
1328 if (!paths.isEmpty())
1329 std::sort(first: paths.begin(), last: paths.end(), comp: std::greater<QString>()); // Ensure subdirs preceed their parents.
1330
1331 QString stableRelativePath = dir;
1332 for (const QString &path : qAsConst(t&: paths)) {
1333 if (dir.startsWith(s: path)) {
1334 stableRelativePath = dir.mid(position: path.length()+1);
1335 break;
1336 }
1337 }
1338
1339 stableRelativePath.replace(before: Backslash, after: Slash);
1340
1341 // remove optional versioning in dot notation from uri
1342 int versionDot = stableRelativePath.lastIndexOf(c: Dot);
1343 if (versionDot >= 0) {
1344 int nextSlash = stableRelativePath.indexOf(c: Slash, from: versionDot);
1345 if (nextSlash >= 0)
1346 stableRelativePath.remove(i: versionDot, len: nextSlash - versionDot);
1347 else
1348 stableRelativePath = stableRelativePath.left(n: versionDot);
1349 }
1350
1351 stableRelativePath.replace(before: Slash, after: Dot);
1352
1353 return stableRelativePath;
1354}
1355
1356/*
1357Locates the qmldir file for \a uri version \a vmaj.vmin. Returns true if found,
1358and fills in outQmldirFilePath and outQmldirUrl appropriately. Otherwise returns
1359false.
1360*/
1361QQmlImports::LocalQmldirResult QQmlImportsPrivate::locateLocalQmldir(
1362 const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
1363 QString *outQmldirFilePath, QString *outQmldirPathUrl)
1364{
1365 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
1366
1367 // Check cache first
1368
1369 QQmlImportDatabase::QmldirCache *cacheHead = nullptr;
1370 {
1371 QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(key: uri);
1372 if (cachePtr) {
1373 cacheHead = *cachePtr;
1374 QQmlImportDatabase::QmldirCache *cache = cacheHead;
1375 while (cache) {
1376 if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
1377 *outQmldirFilePath = cache->qmldirFilePath;
1378 *outQmldirPathUrl = cache->qmldirPathUrl;
1379 return cache->qmldirFilePath.isEmpty() ? QQmlImports::QmldirNotFound
1380 : QQmlImports::QmldirFound;
1381 }
1382 cache = cache->next;
1383 }
1384 }
1385 }
1386
1387 QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(e: database->engine)->typeLoader;
1388
1389 // Interceptor might redirect remote files to local ones.
1390 QQmlAbstractUrlInterceptor *interceptor = typeLoader.engine()->urlInterceptor();
1391 QStringList localImportPaths = database->importPathList(
1392 type: interceptor ? QQmlImportDatabase::LocalOrRemote : QQmlImportDatabase::Local);
1393
1394 // Search local import paths for a matching version
1395 const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(
1396 uri, basePaths: localImportPaths, vmaj, vmin);
1397 bool pathTurnedRemote = false;
1398 for (QString qmldirPath : qmlDirPaths) {
1399 if (interceptor) {
1400 const QUrl intercepted = interceptor->intercept(
1401 path: QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
1402 type: QQmlAbstractUrlInterceptor::QmldirFile);
1403 qmldirPath = QQmlFile::urlToLocalFileOrQrc(intercepted);
1404 if (!pathTurnedRemote && qmldirPath.isEmpty() && !QQmlFile::isLocalFile(url: intercepted))
1405 pathTurnedRemote = true;
1406 }
1407
1408 QString absoluteFilePath = typeLoader.absoluteFilePath(path: qmldirPath);
1409 if (!absoluteFilePath.isEmpty()) {
1410 QString url;
1411 const QStringRef absolutePath = absoluteFilePath.leftRef(n: absoluteFilePath.lastIndexOf(c: Slash) + 1);
1412 if (absolutePath.at(i: 0) == Colon) {
1413 url = QLatin1String("qrc") + absolutePath;
1414 } else {
1415 url = QUrl::fromLocalFile(localfile: absolutePath.toString()).toString();
1416 // This handles the UNC path case as when the path is retrieved from the QUrl it
1417 // will convert the host name from upper case to lower case. So the absoluteFilePath
1418 // is changed at this point to make sure it will match later on in that case.
1419 if (absoluteFilePath.startsWith(s: QLatin1String("//")))
1420 absoluteFilePath = QUrl::fromLocalFile(localfile: absoluteFilePath).toString(options: QUrl::RemoveScheme);
1421 }
1422 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
1423 cache->versionMajor = vmaj;
1424 cache->versionMinor = vmin;
1425 cache->qmldirFilePath = absoluteFilePath;
1426 cache->qmldirPathUrl = url;
1427 cache->next = cacheHead;
1428 database->qmldirCache.insert(key: uri, value: cache);
1429
1430 *outQmldirFilePath = absoluteFilePath;
1431 *outQmldirPathUrl = url;
1432
1433 return QQmlImports::QmldirFound;
1434 }
1435 }
1436
1437 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
1438 cache->versionMajor = vmaj;
1439 cache->versionMinor = vmin;
1440 cache->next = cacheHead;
1441 database->qmldirCache.insert(key: uri, value: cache);
1442
1443 return pathTurnedRemote ? QQmlImports::QmldirInterceptedToRemote : QQmlImports::QmldirNotFound;
1444}
1445
1446bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin,
1447 QList<QQmlError> *errors)
1448{
1449 int lowest_min = INT_MAX;
1450 int highest_min = INT_MIN;
1451
1452 typedef QQmlDirComponents::const_iterator ConstIterator;
1453 const QQmlDirComponents &components = qmldir.components();
1454
1455 ConstIterator cend = components.constEnd();
1456 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1457 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1458 if ((cit2->typeName == cit->typeName) &&
1459 (cit2->majorVersion == cit->majorVersion) &&
1460 (cit2->minorVersion == cit->minorVersion)) {
1461 // This entry clashes with a predecessor
1462 QQmlError error;
1463 error.setDescription(QQmlImportDatabase::tr(sourceText: "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1464 .arg(a: cit->typeName).arg(a: cit->majorVersion).arg(a: cit->minorVersion).arg(a: uri));
1465 errors->prepend(t: error);
1466 return false;
1467 }
1468 }
1469
1470 if (cit->majorVersion == vmaj) {
1471 lowest_min = qMin(a: lowest_min, b: cit->minorVersion);
1472 highest_min = qMax(a: highest_min, b: cit->minorVersion);
1473 }
1474 }
1475
1476 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1477 const QQmlDirScripts &scripts = qmldir.scripts();
1478
1479 SConstIterator send = scripts.constEnd();
1480 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1481 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1482 if ((sit2->nameSpace == sit->nameSpace) &&
1483 (sit2->majorVersion == sit->majorVersion) &&
1484 (sit2->minorVersion == sit->minorVersion)) {
1485 // This entry clashes with a predecessor
1486 QQmlError error;
1487 error.setDescription(QQmlImportDatabase::tr(sourceText: "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1488 .arg(a: sit->nameSpace).arg(a: sit->majorVersion).arg(a: sit->minorVersion).arg(a: uri));
1489 errors->prepend(t: error);
1490 return false;
1491 }
1492 }
1493
1494 if (sit->majorVersion == vmaj) {
1495 lowest_min = qMin(a: lowest_min, b: sit->minorVersion);
1496 highest_min = qMax(a: highest_min, b: sit->minorVersion);
1497 }
1498 }
1499
1500 if (lowest_min > vmin || highest_min < vmin) {
1501 QQmlError error;
1502 error.setDescription(QQmlImportDatabase::tr(sourceText: "module \"%1\" version %2.%3 is not installed").arg(a: uri).arg(a: vmaj).arg(a: vmin));
1503 errors->prepend(t: error);
1504 return false;
1505 }
1506
1507 return true;
1508}
1509
1510QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) const
1511{
1512 QQmlImportNamespace *nameSpace = nullptr;
1513
1514 if (prefix.isEmpty()) {
1515 nameSpace = &unqualifiedset;
1516 } else {
1517 nameSpace = findQualifiedNamespace(prefix);
1518
1519 if (!nameSpace) {
1520 nameSpace = new QQmlImportNamespace;
1521 nameSpace->prefix = prefix;
1522 qualifiedSets.append(v: nameSpace);
1523 }
1524 }
1525
1526 return nameSpace;
1527}
1528
1529QQmlImportInstance *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace,
1530 const QString &uri, const QString &url, int vmaj, int vmin,
1531 QV4::CompiledData::Import::ImportType type,
1532 QList<QQmlError> *errors, bool lowPrecedence)
1533{
1534 Q_ASSERT(nameSpace);
1535 Q_ASSERT(errors);
1536 Q_UNUSED(errors);
1537 Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
1538
1539 QQmlImportInstance *import = new QQmlImportInstance;
1540 import->uri = uri;
1541 import->url = url;
1542 import->localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
1543 import->majversion = vmaj;
1544 import->minversion = vmin;
1545 import->isLibrary = (type == QV4::CompiledData::Import::ImportLibrary);
1546
1547 if (lowPrecedence)
1548 nameSpace->imports.append(t: import);
1549 else
1550 nameSpace->imports.prepend(t: import);
1551
1552 return import;
1553}
1554
1555bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &prefix,
1556 int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
1557 QQmlImportDatabase *database,
1558 QList<QQmlError> *errors)
1559{
1560 Q_ASSERT(database);
1561 Q_ASSERT(errors);
1562
1563 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1564 Q_ASSERT(nameSpace);
1565
1566 QQmlImportInstance *inserted = addImportToNamespace(nameSpace, uri, url: qmldirUrl, vmaj, vmin, type: QV4::CompiledData::Import::ImportLibrary, errors);
1567 Q_ASSERT(inserted);
1568
1569 if (!incomplete) {
1570 QQmlTypeLoaderQmldirContent qmldir;
1571
1572 if (!qmldirIdentifier.isEmpty()) {
1573 if (!getQmldirContent(qmldirIdentifier, uri, qmldir: &qmldir, errors))
1574 return false;
1575
1576 if (qmldir.hasContent()) {
1577 if (!importExtension(qmldirFilePath: qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors))
1578 return false;
1579
1580 if (!inserted->setQmldirContent(resolvedUrl: qmldirUrl, qmldir, nameSpace, errors))
1581 return false;
1582 }
1583 }
1584
1585 // Ensure that we are actually providing something
1586 if ((vmaj < 0) || (vmin < 0) || !QQmlMetaType::isModule(module: uri, versionMajor: vmaj, versionMinor: vmin)) {
1587 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1588 QQmlError error;
1589 if (QQmlMetaType::isAnyModule(uri))
1590 error.setDescription(QQmlImportDatabase::tr(sourceText: "module \"%1\" version %2.%3 is not installed").arg(a: uri).arg(a: vmaj).arg(a: vmin));
1591 else
1592 error.setDescription(QQmlImportDatabase::tr(sourceText: "module \"%1\" is not installed").arg(a: uri));
1593 errors->prepend(t: error);
1594 return false;
1595 } else if ((vmaj >= 0) && (vmin >= 0) && qmldir.hasContent()) {
1596 // Verify that the qmldir content is valid for this version
1597 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1598 return false;
1599 }
1600 }
1601 }
1602
1603 return true;
1604}
1605
1606bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix,
1607 int vmaj, int vmin,
1608 bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
1609 QList<QQmlError> *errors)
1610{
1611 Q_ASSERT(errors);
1612
1613 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1614 Q_ASSERT(nameSpace);
1615
1616 // The uri for this import. For library imports this is the same as uri
1617 // specified by the user, but it may be different in the case of file imports.
1618 QString importUri = uri;
1619 QString qmldirUrl = resolveLocalUrl(url: base, relative: importUri + (importUri.endsWith(c: Slash)
1620 ? String_qmldir
1621 : Slash_qmldir));
1622 if (QQmlAbstractUrlInterceptor *interceptor = typeLoader->engine()->urlInterceptor()) {
1623 qmldirUrl = interceptor->intercept(path: QUrl(qmldirUrl),
1624 type: QQmlAbstractUrlInterceptor::QmldirFile).toString();
1625 }
1626 QString qmldirIdentifier;
1627
1628 if (QQmlFile::isLocalFile(url: qmldirUrl)) {
1629
1630 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1631 Q_ASSERT(!localFileOrQrc.isEmpty());
1632
1633 const QString dir = localFileOrQrc.left(n: localFileOrQrc.lastIndexOf(c: Slash) + 1);
1634 if (!typeLoader->directoryExists(path: dir)) {
1635 if (!isImplicitImport) {
1636 QQmlError error;
1637 error.setDescription(QQmlImportDatabase::tr(sourceText: "\"%1\": no such directory").arg(a: uri));
1638 error.setUrl(QUrl(qmldirUrl));
1639 errors->prepend(t: error);
1640 }
1641 return false;
1642 }
1643
1644 // Transforms the (possible relative) uri into our best guess relative to the
1645 // import paths.
1646 importUri = resolvedUri(dir_arg: dir, database);
1647 if (importUri.endsWith(c: Slash))
1648 importUri.chop(n: 1);
1649
1650 if (!typeLoader->absoluteFilePath(path: localFileOrQrc).isEmpty())
1651 qmldirIdentifier = localFileOrQrc;
1652
1653 } else if (nameSpace->prefix.isEmpty() && !incomplete) {
1654
1655 if (!isImplicitImport) {
1656 QQmlError error;
1657 error.setDescription(QQmlImportDatabase::tr(sourceText: "import \"%1\" has no qmldir and no namespace").arg(a: importUri));
1658 error.setUrl(QUrl(qmldirUrl));
1659 errors->prepend(t: error);
1660 }
1661
1662 return false;
1663
1664 }
1665
1666 // The url for the path containing files for this import
1667 QString url = resolveLocalUrl(url: base, relative: uri);
1668 if (!url.endsWith(c: Slash) && !url.endsWith(c: Backslash))
1669 url += Slash;
1670
1671 // ### For enum support, we are now adding the implicit import always (and earlier). Bail early
1672 // if the implicit import has already been explicitly added, otherwise we can run into issues
1673 // with duplicate imports. However remember that we attempted to add this as implicit import, to
1674 // allow for the loading of internal types.
1675 if (isImplicitImport) {
1676 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
1677 it != nameSpace->imports.constEnd(); ++it) {
1678 if ((*it)->url == url) {
1679 (*it)->implicitlyImported = true;
1680 return true;
1681 }
1682 }
1683 }
1684
1685 QQmlImportInstance *inserted = addImportToNamespace(nameSpace, uri: importUri, url, vmaj, vmin, type: QV4::CompiledData::Import::ImportFile, errors, lowPrecedence: isImplicitImport);
1686 Q_ASSERT(inserted);
1687
1688 if (!incomplete && !qmldirIdentifier.isEmpty()) {
1689 QQmlTypeLoaderQmldirContent qmldir;
1690 if (!getQmldirContent(qmldirIdentifier, uri: importUri, qmldir: &qmldir, errors))
1691 return false;
1692
1693 if (qmldir.hasContent()) {
1694 if (!importExtension(qmldirFilePath: qmldir.pluginLocation(), uri: importUri, vmaj, vmin, database, qmldir, errors))
1695 return false;
1696
1697 if (!inserted->setQmldirContent(resolvedUrl: url, qmldir, nameSpace, errors))
1698 return false;
1699 }
1700 }
1701
1702 return true;
1703}
1704
1705bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString &prefix,
1706 const QString &qmldirIdentifier, const QString& qmldirUrl,
1707 QQmlImportDatabase *database, QList<QQmlError> *errors)
1708{
1709 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1710 Q_ASSERT(nameSpace);
1711
1712 if (QQmlImportInstance *import = nameSpace->findImport(uri)) {
1713 QQmlTypeLoaderQmldirContent qmldir;
1714 if (!getQmldirContent(qmldirIdentifier, uri, qmldir: &qmldir, errors))
1715 return false;
1716
1717 if (qmldir.hasContent()) {
1718 int vmaj = import->majversion;
1719 int vmin = import->minversion;
1720 if (!importExtension(qmldirFilePath: qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors))
1721 return false;
1722
1723 if (import->setQmldirContent(resolvedUrl: qmldirUrl, qmldir, nameSpace, errors)) {
1724 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1725 // The implicit import qmldir can be empty, and plugins have no extra versions
1726 if (uri != QLatin1String(".") && !QQmlMetaType::isModule(module: uri, versionMajor: vmaj, versionMinor: vmin)) {
1727 QQmlError error;
1728 if (QQmlMetaType::isAnyModule(uri))
1729 error.setDescription(QQmlImportDatabase::tr(sourceText: "module \"%1\" version %2.%3 is not installed").arg(a: uri).arg(a: vmaj).arg(a: vmin));
1730 else
1731 error.setDescription(QQmlImportDatabase::tr(sourceText: "module \"%1\" is not installed").arg(a: uri));
1732 errors->prepend(t: error);
1733 return false;
1734 }
1735 } else if ((vmaj >= 0) && (vmin >= 0)) {
1736 // Verify that the qmldir content is valid for this version
1737 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1738 return false;
1739 }
1740 return true;
1741 }
1742 }
1743 }
1744
1745 if (errors->isEmpty()) {
1746 QQmlError error;
1747 error.setDescription(QQmlTypeLoader::tr(sourceText: "Cannot update qmldir content for '%1'").arg(a: uri));
1748 errors->prepend(t: error);
1749 }
1750
1751 return false;
1752}
1753
1754/*!
1755 \internal
1756
1757 Adds an implicit "." file import. This is equivalent to calling addFileImport(), but error
1758 messages related to the path or qmldir file not existing are suppressed.
1759
1760 Additionally, this will add the import with lowest instead of highest precedence.
1761*/
1762bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors)
1763{
1764 Q_ASSERT(errors);
1765
1766 if (qmlImportTrace())
1767 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
1768 << ")::addImplicitImport";
1769
1770 bool incomplete = !isLocal(url: baseUrl());
1771 return d->addFileImport(uri: QLatin1String("."), prefix: QString(), vmaj: -1, vmin: -1, isImplicitImport: true, incomplete, database: importDb, errors);
1772}
1773
1774/*!
1775 \internal
1776 */
1777bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType)
1778{
1779 importInstance->url = importUrl.toString();
1780 importInstance->uri = name;
1781 importInstance->isInlineComponent = true;
1782 importInstance->majversion = 0;
1783 importInstance->minversion = 0;
1784 importInstance->containingType = containingType;
1785 d->unqualifiedset.imports.push_back(t: importInstance);
1786 d->unqualifiedset.setNeedsSorting(true);
1787 return true;
1788}
1789
1790/*!
1791 \internal
1792
1793 Adds information to \a imports such that subsequent calls to resolveType()
1794 will resolve types qualified by \a prefix by considering types found at the given \a uri.
1795
1796 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
1797 added via addImportPath() (if importType is LibraryImport).
1798
1799 The \a prefix may be empty, in which case the import location is considered for
1800 unqualified types.
1801
1802 The base URL must already have been set with Import::setBaseUrl().
1803
1804 Optionally, the url the import resolved to can be returned by providing the url parameter.
1805 Not all imports will result in an output url being generated, in which case the url will
1806 be set to an empty string.
1807
1808 Returns true on success, and false on failure. In case of failure, the errors array will
1809 filled appropriately.
1810*/
1811bool QQmlImports::addFileImport(QQmlImportDatabase *importDb,
1812 const QString& uri, const QString& prefix, int vmaj, int vmin,
1813 bool incomplete, QList<QQmlError> *errors)
1814{
1815 Q_ASSERT(importDb);
1816 Q_ASSERT(errors);
1817
1818 if (qmlImportTrace())
1819 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addFileImport: "
1820 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1821
1822 return d->addFileImport(uri, prefix, vmaj, vmin, isImplicitImport: false, incomplete, database: importDb, errors);
1823}
1824
1825bool QQmlImports::addLibraryImport(QQmlImportDatabase *importDb,
1826 const QString &uri, const QString &prefix, int vmaj, int vmin,
1827 const QString &qmldirIdentifier, const QString& qmldirUrl, bool incomplete, QList<QQmlError> *errors)
1828{
1829 Q_ASSERT(importDb);
1830 Q_ASSERT(errors);
1831
1832 if (qmlImportTrace())
1833 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addLibraryImport: "
1834 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1835
1836 return d->addLibraryImport(uri, prefix, vmaj, vmin, qmldirIdentifier, qmldirUrl, incomplete, database: importDb, errors);
1837}
1838
1839bool QQmlImports::updateQmldirContent(QQmlImportDatabase *importDb,
1840 const QString &uri, const QString &prefix,
1841 const QString &qmldirIdentifier, const QString& qmldirUrl, QList<QQmlError> *errors)
1842{
1843 Q_ASSERT(importDb);
1844 Q_ASSERT(errors);
1845
1846 if (qmlImportTrace())
1847 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::updateQmldirContent: "
1848 << uri << " to " << qmldirUrl << " as " << prefix;
1849
1850 return d->updateQmldirContent(uri, prefix, qmldirIdentifier, qmldirUrl, database: importDb, errors);
1851}
1852
1853QQmlImports::LocalQmldirResult QQmlImports::locateLocalQmldir(
1854 QQmlImportDatabase *importDb, const QString &uri, int vmaj, int vmin,
1855 QString *qmldirFilePath, QString *url)
1856{
1857 return d->locateLocalQmldir(uri, vmaj, vmin, database: importDb, outQmldirFilePath: qmldirFilePath, outQmldirPathUrl: url);
1858}
1859
1860bool QQmlImports::isLocal(const QString &url)
1861{
1862 return !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1863}
1864
1865bool QQmlImports::isLocal(const QUrl &url)
1866{
1867 return !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1868}
1869
1870QUrl QQmlImports::urlFromLocalFileOrQrcOrUrl(const QString &file)
1871{
1872 QUrl url(QLatin1String(file.at(i: 0) == Colon ? "qrc" : "") + file);
1873
1874 // We don't support single character schemes as those conflict with windows drive letters.
1875 if (url.scheme().length() < 2)
1876 return QUrl::fromLocalFile(localfile: file);
1877 return url;
1878}
1879
1880void QQmlImports::setDesignerSupportRequired(bool b)
1881{
1882 designerSupportRequired = b;
1883}
1884
1885
1886/*!
1887\class QQmlImportDatabase
1888\brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
1889\internal
1890*/
1891QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
1892: engine(e)
1893{
1894 filePluginPath << QLatin1String(".");
1895 // Search order is applicationDirPath(), qrc:/qt-project.org/imports, $QML2_IMPORT_PATH, QLibraryInfo::Qml2ImportsPath
1896
1897 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
1898 addImportPath(dir: installImportsPath);
1899
1900 // env import paths
1901 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QML2_IMPORT_PATH"))) {
1902 const QString envImportPath = qEnvironmentVariable(varName: "QML2_IMPORT_PATH");
1903#if defined(Q_OS_WIN)
1904 QLatin1Char pathSep(';');
1905#else
1906 QLatin1Char pathSep(':');
1907#endif
1908 QStringList paths = envImportPath.split(sep: pathSep, behavior: Qt::SkipEmptyParts);
1909 for (int ii = paths.count() - 1; ii >= 0; --ii)
1910 addImportPath(dir: paths.at(i: ii));
1911 }
1912
1913 addImportPath(QStringLiteral("qrc:/qt-project.org/imports"));
1914 addImportPath(dir: QCoreApplication::applicationDirPath());
1915#if defined(Q_OS_ANDROID)
1916 addImportPath(QStringLiteral("qrc:/android_rcc_bundle/qml"));
1917 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_BUNDLED_LIBS_PATH"))) {
1918 const QString envImportPath = qEnvironmentVariable("QT_BUNDLED_LIBS_PATH");
1919 QLatin1Char pathSep(':');
1920 QStringList paths = envImportPath.split(pathSep, Qt::SkipEmptyParts);
1921 for (int ii = paths.count() - 1; ii >= 0; --ii)
1922 addPluginPath(paths.at(ii));
1923 }
1924#endif
1925}
1926
1927QQmlImportDatabase::~QQmlImportDatabase()
1928{
1929 clearDirCache();
1930}
1931
1932/*!
1933 \internal
1934
1935 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
1936 The \a prefix must contain the dot.
1937
1938 \a qmldirPath is the location of the qmldir file.
1939 */
1940QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1941 const QString &qmldirPath,
1942 const QString &qmldirPluginPath,
1943 const QString &baseName, const QStringList &suffixes,
1944 const QString &prefix)
1945{
1946 QStringList searchPaths = filePluginPath;
1947 bool qmldirPluginPathIsRelative = QDir::isRelativePath(path: qmldirPluginPath);
1948 if (!qmldirPluginPathIsRelative)
1949 searchPaths.prepend(t: qmldirPluginPath);
1950
1951 for (const QString &pluginPath : qAsConst(t&: searchPaths)) {
1952 QString resolvedPath;
1953 if (pluginPath == QLatin1String(".")) {
1954 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
1955 resolvedPath = QDir::cleanPath(path: qmldirPath + Slash + qmldirPluginPath);
1956 else
1957 resolvedPath = qmldirPath;
1958 } else {
1959 if (QDir::isRelativePath(path: pluginPath))
1960 resolvedPath = QDir::cleanPath(path: qmldirPath + Slash + pluginPath);
1961 else
1962 resolvedPath = pluginPath;
1963 }
1964
1965 // hack for resources, should probably go away
1966 if (resolvedPath.startsWith(c: Colon))
1967 resolvedPath = QCoreApplication::applicationDirPath();
1968
1969 if (!resolvedPath.endsWith(c: Slash))
1970 resolvedPath += Slash;
1971
1972#if defined(Q_OS_ANDROID)
1973 if (qmldirPath.size() > 25 && qmldirPath.at(0) == QLatin1Char(':') && qmldirPath.at(1) == QLatin1Char('/') &&
1974 qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"), Qt::CaseInsensitive)) {
1975 QString pluginName = qmldirPath.mid(21) + Slash + baseName;
1976 pluginName.replace(QLatin1Char('/'), QLatin1Char('_'));
1977 QString bundledPath = resolvedPath + QLatin1String("lib") + pluginName;
1978 for (const QString &suffix : suffixes) {
1979 const QString absolutePath = typeLoader->absoluteFilePath(bundledPath + suffix);
1980 if (!absolutePath.isEmpty())
1981 return absolutePath;
1982 }
1983 }
1984#endif
1985 resolvedPath += prefix + baseName;
1986 for (const QString &suffix : suffixes) {
1987 const QString absolutePath = typeLoader->absoluteFilePath(path: resolvedPath + suffix);
1988 if (!absolutePath.isEmpty())
1989 return absolutePath;
1990 }
1991 }
1992
1993 if (qmlImportTrace())
1994 qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
1995 << "in" << qmldirPath;
1996
1997 return QString();
1998}
1999
2000/*!
2001 \internal
2002
2003 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
2004
2005 \table
2006 \header \li Platform \li Valid suffixes
2007 \row \li Windows \li \c .dll
2008 \row \li Unix/Linux \li \c .so
2009 \row \li \macos \li \c .dylib, \c .bundle, \c .so
2010 \endtable
2011
2012 Version number on unix are ignored.
2013*/
2014QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
2015 const QString &qmldirPath, const QString &qmldirPluginPath,
2016 const QString &baseName)
2017{
2018#if defined(Q_OS_WIN)
2019 static const QString prefix;
2020 static const QStringList suffixes = {
2021# ifdef QT_DEBUG
2022 QLatin1String("d.dll"), // try a qmake-style debug build first
2023 QLatin1String(".dll")
2024#else
2025 QLatin1String(".dll"),
2026 QLatin1String("d.dll") // try a qmake-style debug build after
2027# endif
2028 };
2029#elif defined(Q_OS_DARWIN)
2030 static const QString prefix = QLatin1String("lib");
2031 static const QStringList suffixes = {
2032# ifdef QT_DEBUG
2033 QLatin1String("_debug.dylib"), // try a qmake-style debug build first
2034 QLatin1String(".dylib"),
2035# else
2036 QLatin1String(".dylib"),
2037 QLatin1String("_debug.dylib"), // try a qmake-style debug build after
2038# endif
2039 QLatin1String(".so"),
2040 QLatin1String(".bundle")
2041 };
2042#else // Unix
2043 static const QString prefix = QLatin1String("lib");
2044 static const QStringList suffixes = {
2045# if defined(Q_OS_ANDROID)
2046 QStringLiteral(LIBS_SUFFIX),
2047# endif
2048 QLatin1String(".so")
2049
2050 };
2051#endif
2052
2053 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, suffixes, prefix);
2054}
2055
2056/*!
2057 \internal
2058*/
2059QStringList QQmlImportDatabase::pluginPathList() const
2060{
2061 return filePluginPath;
2062}
2063
2064/*!
2065 \internal
2066*/
2067void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
2068{
2069 if (qmlImportTrace())
2070 qDebug().nospace() << "QQmlImportDatabase::setPluginPathList: " << paths;
2071
2072 filePluginPath = paths;
2073}
2074
2075/*!
2076 \internal
2077*/
2078void QQmlImportDatabase::addPluginPath(const QString& path)
2079{
2080 if (qmlImportTrace())
2081 qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
2082
2083 QUrl url = QUrl(path);
2084 if (url.isRelative() || url.scheme() == QLatin1String("file")
2085 || (url.scheme().length() == 1 && QFile::exists(fileName: path)) ) { // windows path
2086 QDir dir = QDir(path);
2087 filePluginPath.prepend(t: dir.canonicalPath());
2088 } else {
2089 filePluginPath.prepend(t: path);
2090 }
2091}
2092
2093/*!
2094 \internal
2095*/
2096void QQmlImportDatabase::addImportPath(const QString& path)
2097{
2098 if (qmlImportTrace())
2099 qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
2100
2101 if (path.isEmpty())
2102 return;
2103
2104 QUrl url = QUrl(path);
2105 QString cPath;
2106
2107 if (url.scheme() == QLatin1String("file")) {
2108 cPath = QQmlFile::urlToLocalFileOrQrc(url);
2109 } else if (path.startsWith(c: QLatin1Char(':'))) {
2110 // qrc directory, e.g. :/foo
2111 // need to convert to a qrc url, e.g. qrc:/foo
2112 cPath = QLatin1String("qrc") + path;
2113 cPath.replace(before: Backslash, after: Slash);
2114 } else if (url.isRelative() ||
2115 (url.scheme().length() == 1 && QFile::exists(fileName: path)) ) { // windows path
2116 QDir dir = QDir(path);
2117 cPath = dir.canonicalPath();
2118 } else {
2119 cPath = path;
2120 cPath.replace(before: Backslash, after: Slash);
2121 }
2122
2123 if (!cPath.isEmpty()
2124 && !fileImportPath.contains(str: cPath))
2125 fileImportPath.prepend(t: cPath);
2126}
2127
2128/*!
2129 \internal
2130*/
2131QStringList QQmlImportDatabase::importPathList(PathType type) const
2132{
2133 if (type == LocalOrRemote)
2134 return fileImportPath;
2135
2136 QStringList list;
2137 for (const QString &path : fileImportPath) {
2138 bool localPath = isPathAbsolute(path) || QQmlFile::isLocalFile(url: path);
2139 if (localPath == (type == Local))
2140 list.append(t: path);
2141 }
2142
2143 return list;
2144}
2145
2146/*!
2147 \internal
2148*/
2149void QQmlImportDatabase::setImportPathList(const QStringList &paths)
2150{
2151 if (qmlImportTrace())
2152 qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
2153
2154 fileImportPath.clear();
2155 for (auto it = paths.crbegin(); it != paths.crend(); ++it)
2156 addImportPath(path: *it);
2157
2158 // Our existing cached paths may have been invalidated
2159 clearDirCache();
2160}
2161
2162/*!
2163 \internal
2164*/
2165static bool registerPluginTypes(QObject *instance, const QString &basePath, const QString &uri,
2166 const QString &typeNamespace, int vmaj, QList<QQmlError> *errors)
2167{
2168 if (qmlImportTrace())
2169 qDebug().nospace() << "QQmlImportDatabase::registerPluginTypes: " << uri << " from " << basePath;
2170
2171 if (!QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors))
2172 return false;
2173
2174 if (vmaj >= 0 && !typeNamespace.isEmpty() && !QQmlMetaType::protectModule(uri, majVersion: vmaj)) {
2175 QQmlError error;
2176 error.setDescription(
2177 QString::fromLatin1(str: "Cannot protect module %1 %2 as it was never registered")
2178 .arg(a: uri).arg(a: vmaj));
2179 errors->append(t: error);
2180 return false;
2181 }
2182
2183 return true;
2184}
2185
2186/*!
2187 \internal
2188*/
2189bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &basePath,
2190 const QString &uri, const QString &typeNamespace, int vmaj, QList<QQmlError> *errors)
2191{
2192 // Dynamic plugins are differentiated by their filepath. For static plugins we
2193 // don't have that information so we use their address as key instead.
2194 const QString uniquePluginID = QString::asprintf(format: "%p", instance);
2195 {
2196 StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
2197 QMutexLocker lock(&plugins->mutex);
2198
2199 // Plugin types are global across all engines and should only be
2200 // registered once. But each engine still needs to be initialized.
2201 bool typesRegistered = plugins->contains(akey: uniquePluginID);
2202
2203 if (typesRegistered) {
2204 Q_ASSERT_X(plugins->value(uniquePluginID).uri == uri,
2205 "QQmlImportDatabase::importStaticPlugin",
2206 "Internal error: Static plugin imported previously with different uri");
2207 } else {
2208 RegisteredPlugin plugin;
2209 plugin.uri = uri;
2210 plugin.loader = nullptr;
2211 plugins->insert(akey: uniquePluginID, avalue: plugin);
2212
2213 if (!registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors))
2214 return false;
2215 }
2216
2217 // Release the lock on plugins early as we're done with the global part. Releasing the lock
2218 // also allows other QML loader threads to acquire the lock while this thread is blocking
2219 // in the initializeEngine call to the gui thread (which in turn may be busy waiting for
2220 // other QML loader threads and thus not process the initializeEngine call).
2221 }
2222
2223 if (!initializedPlugins.contains(value: uniquePluginID))
2224 finalizePlugin(instance, path: uniquePluginID, uri);
2225
2226 return true;
2227}
2228
2229#if QT_CONFIG(library)
2230/*!
2231 \internal
2232*/
2233bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QString &uri,
2234 const QString &typeNamespace, int vmaj, QList<QQmlError> *errors)
2235{
2236 QFileInfo fileInfo(filePath);
2237 const QString absoluteFilePath = fileInfo.absoluteFilePath();
2238
2239 QObject *instance = nullptr;
2240 bool engineInitialized = initializedPlugins.contains(value: absoluteFilePath);
2241 {
2242 StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
2243 QMutexLocker lock(&plugins->mutex);
2244 bool typesRegistered = plugins->contains(akey: absoluteFilePath);
2245
2246 if (typesRegistered) {
2247 Q_ASSERT_X(plugins->value(absoluteFilePath).uri == uri,
2248 "QQmlImportDatabase::importDynamicPlugin",
2249 "Internal error: Plugin imported previously with different uri");
2250 }
2251
2252 if (!engineInitialized || !typesRegistered) {
2253 if (!QQml_isFileCaseCorrect(fileName: absoluteFilePath)) {
2254 if (errors) {
2255 QQmlError error;
2256 error.setDescription(tr(sourceText: "File name case mismatch for \"%1\"").arg(a: absoluteFilePath));
2257 errors->prepend(t: error);
2258 }
2259 return false;
2260 }
2261
2262 QPluginLoader* loader = nullptr;
2263 if (!typesRegistered) {
2264 loader = new QPluginLoader(absoluteFilePath);
2265
2266 if (!loader->load()) {
2267 if (errors) {
2268 QQmlError error;
2269 error.setDescription(loader->errorString());
2270 errors->prepend(t: error);
2271 }
2272 delete loader;
2273 return false;
2274 }
2275 } else {
2276 loader = plugins->value(akey: absoluteFilePath).loader;
2277 }
2278
2279 instance = loader->instance();
2280
2281 if (!typesRegistered) {
2282 RegisteredPlugin plugin;
2283 plugin.uri = uri;
2284 plugin.loader = loader;
2285 plugins->insert(akey: absoluteFilePath, avalue: plugin);
2286
2287 // Continue with shared code path for dynamic and static plugins:
2288 if (!registerPluginTypes(instance, basePath: fileInfo.absolutePath(), uri, typeNamespace, vmaj, errors))
2289 return false;
2290 }
2291 }
2292
2293 // Release the lock on plugins early as we're done with the global part. Releasing the lock
2294 // also allows other QML loader threads to acquire the lock while this thread is blocking
2295 // in the initializeEngine call to the gui thread (which in turn may be busy waiting for
2296 // other QML loader threads and thus not process the initializeEngine call).
2297 }
2298
2299 if (!engineInitialized)
2300 finalizePlugin(instance, path: absoluteFilePath, uri);
2301
2302 return true;
2303}
2304
2305bool QQmlImportDatabase::removeDynamicPlugin(const QString &filePath)
2306{
2307 StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
2308 QMutexLocker lock(&plugins->mutex);
2309
2310 auto it = plugins->find(akey: QFileInfo(filePath).absoluteFilePath());
2311 if (it == plugins->end())
2312 return false;
2313
2314 QPluginLoader *loader = it->loader;
2315 if (!loader)
2316 return false;
2317
2318 if (!loader->unload()) {
2319 qWarning(msg: "Unloading %s failed: %s", qPrintable(it->uri),
2320 qPrintable(loader->errorString()));
2321 }
2322
2323 delete loader;
2324 plugins->erase(it);
2325 return true;
2326}
2327
2328QStringList QQmlImportDatabase::dynamicPlugins() const
2329{
2330 StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
2331 QMutexLocker lock(&plugins->mutex);
2332 QStringList results;
2333 for (auto it = plugins->constBegin(), end = plugins->constEnd(); it != end; ++it) {
2334 if (it->loader != nullptr)
2335 results.append(t: it.key());
2336 }
2337 return results;
2338}
2339#endif // QT_CONFIG(library)
2340
2341void QQmlImportDatabase::clearDirCache()
2342{
2343 QStringHash<QmldirCache *>::ConstIterator itr = qmldirCache.constBegin();
2344 while (itr != qmldirCache.constEnd()) {
2345 QmldirCache *cache = *itr;
2346 do {
2347 QmldirCache *nextCache = cache->next;
2348 delete cache;
2349 cache = nextCache;
2350 } while (cache);
2351
2352 ++itr;
2353 }
2354 qmldirCache.clear();
2355}
2356
2357void QQmlImportDatabase::finalizePlugin(QObject *instance, const QString &path, const QString &uri)
2358{
2359 // The plugin's per-engine initialization does not need lock protection, as this function is
2360 // only called from the engine specific loader thread and importDynamicPlugin as well as
2361 // importStaticPlugin are the only places of access.
2362
2363 initializedPlugins.insert(value: path);
2364 if (auto *extensionIface = qobject_cast<QQmlExtensionInterface *>(object: instance)) {
2365 QQmlEnginePrivate::get(e: engine)->typeLoader.initializeEngine(
2366 extensionIface, uri.toUtf8().constData());
2367 } else if (auto *engineIface = qobject_cast<QQmlEngineExtensionInterface *>(object: instance)) {
2368 QQmlEnginePrivate::get(e: engine)->typeLoader.initializeEngine(
2369 engineIface, uri.toUtf8().constData());
2370 }
2371}
2372
2373QT_END_NAMESPACE
2374

source code of qtdeclarative/src/qml/qml/qqmlimport.cpp