Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmlimport.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Crimson AS <info@crimson.no>
2// Copyright (C) 2016 The Qt Company Ltd.
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 "qqmlimport_p.h"
6
7#include <QtCore/qdebug.h>
8#include <QtCore/qdir.h>
9#include <QtQml/qqmlfile.h>
10#include <QtCore/qfileinfo.h>
11#include <QtCore/qpluginloader.h>
12#include <QtCore/qlibraryinfo.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtQml/qqmlextensioninterface.h>
15#include <QtQml/qqmlextensionplugin.h>
16#include <private/qqmlextensionplugin_p.h>
17#include <private/qqmlglobal_p.h>
18#include <private/qqmltypenamecache_p.h>
19#include <private/qqmlengine_p.h>
20#include <private/qqmltypemodule_p.h>
21#include <private/qqmltypeloaderqmldircontent_p.h>
22#include <private/qqmlpluginimporter_p.h>
23#include <QtCore/qjsonobject.h>
24#include <QtCore/qjsonarray.h>
25#include <QtQml/private/qqmltype_p_p.h>
26#include <QtQml/private/qqmlimportresolver_p.h>
27
28#ifdef Q_OS_MACOS
29#include "private/qcore_mac_p.h"
30#endif
31
32#include <algorithm>
33#include <functional>
34
35using namespace Qt::Literals::StringLiterals;
36
38
39DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
40
42{
43 Q_DISABLE_COPY_MOVE(QmlImportCategoryHolder);
44public:
45
46 QmlImportCategoryHolder() : m_category("qt.qml.import")
47 {
48 // We have to explicitly setEnabled() here because for categories that start with
49 // "qt." it won't accept QtDebugMsg as argument. Debug messages are off by default
50 // for all Qt logging categories.
51 if (qmlImportTrace())
52 m_category.setEnabled(QtDebugMsg, true);
53 }
54
56
57 const QLoggingCategory &category() const { return m_category; }
58
59private:
60 QLoggingCategory m_category;
61};
62
64{
65 static const QmlImportCategoryHolder holder;
66 return holder.category();
67}
68
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"));
80
81namespace {
82
83QTypeRevision relevantVersion(const QString &uri, QTypeRevision version)
84{
86}
87
88QQmlError moduleNotFoundError(const QString &uri, QTypeRevision version)
89{
91 if (version.hasMajorVersion()) {
92 error.setDescription(QQmlImportDatabase::tr(
93 "module \"%1\" version %2.%3 is not installed")
94 .arg(uri).arg(version.majorVersion())
95 .arg(version.hasMinorVersion()
96 ? QString::number(version.minorVersion())
97 : QLatin1String("x")));
98 } else {
99 error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed")
100 .arg(uri));
101 }
102 return error;
103}
104
105QString resolveLocalUrl(const QString &url, const QString &relative)
106{
107 if (relative.contains(Colon)) {
108 // contains a host name
109 return QUrl(url).resolved(QUrl(relative)).toString();
110 } else if (relative.isEmpty()) {
111 return url;
112 } else if (relative.at(0) == Slash || !url.contains(Slash)) {
113 return relative;
114 } else {
115 const QStringView baseRef = QStringView{url}.left(url.lastIndexOf(Slash) + 1);
116 if (relative == QLatin1String("."))
117 return baseRef.toString();
118
119 QString base = baseRef + relative;
120
121 // Remove any relative directory elements in the path
122 int length = base.size();
123 int index = 0;
124 while ((index = base.indexOf(QLatin1String("/."), index)) != -1) {
125 if ((length > (index + 2)) && (base.at(index + 2) == Dot) &&
126 (length == (index + 3) || (base.at(index + 3) == Slash))) {
127 // Either "/../" or "/..<END>"
128 int previous = base.lastIndexOf(Slash, index - 1);
129 if (previous == -1)
130 break;
131
132 int removeLength = (index - previous) + 3;
133 base.remove(previous + 1, removeLength);
134 length -= removeLength;
135 index = previous;
136 } else if ((length == (index + 2)) || (base.at(index + 2) == Slash)) {
137 // Either "/./" or "/.<END>"
138 base.remove(index, 2);
139 length -= 2;
140 } else {
141 ++index;
142 }
143 }
144
145 return base;
146 }
147}
148
149bool isPathAbsolute(const QString &path)
150{
151#if defined(Q_OS_UNIX)
152 return (path.at(0) == Slash);
153#else
155 return fi.isAbsolute();
156#endif
157}
158
159} // namespace
160
201{
202 // If the given version is invalid, return a valid but useless version to signal "It's OK".
203 return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
204}
205
209void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
210{
211 m_baseUrl = url;
212
213 if (urlString.isEmpty())
214 m_base = url.toString();
215 else
216 m_base = urlString;
217}
218
226/*
227 \internal
228
229 This method is responsible for populating data of all types visible in this
230 document's imports into the \a cache for resolution elsewhere (e.g. in JS,
231 or when loading additional types).
232
233 \note This is for C++ types only. Composite types are handled separately,
234 as they do not have a QQmlTypeModule.
235*/
237{
238 const QQmlImportNamespace &set = m_unqualifiedset;
239
240 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
241 const QQmlImportInstance *import = set.imports.at(ii);
242 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
243 if (module) {
244 cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->version));
245 }
246 }
247
248 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
249
250 const QQmlImportNamespace &set = *ns;
251
252 // positioning is important; we must create the namespace even if there is no module.
253 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
254 typeimport.m_qualifier = set.prefix;
255
256 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
257 const QQmlImportInstance *import = set.imports.at(ii);
258 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
259 if (module) {
260 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
261 typeimport.modules.append(QQmlTypeModuleVersion(module, import->version));
262 }
263 }
264 }
265}
266
267// We need to exclude the entry for the current baseUrl. This can happen for example
268// when handling qmldir files on the remote dir case and the current type is marked as
269// singleton.
270bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl)
271{
272 if (importUrl.isEmpty())
273 return false;
274
275 if (baseUrl.startsWith(importUrl))
276 {
277 if (fileName == QStringView{baseUrl}.mid(importUrl.size()))
278 return false;
279 }
280
281 return true;
282}
283
285{
286 typedef QQmlDirComponents::const_iterator ConstIterator;
287
288 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
289 const QQmlImportInstance *import = set.imports.at(ii);
290
291 const QQmlDirComponents &components = import->qmlDirComponents;
292
293 const QTypeRevision importVersion = import->version;
294 auto shouldSkipSingleton = [importVersion](QTypeRevision singletonVersion) -> bool {
295 return importVersion.hasMajorVersion() &&
296 (singletonVersion.majorVersion() > importVersion.majorVersion()
297 || (singletonVersion.majorVersion() == importVersion.majorVersion()
298 && singletonVersion.minorVersion() > importVersion.minorVersion()));
299 };
300
301 ConstIterator cend = components.constEnd();
302 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
303 if (cit->singleton && excludeBaseUrl(import->url, cit->fileName, baseUrl.toString())) {
304 if (shouldSkipSingleton(cit->version))
305 continue;
307 ref.typeName = cit->typeName;
308 ref.prefix = set.prefix;
309 ref.version = cit->version;
310 resultList.append(ref);
311 }
312 }
313
314 if (QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version)) {
315 module->walkCompositeSingletons([&resultList, &set, &shouldSkipSingleton](const QQmlType &singleton) {
316 if (shouldSkipSingleton(singleton.version()))
317 return;
319 ref.typeName = singleton.elementName();
320 ref.prefix = set.prefix;
321 ref.version = singleton.version();
322 resultList.append(ref);
323 });
324 }
325 }
326}
327
328/*
329 \internal
330
331 Returns a list of all composite singletons present in this document's
332 imports.
333
334 This information is used by QQmlTypeLoader to ensure that composite singletons
335 are marked as dependencies during type loading.
336*/
338{
340
341 const QQmlImportNamespace &set = m_unqualifiedset;
342 findCompositeSingletons(set, compositeSingletons, baseUrl());
343
344 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
345 const QQmlImportNamespace &set = *ns;
346 findCompositeSingletons(set, compositeSingletons, baseUrl());
347 }
348
349 std::stable_sort(compositeSingletons.begin(), compositeSingletons.end(),
352 if (lhs.prefix != rhs.prefix)
353 return lhs.prefix < rhs.prefix;
354
355 if (lhs.typeName != rhs.typeName)
356 return lhs.typeName < rhs.typeName;
357
358 return lhs.version.majorVersion() != rhs.version.majorVersion()
359 ? lhs.version.majorVersion() < rhs.version.majorVersion()
360 : lhs.version.minorVersion() < rhs.version.minorVersion();
361 });
362
363 return compositeSingletons;
364}
365
366/*
367 \internal
368
369 Returns a list of scripts imported by this document. This is used by
370 QQmlTypeLoader to properly handle dependencies on imported scripts.
371*/
373{
375
376 const QQmlImportNamespace &set = m_unqualifiedset;
377
378 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
379 const QQmlImportInstance *import = set.imports.at(ii);
380
381 for (const QQmlDirParser::Script &script : import->qmlDirScripts) {
383 ref.nameSpace = script.nameSpace;
384 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
385 scripts.append(ref);
386 }
387 }
388
389 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
390 const QQmlImportNamespace &set = *ns;
391
392 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
393 const QQmlImportInstance *import = set.imports.at(ii);
394
395 for (const QQmlDirParser::Script &script : import->qmlDirScripts) {
397 ref.nameSpace = script.nameSpace;
398 ref.qualifier = set.prefix;
399 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
400 scripts.append(ref);
401 }
402 }
403 }
404
405 return scripts;
406}
407
419 QTypeRevision version)
420{
421 QStringList paths = qQmlResolveImportPaths(uri, basePaths, version);
422 for (QString &path : paths)
424 return paths;
425}
426
428{
429 if (versionMode == QQmlImports::FullyVersioned) {
430 // extension with fully encoded version number (eg. MyModule.3.2)
431 return QString::asprintf(".%d.%d", version.majorVersion(), version.minorVersion());
432 } else if (versionMode == QQmlImports::PartiallyVersioned) {
433 // extension with encoded version major (eg. MyModule.3)
434 return QString::asprintf(".%d", version.majorVersion());
435 } // else extension without version number (eg. MyModule)
436 return QString();
437}
438
453 const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return,
454 QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
455 QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) const
456{
457 QQmlImportNamespace *ns = findQualifiedNamespace(type);
458 if (ns) {
459 if (ns_return)
460 *ns_return = ns;
461 return true;
462 }
463 if (type_return) {
464 if (resolveType(type, version_return, type_return, errors, registrationType,
465 typeRecursionDetected)) {
466 if (lcQmlImport().isDebugEnabled()) {
467#define RESOLVE_TYPE_DEBUG qCDebug(lcQmlImport) \
468 << "resolveType:" << qPrintable(baseUrl().toString()) << type.toString() << " => "
469
470 if (type_return && type_return->isValid()) {
471 if (type_return->isCompositeSingleton())
472 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL-SINGLETON";
473 else if (type_return->isComposite())
474 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL";
475 else if (type_return->isInlineComponentType())
476 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE(INLINECOMPONENT)";
477 else
478 RESOLVE_TYPE_DEBUG << type_return->typeName() << " TYPE";
479 }
480#undef RESOLVE_TYPE_DEBUG
481 }
482 return true;
483 }
484 }
485 return false;
486}
487
489 const QQmlTypeLoaderQmldirContent &qmldir,
490 QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
491{
492
493 const QString preferredPath = qmldir.preferredPath();
494 if (preferredPath.isEmpty()) {
495 Q_ASSERT(resolvedUrl.endsWith(Slash));
497 } else {
498 Q_ASSERT(preferredPath.endsWith(Slash));
499 if (preferredPath.startsWith(u':'))
500 url = QStringLiteral("qrc") + preferredPath;
501 else
502 url = QUrl::fromLocalFile(preferredPath).toString();
503 }
504
505 qmlDirComponents = qmldir.components();
506
507 const QQmlDirScripts &scripts = qmldir.scripts();
508 if (!scripts.isEmpty()) {
509 // Verify that we haven't imported these scripts already
511 it != nameSpace->imports.constEnd(); ++it) {
512 if ((*it != this) && ((*it)->uri == uri)) {
514 error.setDescription(
515 QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3")
516 .arg(uri, url, (*it)->url));
517 errors->prepend(error);
518 return false;
519 }
520 }
521
523 }
524
525 return true;
526}
527
529 QTypeRevision version)
530{
532
534 sit != qmldirscripts.constEnd(); ++sit) {
535 // Only include scripts that match our requested version
536 if ((!version.hasMajorVersion() || (sit->version.majorVersion() == version.majorVersion()))
538 || (sit->version.minorVersion() <= version.minorVersion()))) {
539 // Load the highest version that matches
540 QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
541 if (vit == versioned.end()
542 || (vit->version.minorVersion() < sit->version.minorVersion())) {
543 versioned.insert(sit->nameSpace, *sit);
544 }
545 }
546 }
547
548 return versioned.values();
549}
550
564 QTypeRevision *version_return, QQmlType *type_return,
565 const QString *base, bool *typeRecursionDetected,
566 QQmlType::RegistrationType registrationType,
567 QQmlImport::RecursionRestriction recursionRestriction,
568 QList<QQmlError> *errors) const
569{
571 if (t.isValid()) {
572 if (version_return)
573 *version_return = version;
574 if (type_return)
575 *type_return = t;
576 return true;
577 }
578
579 const QString typeStr = type.toString();
580 if (isInlineComponent) {
581 Q_ASSERT(type_return);
582 bool ret = uri == typeStr;
583 if (ret) {
584 Q_ASSERT(!type_return->isValid());
586 }
587 return ret;
588 }
590 if (it != end) {
591 QString componentUrl;
594 for ( ; it != end && it.key() == typeStr; ++it) {
595 const QQmlDirParser::Component &c = *it;
596 switch (registrationType) {
598 break;
600 if (!c.singleton)
601 continue;
602 break;
603 default:
604 if (c.singleton)
605 continue;
606 break;
607 }
608
609 // importing invalid version means import ALL versions
610 if (!version.hasMajorVersion() || (implicitlyImported && c.internal)
611 // allow the implicit import of internal types
612 || (c.version.majorVersion() == version.majorVersion()
613 && c.version.minorVersion() <= version.minorVersion())) {
614 // Is this better than the previous candidate?
615 if ((candidate == end)
616 || (c.version.majorVersion() > candidate->version.majorVersion())
617 || ((c.version.majorVersion() == candidate->version.majorVersion())
618 && (c.version.minorVersion() > candidate->version.minorVersion()))) {
619 if (base) {
620 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
621 if (c.internal) {
622 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
623 continue; // failed attempt to access an internal type
624 }
625
626 const bool recursion = *base == componentUrl;
627 if (typeRecursionDetected)
628 *typeRecursionDetected = recursion;
629
630 if (recursionRestriction == QQmlImport::PreventRecursion && recursion) {
631 continue; // no recursion
632 }
633 }
634
635 // This is our best candidate so far
636 candidate = it;
638 }
639 }
640 }
641
642 if (candidate != end) {
643 if (!base) // ensure we have a componentUrl
644 componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
645 QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, lookupMode,
646 nullptr, candidate->version);
647 if (version_return)
648 *version_return = candidate->version;
649 if (type_return)
650 *type_return = returnType;
651 return returnType.isValid();
652 }
653 } else if (!isLibrary) {
654 // the base path of the import if it's a local file
655 const QString localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
656 if (localDirectoryPath.isEmpty())
657 return false;
658
659 QString qmlUrl;
660 bool exists = false;
661
662 const QString urlsToTry[2] = {
663 typeStr + dotqml_string, // Type -> Type.qml
664 typeStr + dotuidotqml_string // Type -> Type.ui.qml
665 };
666 for (const QString &urlToTry : urlsToTry) {
667 exists = typeLoader->fileExists(localDirectoryPath, urlToTry);
668 if (exists) {
669#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
670 // don't let function.qml confuse the use of "new Function(...)" for example.
671 if (!QQml_isFileCaseCorrect(localDirectoryPath + urlToTry)) {
672 exists = false;
673 if (errors) {
674 QQmlError caseError;
675 caseError.setDescription(QLatin1String("File name case mismatch"));
676 errors->append(caseError);
677 }
678 break;
679 }
680#else
681 Q_UNUSED(errors);
682#endif
683 qmlUrl = url + urlToTry;
684 break;
685 }
686 }
687
688 if (exists) {
689 const bool recursion = base && *base == qmlUrl;
690 if (typeRecursionDetected)
691 *typeRecursionDetected = recursion;
692 if (recursionRestriction == QQmlImport::AllowRecursion || !recursion) {
694 qmlUrl, type, registrationType == QQmlType::CompositeSingletonType
697 errors);
698 if (type_return)
699 *type_return = returnType;
700 return returnType.isValid();
701 }
702 }
703 }
704
705 return false;
706}
707
709 const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return,
710 QList<QQmlError> *errors, QQmlType::RegistrationType registrationType,
711 bool *typeRecursionDetected) const
712{
713 const QVector<QHashedStringRef> splitName = type.split(Dot);
714 auto resolveTypeInNamespace = [&](
715 QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
716 QList<QQmlError> *errors) -> bool {
717 if (nameSpace->resolveType(
718 m_typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
719 registrationType, typeRecursionDetected))
720 return true;
721 if (nameSpace->imports.size() == 1
722 && !nameSpace->imports.at(0)->isLibrary
723 && type_return
724 && nameSpace != &m_unqualifiedset) {
725 // qualified, and only 1 url
726 *type_return = QQmlMetaType::typeForUrl(
727 resolveLocalUrl(nameSpace->imports.at(0)->url,
728 unqualifiedtype.toString() + QLatin1String(".qml")),
730 return type_return->isValid();
731 }
732 return false;
733 };
734 switch (splitName.size()) {
735 case 1: {
736 // must be a simple type
737 return resolveTypeInNamespace(type, &m_unqualifiedset, errors);
738 }
739 case 2: {
740 // either namespace + simple type OR simple type + inline component
741 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
742 if (s) {
743 // namespace + simple type
744 return resolveTypeInNamespace(splitName.at(1), s, errors);
745 } else {
746 if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset, nullptr)) {
747 // either simple type + inline component
749 type_return->sourceUrl(), splitName.at(1).toString());
750 return true;
751 } else {
752 // or a failure
753 if (errors) {
755 error.setDescription(QQmlImportDatabase::tr("- %1 is neither a type nor a namespace").arg(splitName.at(0).toString()));
756 errors->prepend(error);
757 }
758 return false;
759 }
760 }
761 }
762 case 3: {
763 // must be namespace + simple type + inline component
764 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
766 if (!s) {
767 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(splitName.at(0).toString()));
768 } else {
769 if (resolveTypeInNamespace(splitName.at(1), s, nullptr)) {
771 type_return->sourceUrl(), splitName.at(2).toString());
772 return true;
773 } else {
774 error.setDescription(QQmlImportDatabase::tr("- %1 is not a type").arg(splitName.at(1).toString()));
775 }
776 }
777 if (errors) {
778 errors->prepend(error);
779 }
780 return false;
781 }
782 default: {
783 // all other numbers suggest a user error
784 if (errors) {
786 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
787 errors->prepend(error);
788 }
789 return false;
790 }
791 }
792 Q_UNREACHABLE();
793}
794
796{
797 for (QQmlImportInstance *import : imports) {
798 if (import->uri == uri)
799 return import;
800 }
801 return nullptr;
802}
803
805 QTypeRevision *version_return, QQmlType *type_return,
806 const QString *base, QList<QQmlError> *errors,
807 QQmlType::RegistrationType registrationType,
808 bool *typeRecursionDetected)
809{
810 QQmlImport::RecursionRestriction recursionRestriction =
812
813 bool localTypeRecursionDetected = false;
814 if (!typeRecursionDetected)
815 typeRecursionDetected = &localTypeRecursionDetected;
816
817 // TODO: move the sorting somewhere else and make resolveType() const.
818 if (needsSorting()) {
819 std::stable_partition(imports.begin(), imports.end(), [](QQmlImportInstance *import) {
820 return import->isInlineComponent;
821 });
822 setNeedsSorting(false);
823 }
824 for (int i=0; i<imports.size(); ++i) {
825 const QQmlImportInstance *import = imports.at(i);
826 if (import->resolveType(typeLoader, type, version_return, type_return, base,
827 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
828 if (qmlCheckTypes()) {
829 // check for type clashes
830 for (int j = i+1; j<imports.size(); ++j) {
831 const QQmlImportInstance *import2 = imports.at(j);
832 if (import2->resolveType(typeLoader, type, version_return, nullptr, base,
833 nullptr, registrationType)) {
834 if (errors) {
835 QString u1 = import->url;
836 QString u2 = import2->url;
837 if (base) {
839 int dot = b.lastIndexOf(Dot);
840 if (dot >= 0) {
841 b = b.left(dot+1);
842 QStringView l = b.left(dot);
843 if (u1.startsWith(b))
844 u1 = u1.mid(b.size());
845 else if (u1 == l)
846 u1 = QQmlImportDatabase::tr("local directory");
847 if (u2.startsWith(b))
848 u2 = u2.mid(b.size());
849 else if (u2 == l)
850 u2 = QQmlImportDatabase::tr("local directory");
851 }
852 }
853
855 if (u1 != u2) {
856 error.setDescription(
857 QQmlImportDatabase::tr(
858 "is ambiguous. Found in %1 and in %2")
859 .arg(u1, u2));
860 } else {
861 error.setDescription(
862 QQmlImportDatabase::tr(
863 "is ambiguous. Found in %1 in version "
864 "%2.%3 and %4.%5")
865 .arg(u1)
866 .arg(import->version.majorVersion())
867 .arg(import->version.minorVersion())
868 .arg(import2->version.majorVersion())
869 .arg(import2->version.minorVersion()));
870 }
871 errors->prepend(error);
872 }
873 return false;
874 }
875 }
876 }
877 return true;
878 }
879 }
880 if (errors) {
882 if (*typeRecursionDetected)
883 error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
884 else
885 error.setDescription(QQmlImportDatabase::tr("is not a type"));
886 errors->prepend(error);
887 }
888 return false;
889}
890
891QQmlImportNamespace *QQmlImports::findQualifiedNamespace(const QHashedStringRef &prefix) const
892{
893 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
894 if (prefix == ns->prefix)
895 return ns;
896 }
897 return nullptr;
898}
899
900/*
901Import an extension defined by a qmldir file.
902*/
903QTypeRevision QQmlImports::importExtension(
904 const QString &uri, QTypeRevision version, QQmlImportDatabase *database,
905 const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
906{
907 Q_ASSERT(qmldir->hasContent());
908
910 << "importExtension:" << qPrintable(m_base) << "loaded" << qmldir->qmldirLocation();
911
912 if (designerSupportRequired && !qmldir->designerSupported()) {
913 if (errors) {
915 error.setDescription(
916 QQmlImportDatabase::tr("module does not support the designer \"%1\"")
917 .arg(qmldir->typeNamespace()));
918 error.setUrl(QUrl::fromLocalFile(qmldir->qmldirLocation()));
919 errors->prepend(error);
920 }
921 return QTypeRevision();
922 }
923
924 if (qmldir->plugins().isEmpty())
925 return validVersion(version);
926
927 QQmlPluginImporter importer(uri, version, database, qmldir, m_typeLoader, errors);
928 return importer.importPlugins();
929}
930
931bool QQmlImports::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
933{
934 Q_ASSERT(errors);
935 Q_ASSERT(qmldir);
936
937 *qmldir = m_typeLoader->qmldirContent(qmldirIdentifier);
938 if ((*qmldir).hasContent()) {
939 // Ensure that parsing was successful
940 if ((*qmldir).hasError()) {
941 QUrl url = QUrl::fromLocalFile(qmldirIdentifier);
942 const QList<QQmlError> qmldirErrors = (*qmldir).errors(uri);
943 for (int i = 0; i < qmldirErrors.size(); ++i) {
944 QQmlError error = qmldirErrors.at(i);
945 error.setUrl(url);
946 errors->append(error);
947 }
948 return false;
949 }
950 }
951
952 return true;
953}
954
955QString QQmlImports::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
956{
957 QString dir = dir_arg;
959 dir.chop(1);
960
961 QStringList paths = database->fileImportPath;
962 if (!paths.isEmpty())
963 std::sort(paths.begin(), paths.end(), std::greater<QString>()); // Ensure subdirs preceed their parents.
964
965 QString stableRelativePath = dir;
966 for (const QString &path : std::as_const(paths)) {
967 if (dir.startsWith(path)) {
968 stableRelativePath = dir.mid(path.size()+1);
969 break;
970 }
971 }
972
973 stableRelativePath.replace(Backslash, Slash);
974
975 // remove optional versioning in dot notation from uri
976 int versionDot = stableRelativePath.lastIndexOf(Dot);
977 if (versionDot >= 0) {
978 int nextSlash = stableRelativePath.indexOf(Slash, versionDot);
979 if (nextSlash >= 0)
980 stableRelativePath.remove(versionDot, nextSlash - versionDot);
981 else
982 stableRelativePath = stableRelativePath.left(versionDot);
983 }
984
985 stableRelativePath.replace(Slash, Dot);
986
987 return stableRelativePath;
988}
989
990/* removes all file selector occurrences in path
991 firstPlus is the position of the initial '+' in the path
992 which we always have as we check for '+' to decide whether
993 we need to do some work at all
994*/
995static QString pathWithoutFileSelectors(QString path, // we want a copy of path
996 qsizetype firstPlus)
997{
998 do {
999 Q_ASSERT(path.at(firstPlus) == u'+');
1000 const auto eos = path.size();
1001 qsizetype terminatingSlashPos = firstPlus + 1;
1002 while (terminatingSlashPos != eos && path.at(terminatingSlashPos) != u'/')
1003 ++terminatingSlashPos;
1004 path.remove(firstPlus, terminatingSlashPos - firstPlus + 1);
1005 firstPlus = path.indexOf(u'+', firstPlus);
1006 } while (firstPlus != -1);
1007 return path;
1008}
1009
1027QTypeRevision QQmlImports::matchingQmldirVersion(
1028 const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, QTypeRevision version,
1029 QList<QQmlError> *errors)
1030{
1031 int bestMajorVersion = -1;
1032 quint8 lowestMinorVersion = std::numeric_limits<quint8>::max();
1033 quint8 highestMinorVersion = 0;
1034
1035 auto addVersion = [&](QTypeRevision newVersion) {
1036 if (!newVersion.hasMajorVersion())
1037 return;
1038 if (!version.hasMajorVersion() || version.majorVersion() == newVersion.majorVersion()) {
1039 if (newVersion.majorVersion() > bestMajorVersion) {
1040 bestMajorVersion = newVersion.majorVersion();
1041 if (newVersion.hasMinorVersion()) {
1042 lowestMinorVersion = newVersion.minorVersion();
1043 highestMinorVersion = newVersion.minorVersion();
1044 }
1045 } else if (newVersion.majorVersion() == bestMajorVersion
1046 && newVersion.hasMinorVersion()) {
1047 lowestMinorVersion = qMin(lowestMinorVersion, newVersion.minorVersion());
1048 highestMinorVersion = qMax(highestMinorVersion, newVersion.minorVersion());
1049 }
1050 }
1051 };
1052
1053 typedef QQmlDirComponents::const_iterator ConstIterator;
1054 const QQmlDirComponents &components = qmldir.components();
1055
1056 QMultiHash<QString, ConstIterator> baseFileName2ConflictingComponents;
1057
1058 ConstIterator cend = components.constEnd();
1059 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1060 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1061 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1062 // ugly heuristic to deal with file selectors
1063 const auto comp2PotentialFileSelectorPos = cit2->fileName.indexOf(u'+');
1064 const bool comp2MightHaveFileSelector = comp2PotentialFileSelectorPos != -1;
1065 /* If we detect conflicting paths, we check if they agree when we remove anything looking like a
1066 file selector.
1067 We need to create copies of the filenames, otherwise QString::replace would modify the
1068 existing file-names
1069 */
1070 QString compFileName1 = cit->fileName;
1071 QString compFileName2 = cit2->fileName;
1072 if (auto fileSelectorPos1 = compFileName1.indexOf(u'+'); fileSelectorPos1 != -1) {
1073 // existing entry was file selector entry, fix it up
1074 // it could also be the case that _both_ are using file selectors
1075 QString baseName = comp2MightHaveFileSelector ? pathWithoutFileSelectors(compFileName2,
1076 comp2PotentialFileSelectorPos)
1077 : compFileName2;
1078 if (pathWithoutFileSelectors(compFileName1, fileSelectorPos1) == baseName) {
1079 baseFileName2ConflictingComponents.insert(baseName, cit);
1080 baseFileName2ConflictingComponents.insert(baseName, cit2);
1081 continue;
1082 }
1083 // fall through to error case
1084 } else if (comp2MightHaveFileSelector) {
1085 // new entry contains file selector (and we now that cit did not)
1086 if (pathWithoutFileSelectors(compFileName2, comp2PotentialFileSelectorPos) == compFileName1) {
1087 baseFileName2ConflictingComponents.insert(compFileName1, cit2);
1088 continue;
1089 }
1090 // fall through to error case
1091 }
1092 // This entry clashes with a predecessor
1094 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1095 .arg(cit->typeName).arg(cit->version.majorVersion())
1096 .arg(cit->version.minorVersion()).arg(uri));
1097 errors->prepend(error);
1098 return QTypeRevision();
1099 }
1100 }
1101
1102 addVersion(cit->version);
1103 }
1104
1105 // ensure that all components point to the actual base URL, and let the file selectors resolve them correctly during URL resolution
1106 for (auto keyIt = baseFileName2ConflictingComponents.keyBegin(); keyIt != baseFileName2ConflictingComponents.keyEnd(); ++keyIt) {
1107 const QString& baseFileName = *keyIt;
1108 const auto conflictingComponents = baseFileName2ConflictingComponents.values(baseFileName);
1109 for (ConstIterator component: conflictingComponents)
1110 component->fileName = baseFileName;
1111 }
1112
1113 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1114 const QQmlDirScripts &scripts = qmldir.scripts();
1115
1116 SConstIterator send = scripts.constEnd();
1117 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1118 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1119 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1120 // This entry clashes with a predecessor
1122 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1123 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1124 .arg(sit->version.minorVersion()).arg(uri));
1125 errors->prepend(error);
1126 return QTypeRevision();
1127 }
1128 }
1129
1130 addVersion(sit->version);
1131 }
1132
1133 // Failure to find a match is only an error if we were asking for a specific version ...
1134 if (version.hasMajorVersion()
1135 && (bestMajorVersion < 0
1136 || (version.hasMinorVersion()
1137 && (lowestMinorVersion > version.minorVersion()
1138 || highestMinorVersion < version.minorVersion())))) {
1139 errors->prepend(moduleNotFoundError(uri, version));
1140 return QTypeRevision();
1141 }
1142
1143 // ... otherwise, anything is valid.
1144 if (bestMajorVersion < 0)
1145 return validVersion();
1146
1148 bestMajorVersion,
1149 (version.hasMajorVersion() && version.hasMinorVersion())
1150 ? version.minorVersion()
1151 : highestMinorVersion);
1152}
1153
1154QQmlImportNamespace *QQmlImports::importNamespace(const QString &prefix)
1155{
1156 QQmlImportNamespace *nameSpace = nullptr;
1157
1158 if (prefix.isEmpty()) {
1159 nameSpace = &m_unqualifiedset;
1160 } else {
1161 nameSpace = findQualifiedNamespace(prefix);
1162
1163 if (!nameSpace) {
1164 nameSpace = new QQmlImportNamespace;
1165 nameSpace->prefix = prefix;
1166 m_qualifiedSets.append(nameSpace);
1167 }
1168 }
1169
1170 return nameSpace;
1171}
1172
1173QQmlImportInstance *QQmlImports::addImportToNamespace(
1174 QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, QTypeRevision version,
1176{
1177 Q_ASSERT(nameSpace);
1178 Q_ASSERT(errors);
1179 Q_UNUSED(errors);
1180 Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
1181
1183 import->uri = uri;
1184 import->url = url;
1185 import->version = version;
1186 import->isLibrary = (type == QV4::CompiledData::Import::ImportLibrary);
1187 import->precedence = precedence;
1188 import->implicitlyImported = precedence >= QQmlImportInstance::Implicit;
1189
1190 for (auto it = nameSpace->imports.cbegin(), end = nameSpace->imports.cend();
1191 it != end; ++it) {
1192 if ((*it)->precedence < precedence)
1193 continue;
1194
1195 nameSpace->imports.insert(it, import);
1196 return import;
1197 }
1198 nameSpace->imports.append(import);
1199 return import;
1200}
1201
1203 QQmlImportDatabase *database, const QString &uri, const QString &prefix,
1204 QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl,
1205 ImportFlags flags, quint16 precedence, QList<QQmlError> *errors)
1206{
1207 Q_ASSERT(database);
1208 Q_ASSERT(errors);
1209
1211 << "addLibraryImport:" << qPrintable(baseUrl().toString())
1212 << uri << "version '" << version << "'" << "as" << prefix;
1213
1214 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1215 Q_ASSERT(nameSpace);
1216
1217 QQmlImportInstance *inserted = addImportToNamespace(
1218 nameSpace, uri, qmldirUrl, version,
1220 precedence);
1221 Q_ASSERT(inserted);
1222
1225
1226 if (!qmldirIdentifier.isEmpty()) {
1227 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1228 return QTypeRevision();
1229
1230 if (qmldir.hasContent()) {
1231 version = importExtension(uri, version, database, &qmldir, errors);
1232 if (!version.isValid())
1233 return QTypeRevision();
1234
1235 if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors))
1236 return QTypeRevision();
1237 }
1238 }
1239
1240 // Ensure that we are actually providing something
1241 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1242 if (matchingVersion.isValid())
1243 return matchingVersion;
1244
1245 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1246 if (qmldir.plugins().isEmpty()) {
1247 if (!qmldir.imports().isEmpty())
1248 return validVersion(); // This is a pure redirection
1249 if (qmldir.hasTypeInfo())
1250 return validVersion(); // A pure C++ module without plugin
1251 }
1252 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1253 return QTypeRevision();
1254 } else if (qmldir.hasContent()) {
1255 // Verify that the qmldir content is valid for this version
1256 version = matchingQmldirVersion(qmldir, uri, version, errors);
1257 if (!version.isValid())
1258 return QTypeRevision();
1259 }
1260 }
1261
1262 return validVersion(version);
1263}
1264
1287 QQmlImportDatabase *database, const QString &uri, const QString &prefix,
1288 QTypeRevision version, ImportFlags flags, quint16 precedence,
1289 QString *localQmldir, QList<QQmlError> *errors)
1290{
1291 Q_ASSERT(database);
1292 Q_ASSERT(errors);
1293
1295 << "addFileImport:" << qPrintable(baseUrl().toString())
1296 << uri << version << "as" << prefix;
1297
1298 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1300 const QString fix = uri.startsWith(Slash) ? QLatin1String("file:") + uri
1301 : QLatin1String("qrc") + uri;
1302 error.setDescription(QQmlImportDatabase::tr(
1303 "\"%1\" is not a valid import URL. "
1304 "You can pass relative paths or URLs with schema, but not "
1305 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1306 errors->prepend(error);
1307 return QTypeRevision();
1308 }
1309
1310 Q_ASSERT(errors);
1311
1312 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1313 Q_ASSERT(nameSpace);
1314
1315 // The uri for this import. For library imports this is the same as uri
1316 // specified by the user, but it may be different in the case of file imports.
1317 QString importUri = uri;
1318 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1320 : Slash_qmldir));
1321 qmldirUrl = m_typeLoader->engine()->interceptUrl(
1323 QString qmldirIdentifier;
1324
1325 if (QQmlFile::isLocalFile(qmldirUrl)) {
1326
1327 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1328 Q_ASSERT(!localFileOrQrc.isEmpty());
1329
1330 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1331 if (!m_typeLoader->directoryExists(dir)) {
1332 if (precedence < QQmlImportInstance::Implicit) {
1334 error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri));
1335 error.setUrl(QUrl(qmldirUrl));
1336 errors->prepend(error);
1337 }
1338 return QTypeRevision();
1339 }
1340
1341 // Transforms the (possible relative) uri into our best guess relative to the
1342 // import paths.
1343 importUri = resolvedUri(dir, database);
1344 if (importUri.endsWith(Slash))
1345 importUri.chop(1);
1346
1347 if (!m_typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
1348 qmldirIdentifier = localFileOrQrc;
1349 if (localQmldir)
1350 *localQmldir = qmldirIdentifier;
1351 }
1352
1353 } else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1354
1355 if (precedence < QQmlImportInstance::Implicit) {
1357 error.setDescription(QQmlImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(importUri));
1358 error.setUrl(QUrl(qmldirUrl));
1359 errors->prepend(error);
1360 }
1361
1362 return QTypeRevision();
1363
1364 }
1365
1366 // The url for the path containing files for this import
1367 QString url = resolveLocalUrl(m_base, uri);
1368 if (url.isEmpty()) {
1370 error.setDescription(
1371 QQmlImportDatabase::tr("Cannot resolve URL for import \"%1\"").arg(uri));
1372 error.setUrl(m_baseUrl);
1373 errors->prepend(error);
1374 return QTypeRevision();
1375 }
1376
1377 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1378 url += Slash;
1379
1380 // ### For enum support, we are now adding the implicit import always (and earlier). Bail early
1381 // if the implicit import has already been explicitly added, otherwise we can run into issues
1382 // with duplicate imports. However remember that we attempted to add this as implicit import, to
1383 // allow for the loading of internal types.
1384 if (precedence >= QQmlImportInstance::Implicit) {
1386 it != nameSpace->imports.constEnd(); ++it) {
1387 if ((*it)->url == url) {
1388 (*it)->implicitlyImported = true;
1389 return validVersion(version);
1390 }
1391 }
1392 }
1393
1394 if (!(flags & QQmlImports::ImportIncomplete) && !qmldirIdentifier.isEmpty()) {
1396 if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors))
1397 return QTypeRevision();
1398
1399 if (qmldir.hasContent()) {
1400 // Prefer the qmldir URI. Unless it doesn't exist.
1401 const QString qmldirUri = qmldir.typeNamespace();
1402 if (!qmldirUri.isEmpty())
1403 importUri = qmldirUri;
1404
1405 QQmlImportInstance *inserted = addImportToNamespace(
1406 nameSpace, importUri, url, version, QV4::CompiledData::Import::ImportFile,
1407 errors, precedence);
1408 Q_ASSERT(inserted);
1409
1410 version = importExtension(importUri, version, database, &qmldir, errors);
1411 if (!version.isValid())
1412 return QTypeRevision();
1413
1414 if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
1415 return QTypeRevision();
1416
1417 return validVersion(version);
1418 }
1419 }
1420
1421 QQmlImportInstance *inserted = addImportToNamespace(
1422 nameSpace, importUri, url, version, QV4::CompiledData::Import::ImportFile,
1423 errors, precedence);
1424 Q_ASSERT(inserted);
1425 return validVersion(version);
1426}
1427
1429 QQmlImportDatabase *database, const QString &uri, const QString &prefix,
1430 const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors)
1431{
1432 Q_ASSERT(database);
1433 Q_ASSERT(errors);
1434
1436 << "updateQmldirContent:" << qPrintable(baseUrl().toString())
1437 << uri << "to" << qmldirUrl << "as" << prefix;
1438
1439 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1440 Q_ASSERT(nameSpace);
1441
1442 if (QQmlImportInstance *import = nameSpace->findImport(uri)) {
1444 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1445 return QTypeRevision();
1446
1447 if (qmldir.hasContent()) {
1448 QTypeRevision version = importExtension(
1449 uri, import->version, database, &qmldir, errors);
1450 if (!version.isValid())
1451 return QTypeRevision();
1452
1453 if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) {
1454 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1455 // The implicit import qmldir can be empty, and plugins have no extra versions
1456 if (uri != QLatin1String(".") && !QQmlMetaType::matchingModuleVersion(uri, version).isValid()) {
1457 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1458 return QTypeRevision();
1459 }
1460 } else {
1461 // Verify that the qmldir content is valid for this version
1462 version = matchingQmldirVersion(qmldir, uri, version, errors);
1463 if (!version.isValid())
1464 return QTypeRevision();
1465 }
1466 return validVersion(version);
1467 }
1468 }
1469 }
1470
1471 if (errors->isEmpty()) {
1473 error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri));
1474 errors->prepend(error);
1475 }
1476
1477 return QTypeRevision();
1478}
1479
1494bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl)
1495{
1496 importInstance->url = importUrl.toString();
1497 importInstance->uri = name;
1498 importInstance->isInlineComponent = true;
1499 importInstance->version = QTypeRevision::zero();
1500 m_unqualifiedset.imports.push_back(importInstance);
1501 m_unqualifiedset.setNeedsSorting(true);
1502 return true;
1503}
1504
1506{
1507 QUrl url(QLatin1String(file.at(0) == Colon ? "qrc" : "") + file);
1508
1509 // We don't support single character schemes as those conflict with windows drive letters.
1510 if (url.scheme().size() < 2)
1511 return QUrl::fromLocalFile(file);
1512 return url;
1513}
1514
1516{
1518}
1519
1520static QStringList parseEnvPath(const QString &envImportPath)
1521{
1522 if (QDir::listSeparator() == u':') {
1523 // Double colons are interpreted as separator + resource path.
1524 QStringList paths = envImportPath.split(u':');
1525 bool wasEmpty = false;
1526 for (auto it = paths.begin(); it != paths.end();) {
1527 if (it->isEmpty()) {
1528 wasEmpty = true;
1529 it = paths.erase(it);
1530 } else {
1531 if (wasEmpty) {
1532 it->prepend(u':');
1533 wasEmpty = false;
1534 }
1535 ++it;
1536 }
1537 }
1538 return paths;
1539 } else {
1540 return envImportPath.split(QDir::listSeparator(), Qt::SkipEmptyParts);
1541 }
1542}
1543
1550: engine(e)
1551{
1552 filePluginPath << QLatin1String(".");
1553 // Search order is:
1554 // 1. android or macos specific bundle paths.
1555 // 2. applicationDirPath()
1556 // 3. qrc:/qt-project.org/imports
1557 // 4. qrc:/qt/qml
1558 // 5. $QML2_IMPORT_PATH
1559 // 6. $QML_IMPORT_PATH
1560 // 7. QLibraryInfo::QmlImportsPath
1561
1563 addImportPath(installImportsPath);
1564
1565 auto addEnvImportPath = [this](const char *var) {
1568 for (int ii = paths.size() - 1; ii >= 0; --ii)
1569 addImportPath(paths.at(ii));
1570 }
1571 };
1572
1573 // env import paths
1574 addEnvImportPath("QML_IMPORT_PATH");
1575 addEnvImportPath("QML2_IMPORT_PATH");
1576
1577 addImportPath(QStringLiteral("qrc:/qt/qml"));
1578 addImportPath(QStringLiteral("qrc:/qt-project.org/imports"));
1580
1581 auto addEnvPluginPath = [this](const char *var) {
1584 for (int ii = paths.size() - 1; ii >= 0; --ii)
1585 addPluginPath(paths.at(ii));
1586 }
1587 };
1588
1589 addEnvPluginPath("QML_PLUGIN_PATH");
1590#if defined(Q_OS_ANDROID)
1591 addImportPath(QStringLiteral("qrc:/android_rcc_bundle/qml"));
1592 addEnvPluginPath("QT_BUNDLED_LIBS_PATH");
1593#elif defined(Q_OS_MACOS)
1594 // Add the main bundle's Resources/qml directory as an import path, so that QML modules are
1595 // found successfully when running the app from its build dir.
1596 // This is where macdeployqt and our CMake deployment logic puts Qt and user qmldir files.
1597 if (CFBundleRef bundleRef = CFBundleGetMainBundle()) {
1598 if (QCFType<CFURLRef> urlRef = CFBundleCopyResourceURL(
1599 bundleRef,
1600 QCFString(QLatin1String("qml")), 0, 0)) {
1601 if (QCFType<CFURLRef> absoluteUrlRef = CFURLCopyAbsoluteURL(urlRef)) {
1602 if (QCFString path = CFURLCopyFileSystemPath(absoluteUrlRef, kCFURLPOSIXPathStyle)) {
1603 if (QFile::exists(path)) {
1605 }
1606 }
1607 }
1608 }
1609 }
1610#endif // Q_OS_DARWIN
1611}
1612
1617{
1618 qCDebug(lcQmlImport) << "setPluginPathList:" << paths;
1619 filePluginPath = paths;
1620}
1621
1626{
1627 qCDebug(lcQmlImport) << "addPluginPath:" << path;
1628
1629 QUrl url = QUrl(path);
1630 if (url.isRelative() || url.scheme() == QLatin1String("file")
1631 || (url.scheme().size() == 1 && QFile::exists(path)) ) { // windows path
1632 QDir dir = QDir(path);
1633 filePluginPath.prepend(dir.canonicalPath());
1634 } else {
1635 filePluginPath.prepend(path);
1636 }
1637}
1638
1639QString QQmlImportDatabase::absoluteFilePath(const QString &path) const
1640{
1642}
1643
1648{
1649 qCDebug(lcQmlImport) << "addImportPath:" << path;
1650
1651 if (path.isEmpty())
1652 return;
1653
1654 QUrl url = QUrl(path);
1655 QString cPath;
1656
1657 if (url.scheme() == QLatin1String("file")) {
1659 } else if (path.startsWith(QLatin1Char(':'))) {
1660 // qrc directory, e.g. :/foo
1661 // need to convert to a qrc url, e.g. qrc:/foo
1662 cPath = QLatin1String("qrc") + path;
1663 cPath.replace(Backslash, Slash);
1664 } else if (url.isRelative() ||
1665 (url.scheme().size() == 1 && QFile::exists(path)) ) { // windows path
1666 QDir dir = QDir(path);
1667 cPath = dir.canonicalPath();
1668 } else {
1669 cPath = path;
1670 cPath.replace(Backslash, Slash);
1671 }
1672
1673 if (!cPath.isEmpty()) {
1674 if (fileImportPath.contains(cPath))
1675 fileImportPath.move(fileImportPath.indexOf(cPath), 0);
1676 else
1677 fileImportPath.prepend(cPath);
1678 }
1679}
1680
1685{
1686 if (type == LocalOrRemote)
1687 return fileImportPath;
1688
1690 for (const QString &path : fileImportPath) {
1691 bool localPath = isPathAbsolute(path) || QQmlFile::isLocalFile(path);
1692 if (localPath == (type == Local))
1693 list.append(path);
1694 }
1695
1696 return list;
1697}
1698
1703{
1704 qCDebug(lcQmlImport) << "setImportPathList:" << paths;
1705
1706 fileImportPath.clear();
1707 for (auto it = paths.crbegin(); it != paths.crend(); ++it)
1708 addImportPath(*it);
1709
1710 // Our existing cached paths may have been invalidated
1711 clearDirCache();
1712}
1713
1718 QTypeRevision version, QList<QQmlError> *errors)
1719{
1720 if (!version.hasMajorVersion()) {
1721 version = QQmlMetaType::latestModuleVersion(uri);
1722 if (!version.isValid())
1723 errors->prepend(moduleNotFoundError(uri, version));
1724 }
1725 if (version.hasMajorVersion() && !typeNamespace.isEmpty()
1726 && !QQmlMetaType::protectModule(uri, version, true)) {
1727 // Not being able to protect the module means there are not types registered for it,
1728 // means the plugin we loaded didn't provide any, means we didn't find the module.
1729 // We output the generic error message as depending on the load order of imports we may
1730 // hit this path or another one that only figures "plugin is already loaded but module
1731 // unavailable" and doesn't try to protect it anymore.
1732 errors->prepend(moduleNotFoundError(uri, version));
1733 return QTypeRevision();
1734 }
1735
1736 return version;
1737}
1738
1740{
1741 return QQmlPluginImporter::removePlugin(pluginId);
1742}
1743
1745{
1747}
1748
1749void QQmlImportDatabase::clearDirCache()
1750{
1752 while (itr != qmldirCache.constEnd()) {
1753 QmldirCache *cache = *itr;
1754 do {
1755 QmldirCache *nextCache = cache->next;
1756 delete cache;
1757 cache = nextCache;
1758 } while (cache);
1759
1760 ++itr;
1761 }
1762 qmldirCache.clear();
1763}
1764
static QString applicationDirPath()
Returns the directory that contains the application executable.
\inmodule QtCore
Definition qdir.h:19
static constexpr QChar listSeparator() noexcept
Definition qdir.h:197
static N * next(N *v)
N * first() const
void append(N *)
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
bool isAbsolute() const
Returns true if the file path is absolute, otherwise returns false (i.e.
Definition qfileinfo.h:116
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
QString toString() const
QString toString() const
Definition qstring.h:1001
static QString path(LibraryPath p)
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:471
void push_back(parameter_type t)
Definition qlist.h:672
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
const_iterator constBegin() const noexcept
Definition qlist.h:615
void prepend(rvalue_ref t)
Definition qlist.h:456
iterator begin()
Definition qlist.h:608
const_iterator cend() const noexcept
Definition qlist.h:614
void append(parameter_type t)
Definition qlist.h:441
const_iterator constEnd() const noexcept
Definition qlist.h:616
const_iterator cbegin() const noexcept
Definition qlist.h:613
\inmodule QtCore
Definition qmap.h:186
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
QList< T > values() const
Definition qmap.h:396
iterator find(const Key &key)
Definition qmap.h:640
iterator end()
Definition qmap.h:601
iterator find(const Key &key)
Definition qhash.h:1904
key_iterator keyBegin() const noexcept
Definition qhash.h:1834
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1676
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1930
bool isEmpty() const noexcept
Definition qhash.h:1492
key_iterator keyEnd() const noexcept
Definition qhash.h:1835
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1830
QQmlTypeLoader typeLoader
static QQmlEnginePrivate * get(QQmlEngine *e)
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
QUrl interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataType type) const
Run the current URL interceptors on the given url of the given type and return the result.
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:18
void setDescription(const QString &)
Sets the error description.
static bool isLocalFile(const QString &url)
Returns true if url is a local file that can be opened with QFile.
Definition qqmlfile.cpp:549
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to QFile.
Definition qqmlfile.cpp:643
The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
void addPluginPath(const QString &path)
QStringList dynamicPlugins() const
QStringList importPathList(PathType type=LocalOrRemote) const
bool removeDynamicPlugin(const QString &pluginId)
void setPluginPathList(const QStringList &paths)
void addImportPath(const QString &dir)
static QTypeRevision lockModule(const QString &uri, const QString &typeNamespace, QTypeRevision version, QList< QQmlError > *errors)
QQmlImportDatabase(QQmlEngine *)
void setImportPathList(const QStringList &paths)
bool needsSorting() const
QQmlImportInstance * findImport(const QString &uri) const
QHashedString prefix
bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return, const QString *base=nullptr, QList< QQmlError > *errors=nullptr, QQmlType::RegistrationType registrationType=QQmlType::AnyRegistrationType, bool *typeRecursionDeteced=nullptr)
QList< QQmlImportInstance * > imports
void setNeedsSorting(bool needsSorting)
void populateCache(QQmlTypeNameCache *cache) const
bool resolveType(const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList< QQmlError > *errors=nullptr, QQmlType::RegistrationType registrationType=QQmlType::AnyRegistrationType, bool *typeRecursionDetected=nullptr) const
QList< CompositeSingletonReference > resolvedCompositeSingletons() const
static QString versionString(QTypeRevision version, ImportVersion importVersion)
QList< ScriptReference > resolvedScripts() const
bool addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl)
void setBaseUrl(const QUrl &url, const QString &urlString=QString())
Sets the base URL to be used for all relative file imports added.
QUrl baseUrl() const
static QUrl urlFromLocalFileOrQrcOrUrl(const QString &)
static void setDesignerSupportRequired(bool b)
QTypeRevision addLibraryImport(QQmlImportDatabase *importDb, const QString &uri, const QString &prefix, QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl, ImportFlags flags, quint16 precedence, QList< QQmlError > *errors)
QTypeRevision updateQmldirContent(QQmlImportDatabase *importDb, const QString &uri, const QString &prefix, const QString &qmldirIdentifier, const QString &qmldirUrl, QList< QQmlError > *errors)
QTypeRevision addFileImport(QQmlImportDatabase *importDb, const QString &uri, const QString &prefix, QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir, QList< QQmlError > *errors)
static QStringList completeQmldirPaths(const QString &uri, const QStringList &basePaths, QTypeRevision version)
Forms complete paths to a qmldir file, from a base URL, a module URI and version specification.
static QTypeRevision validVersion(QTypeRevision version=QTypeRevision())
static bool protectModule(const QString &uri, QTypeRevision version, bool weakProtectAllVersions=false)
static QQmlType inlineComponentTypeForUrl(const QUrl &url)
static QTypeRevision matchingModuleVersion(const QString &module, QTypeRevision version)
static QTypeRevision latestModuleVersion(const QString &uri)
static QQmlTypeModule * typeModule(const QString &uri, QTypeRevision version)
static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef &typeName, CompositeTypeLookupMode mode, QList< QQmlError > *errors, QTypeRevision version=QTypeRevision())
static QQmlType qmlType(const QString &qualifiedName, QTypeRevision version)
Returns the type (if any) of URI-qualified named qualifiedName and version specified by version_major...
static bool removePlugin(const QString &pluginId)
static QStringList plugins()
The QQmlTypeLoader class abstracts loading files and their dependencies over the network.
QQmlEngine * engine() const
Return the QQmlEngine associated with this loader.
QString absoluteFilePath(const QString &path)
Returns the absolute filename of path via a directory cache.
bool directoryExists(const QString &path)
Returns true if the path is a directory via a directory cache.
const QQmlTypeLoaderQmldirContent qmldirContent(const QString &filePath)
Return a QQmlTypeLoaderQmldirContent for absoluteFilePath.
bool fileExists(const QString &path, const QString &file)
bool isCompositeSingleton() const
Definition qqmltype.cpp:619
@ AnyRegistrationType
Definition qqmltype_p.h:158
@ CompositeSingletonType
Definition qqmltype_p.h:155
QTypeRevision version() const
Definition qqmltype.cpp:112
QByteArray typeName() const
Definition qqmltype.cpp:431
QUrl sourceUrl() const
Definition qqmltype.cpp:743
bool isComposite() const
Definition qqmltype.cpp:614
bool isValid() const
Definition qqmltype_p.h:58
bool isInlineComponentType() const
Definition qqmltype.cpp:739
QString elementName() const
Definition qqmltype.cpp:442
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
bool isEmpty() const
Definition qset.h:52
iterator erase(const_iterator i)
Definition qset.h:145
ConstIterator constEnd() const
ConstIterator constBegin() const
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
constexpr QStringView left(qsizetype n) const noexcept
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1014
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
\inmodule QtCore
static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
Produces a QTypeRevision from the given majorVersion and minorVersion, both of which need to be a val...
constexpr bool hasMinorVersion() const
Returns true if the minor version is known, otherwise false.
static constexpr QTypeRevision zero()
Produces a QTypeRevision with major and minor version {0}.
static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
Produces a QTypeRevision from the given minorVersion with an invalid major version.
constexpr bool hasMajorVersion() const
Returns true if the major version is known, otherwise false.
constexpr quint8 minorVersion() const
Returns the minor version encoded in the revision.
constexpr bool isValid() const
Returns true if the major version or the minor version is known, otherwise false.
constexpr quint8 majorVersion() const
Returns the major version encoded in the revision.
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3354
QUrl resolved(const QUrl &relative) const
Returns the result of the merge of this URL with relative.
Definition qurl.cpp:2722
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2797
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1983
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
const QLoggingCategory & category() const
~QmlImportCategoryHolder()=default
double e
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
const PluginKeyMapConstIterator cend
Combined button and popup list for selecting options.
@ SkipEmptyParts
Definition qnamespace.h:127
#define Q_UNLIKELY(x)
DBusConnection const char DBusError * error
#define qDebug
[1]
Definition qlogging.h:160
@ QtDebugMsg
Definition qlogging.h:30
#define qCDebug(category,...)
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLint GLenum GLint components
GLuint index
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum type
GLsizei const GLuint * paths
GLbitfield flags
GLint ref
GLuint name
GLfixed GLfixed u2
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLdouble s
[6]
Definition qopenglext.h:235
GLfixed u1
static qreal dot(const QPointF &a, const QPointF &b)
static qreal component(const QPointF &point, unsigned int i)
bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn)
Returns true if the case of fileName is equivalent to the file case of fileName on disk,...
#define DEFINE_BOOL_CONFIG_OPTION(name, var)
#define RESOLVE_TYPE_DEBUG
static const QLatin1Char Backslash('\\')
static const QLatin1Char Dot('.')
static const QString dotuidotqml_string(QStringLiteral(".ui.qml"))
static const QLatin1Char Colon(':')
static const QLatin1String Slash_qmldir("/qmldir")
static const QLatin1Char Slash('/')
static QString pathWithoutFileSelectors(QString path, qsizetype firstPlus)
void findCompositeSingletons(const QQmlImportNamespace &set, QList< QQmlImports::CompositeSingletonReference > &resultList, const QUrl &baseUrl)
static bool designerSupportRequired
bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl)
static const QLatin1String String_qmldir("qmldir")
static QStringList parseEnvPath(const QString &envImportPath)
static const QString dotqml_string(QStringLiteral(".qml"))
const QLoggingCategory & lcQmlImport()
const QLoggingCategory & lcQmlImport()
QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths, QTypeRevision version)
static QUrl resolvedUrl(const QUrl &url, const QQmlRefPointer< QQmlContextData > &context)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QLatin1StringView typeStr(QShaderDescription::VariableType t)
SSL_CTX int(*) void arg)
static QString canonicalPath(const QString &rootPath)
#define qPrintable(string)
Definition qstring.h:1391
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
#define Q_UNUSED(x)
unsigned short quint16
Definition qtypes.h:43
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned char quint8
Definition qtypes.h:41
static QV4::CompiledData::Lookup::Mode lookupMode(QV4::Compiler::JSUnitGenerator::LookupMode mode)
QList< int > list
[14]
QFuture< QSet< QChar > > set
[10]
QFile file
[0]
QFileInfo fi("c:/temp/foo")
[newstuff]
QUrl url("example.com")
[constructor-url-reference]
QUrl baseUrl
QString dir
[11]
char * toString(const MyType &t)
[31]
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:17
QQmlDirComponents qmlDirComponents
bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return, const QString *base=nullptr, bool *typeRecursionDetected=nullptr, QQmlType::RegistrationType=QQmlType::AnyRegistrationType, QQmlImport::RecursionRestriction recursionRestriction=QQmlImport::PreventRecursion, QList< QQmlError > *errors=nullptr) const
bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList< QQmlError > *errors)
QQmlDirScripts qmlDirScripts
QTypeRevision version
static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, QTypeRevision version)
QVector< QQmlTypeModuleVersion > modules