8#include <QtCore/qdebug.h>
9#include <QtCore/qdir.h>
10#include <QtQml/qqmlfile.h>
11#include <QtCore/qfileinfo.h>
12#include <QtCore/qpluginloader.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>
31using namespace Qt::Literals::StringLiterals;
48 m_category.setEnabled(QtDebugMsg,
true);
56 QLoggingCategory m_category;
62 return holder.category();
79QTypeRevision relevantVersion(
const QString &uri, QTypeRevision version)
81 return QQmlMetaType::latestModuleVersion(uri).isValid() ? version : QTypeRevision();
84QString resolveLocalUrl(
const QString &url,
const QString &relative)
86 if (relative.contains(Colon)) {
88 return QUrl(url).resolved(QUrl(relative)).toString();
89 }
else if (relative.isEmpty()) {
91 }
else if (relative.at(0) == Slash || !url.contains(Slash)) {
94 const QStringView baseRef = QStringView{url}.left(url.lastIndexOf(Slash) + 1);
95 if (relative == QLatin1String(
"."))
96 return baseRef.toString();
98 QString base = baseRef + relative;
101 int length = base.size();
103 while ((index = base.indexOf(QLatin1String(
"/."), index)) != -1) {
104 if ((length > (index + 2)) && (base.at(index + 2) == Dot) &&
105 (length == (index + 3) || (base.at(index + 3) == Slash))) {
107 int previous = base.lastIndexOf(Slash, index - 1);
111 int removeLength = (index - previous) + 3;
112 base.remove(previous + 1, removeLength);
113 length -= removeLength;
115 }
else if ((length == (index + 2)) || (base.at(index + 2) == Slash)) {
117 base.remove(index, 2);
131
132
133
134
135
136
137
138
139
140
141
142
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
164
165
166
167
169QQmlError QQmlImports::moduleNotFoundError(
const QString &uri, QTypeRevision version)
172 if (version.hasMajorVersion()) {
173 error.setDescription(QQmlImports::tr(
174 "module \"%1\" version %2.%3 is not installed")
175 .arg(uri).arg(version.majorVersion())
176 .arg(version.hasMinorVersion()
177 ? QString::number(version.minorVersion())
178 : QLatin1String(
"x")));
180 error.setDescription(QQmlImports::tr(
"module \"%1\" is not installed")
186QTypeRevision QQmlImports::validVersion(QTypeRevision version)
189 return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
193
194
195void QQmlImports::setBaseUrl(
const QUrl& url,
const QString &urlString)
199 if (urlString.isEmpty())
200 m_base = url.toString();
206
207
208
209
210
213
214
215
216
217
218
219
220
221
222void QQmlImports::populateCache(QQmlTypeNameCache *cache)
const
224 const QQmlImportNamespace &set = m_unqualifiedset;
226 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
227 const QQmlImportInstance *import = set.imports.at(ii);
228 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
230 cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->version));
234 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
236 const QQmlImportNamespace &set = *ns;
239 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
240 typeimport.m_qualifier = set.prefix;
242 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
243 const QQmlImportInstance *import = set.imports.at(ii);
244 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
246 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
247 typeimport.modules.append(QQmlTypeModuleVersion(module, import->version));
256bool excludeBaseUrl(
const QString &importUrl,
const QString &fileName,
const QString &baseUrl)
258 if (importUrl.isEmpty())
261 if (baseUrl.startsWith(importUrl))
263 if (fileName == QStringView{baseUrl}.mid(importUrl.size()))
272 typedef QQmlDirComponents::const_iterator ConstIterator;
274 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
277 const QQmlDirComponents &components = import->qmlDirComponents;
279 const QTypeRevision importVersion = import->version;
280 auto shouldSkipSingleton = [importVersion](QTypeRevision singletonVersion) ->
bool {
281 return importVersion.hasMajorVersion() &&
282 (singletonVersion.majorVersion() > importVersion.majorVersion()
283 || (singletonVersion.majorVersion() == importVersion.majorVersion()
284 && singletonVersion.minorVersion() > importVersion.minorVersion()));
287 ConstIterator cend = components.constEnd();
288 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
289 if (cit->singleton && excludeBaseUrl(import->url, cit->fileName, baseUrl.toString())) {
290 if (shouldSkipSingleton(cit->version))
292 QQmlImports::CompositeSingletonReference ref;
293 ref.typeName = cit->typeName;
294 ref.prefix = set.prefix;
295 ref.version = cit->version;
296 resultList.append(ref);
300 if (QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version)) {
301 module->walkCompositeSingletons([&resultList, &set, &shouldSkipSingleton](
const QQmlType &singleton) {
302 if (shouldSkipSingleton(singleton.version()))
304 QQmlImports::CompositeSingletonReference ref;
305 ref.typeName = singleton.elementName();
306 ref.prefix = set.prefix;
307 ref.version = singleton.version();
308 resultList.append(ref);
315
316
317
318
319
320
321
322
323QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons()
const
325 QList<QQmlImports::CompositeSingletonReference> compositeSingletons;
327 const QQmlImportNamespace &set = m_unqualifiedset;
328 findCompositeSingletons(set, compositeSingletons, baseUrl());
330 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
331 const QQmlImportNamespace &set = *ns;
332 findCompositeSingletons(set, compositeSingletons, baseUrl());
335 std::stable_sort(compositeSingletons.begin(), compositeSingletons.end(),
336 [](
const QQmlImports::CompositeSingletonReference &lhs,
337 const QQmlImports::CompositeSingletonReference &rhs) {
338 if (lhs.prefix != rhs.prefix)
339 return lhs.prefix < rhs.prefix;
341 if (lhs.typeName != rhs.typeName)
342 return lhs.typeName < rhs.typeName;
344 return lhs.version.majorVersion() != rhs.version.majorVersion()
345 ? lhs.version.majorVersion() < rhs.version.majorVersion()
346 : lhs.version.minorVersion() < rhs.version.minorVersion();
349 return compositeSingletons;
353
354
355
356
357
358QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts()
const
360 QList<QQmlImports::ScriptReference> scripts;
362 const QQmlImportNamespace &set = m_unqualifiedset;
364 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
365 const QQmlImportInstance *import = set.imports.at(ii);
367 for (
const QQmlDirParser::Script &script : import->qmlDirScripts) {
369 ref.nameSpace = script.nameSpace;
370 ref.fileName = QUrl(script.fileName);
371 ref.location = QUrl(import->url).resolved(ref.fileName);
376 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
377 const QQmlImportNamespace &set = *ns;
379 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
380 const QQmlImportInstance *import = set.imports.at(ii);
382 for (
const QQmlDirParser::Script &script : import->qmlDirScripts) {
384 ref.nameSpace = script.nameSpace;
385 ref.qualifier = set.prefix;
386 ref.fileName = QUrl(script.fileName);
387 ref.location = QUrl(import->url).resolved(ref.fileName);
397
398
399
400
401
402
403
404
405
406QStringList QQmlImports::completeQmldirPaths(
const QString &uri,
const QStringList &basePaths,
407 QTypeRevision version)
409 QStringList paths = qQmlResolveImportPaths(uri, basePaths, version);
410 for (QString &path : paths)
411 path += Slash_qmldir;
415QString QQmlImports::versionString(QTypeRevision version, ImportVersion versionMode)
417 if (versionMode == QQmlImports::FullyVersioned) {
419 return QString::asprintf(
".%d.%d", version.majorVersion(), version.minorVersion());
420 }
else if (versionMode == QQmlImports::PartiallyVersioned) {
422 return QString::asprintf(
".%d", version.majorVersion());
428
429
430
431
432
433
434
435
436
437
438
439
440bool QQmlImports::resolveType(
441 QQmlTypeLoader *typeLoader,
const QHashedStringRef &type, QQmlType *type_return,
442 QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
443 QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
const
445 QQmlImportNamespace *ns = findQualifiedNamespace(type);
452 if (resolveType(typeLoader, type, version_return, type_return, errors, registrationType,
453 typeRecursionDetected)) {
454 if (lcQmlImport().isDebugEnabled()) {
455#define RESOLVE_TYPE_DEBUG qCDebug(lcQmlImport)
456 << "resolveType:" << qPrintable(baseUrl().toString()) << type.toString() << " => "
458 if (type_return && type_return->isValid()) {
459 if (type_return->isCompositeSingleton()) {
461 << type_return->sourceUrl() <<
" TYPE/URL-SINGLETON";
462 }
else if (type_return->isInlineComponent()) {
464 << type_return->sourceUrl() <<
" TYPE(INLINECOMPONENT)";
465 }
else if (type_return->isComposite()) {
467 << type_return->sourceUrl() <<
" TYPE/URL";
472#undef RESOLVE_TYPE_DEBUG
480const QQmlImportInstance *QQmlImports::findImportByModuleUri(
const QString &uri,
481 QTypeRevision version)
const
483 if (
const QQmlImportInstance *import = m_unqualifiedset.findImportByModuleUri(uri, version))
486 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
487 if (
const QQmlImportInstance *import = ns->findImportByModuleUri(uri, version))
498 Q_ASSERT(resolvedUrl.endsWith(Slash));
501 qmlDirComponents = qmldir.components();
503 const QQmlDirScripts &scripts = qmldir.scripts();
504 if (!scripts.isEmpty()) {
506 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
507 it != nameSpace->imports.constEnd(); ++it) {
508 if ((*it !=
this) && ((*it)->uri == uri)) {
510 error.setDescription(
511 QQmlImports::tr(
"\"%1\" is ambiguous. Found in %2 and in %3")
512 .arg(uri, url, (*it)->url));
513 errors->prepend(error);
518 qmlDirScripts = getVersionedScripts(scripts, version);
525 QTypeRevision version)
527 QMap<QString, QQmlDirParser::Script> versioned;
529 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
530 sit != qmldirscripts.constEnd(); ++sit) {
532 if ((!version.hasMajorVersion() || (sit->version.majorVersion() == version.majorVersion()))
533 && (!version.hasMinorVersion()
534 || (sit->version.minorVersion() <= version.minorVersion()))) {
536 const auto vit = versioned.constFind(sit->nameSpace);
537 if (vit == versioned.cend()
538 || (vit->version.minorVersion() < sit->version.minorVersion())) {
539 versioned.insert(sit->nameSpace, *sit);
544 return versioned.values();
548
549
550
551
552
553
554
555
556
557
560 QTypeRevision *version_return, QQmlType *type_return,
561 const QString *base,
bool *typeRecursionDetected,
562 QQmlType::RegistrationType registrationType,
564 QList<QQmlError> *errors)
const
569 QQmlType t = uri.isEmpty() ? QQmlType() : QQmlMetaType::qmlType(type, uri, version);
572 *version_return = version;
578 const QString typeStr = type.toString();
580 Q_ASSERT(type_return);
581 bool ret = uri == typeStr;
583 Q_ASSERT(!type_return->isValid());
584 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(QUrl(url));
586 if (!type_return->isValid())
591 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
593 QString componentUrl;
594 QQmlMetaType::CompositeTypeLookupMode lookupMode = QQmlMetaType::NonSingleton;
595 QQmlDirComponents::ConstIterator candidate = end;
596 for ( ; it != end && it.key() == typeStr; ++it) {
597 const QQmlDirParser::Component &c = *it;
598 switch (registrationType) {
599 case QQmlType::AnyRegistrationType:
601 case QQmlType::CompositeSingletonType:
612 if (!version.hasMajorVersion() || (implicitlyImported && c.internal)
614 || (c.version.majorVersion() == version.majorVersion()
615 && c.version.minorVersion() <= version.minorVersion())) {
617 if ((candidate == end)
618 || (c.version.majorVersion() > candidate->version.majorVersion())
619 || ((c.version.majorVersion() == candidate->version.majorVersion())
620 && (c.version.minorVersion() > candidate->version.minorVersion()))) {
622 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
623 if (c.internal && resolveLocalUrl(*base, c.fileName) != componentUrl) {
627 const auto componentsBegin = qmlDirComponents.cbegin();
628 const auto componentsEnd = qmlDirComponents.cend();
629 if (
std::find_if(componentsBegin, componentsEnd,
630 [&](
const QQmlDirParser::Component &component) {
631 return resolveLocalUrl(url, component.fileName)
639 const bool recursion = *base == componentUrl;
640 if (typeRecursionDetected)
641 *typeRecursionDetected = recursion;
650 lookupMode = c.singleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton;
655 if (candidate != end) {
657 componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
658 QQmlType returnType = QQmlMetaType::typeForUrl(
659 typeLoader->interceptUrl(QUrl(componentUrl), QQmlAbstractUrlInterceptor::QmlFile),
660 type, lookupMode,
nullptr, candidate->version);
662 *version_return = candidate->version;
664 *type_return = returnType;
665 return returnType.isValid();
669 const QString localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
670 if (localDirectoryPath.isEmpty())
675 const QString urlsToTry[2] = {
676 typeStr + dotqml_string,
677 typeStr + dotuidotqml_string
679 for (
const QString &urlToTry : urlsToTry) {
680 if (typeLoader->fileExists(localDirectoryPath, urlToTry)) {
681 qmlUrl = url + urlToTry;
686 if (!qmlUrl.isEmpty()) {
687 const bool recursion = base && *base == qmlUrl;
688 if (typeRecursionDetected)
689 *typeRecursionDetected = recursion;
691 QQmlType returnType = QQmlMetaType::typeForUrl(
692 typeLoader->interceptUrl(QUrl(qmlUrl), QQmlAbstractUrlInterceptor::QmlFile),
693 type, registrationType == QQmlType::CompositeSingletonType
694 ? QQmlMetaType::Singleton
695 : QQmlMetaType::NonSingleton,
698 *type_return = returnType;
699 return returnType.isValid();
707bool QQmlImports::resolveType(
708 QQmlTypeLoader *typeLoader,
const QHashedStringRef &type, QTypeRevision *version_return,
709 QQmlType *type_return, QList<QQmlError> *errors,
710 QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
const
712 const QList<QHashedStringRef> splitName = type.split(Dot);
713 auto resolveTypeInNamespace = [&](
714 QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
715 QList<QQmlError> *errors) ->
bool {
716 if (nameSpace->resolveType(
717 typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
718 registrationType, typeRecursionDetected))
720 if (nameSpace->imports.size() == 1
721 && !nameSpace->imports.at(0)->isLibrary
723 && nameSpace != &m_unqualifiedset) {
725 const QString urlString = resolveLocalUrl(
726 nameSpace->imports.at(0)->url, unqualifiedtype.toString() + dotqml_string);
727 *type_return = QQmlMetaType::typeForUrl(
728 typeLoader->interceptUrl(QUrl(urlString), QQmlAbstractUrlInterceptor::QmlFile),
729 type, QQmlMetaType::NonSingleton, errors);
730 return type_return->isValid();
734 switch (splitName.size()) {
737 return resolveTypeInNamespace(type, &m_unqualifiedset, errors);
741 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
745 return resolveTypeInNamespace(splitName.at(1), s, errors);
748 if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset,
nullptr)) {
750 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(
751 *type_return, splitName.at(1).toString());
752 if (type_return->isValid())
759 error.setDescription(QQmlImports::tr(
"- %1 is neither a type nor a namespace")
760 .arg(splitName.at(0).toString()));
761 errors->prepend(error);
767 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
770 error.setDescription(QQmlImports::tr(
"- %1 is not a namespace").arg(splitName.at(0).toString()));
772 if (resolveTypeInNamespace(splitName.at(1), s,
nullptr)) {
773 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(
774 *type_return, splitName.at(2).toString());
775 if (type_return->isValid())
779 error.setDescription(QQmlImports::tr(
780 "- %1 is not an inline component")
781 .arg(splitName.at(2).toString()));
783 error.setDescription(QQmlImports::tr(
"- %1 is not a type").arg(splitName.at(1).toString()));
787 errors->prepend(error);
795 error.setDescription(QQmlImports::tr(
"- nested namespaces not allowed"));
796 errors->prepend(error);
805 const QString &moduleUri, QTypeRevision version)
const
807 const auto end = imports.cend();
809 return import->uri == moduleUri && import->version == version;
811 return it == end ?
nullptr : *it;
815 const QString &location, QTypeRevision version)
const
817 const auto end = imports.cend();
819 return import->url == location && import->version == version;
821 return it == end ?
nullptr : *it;
825 QTypeRevision *version_return, QQmlType *type_return,
826 const QString *base, QList<QQmlError> *errors,
827 QQmlType::RegistrationType registrationType,
828 bool *typeRecursionDetected)
833 bool localTypeRecursionDetected =
false;
834 if (!typeRecursionDetected)
835 typeRecursionDetected = &localTypeRecursionDetected;
844 for (
int i=0; i<imports.size(); ++i) {
846 if (import->resolveType(typeLoader, type, version_return, type_return, base,
847 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
848 if (qmlCheckTypes()) {
850 for (
int j = i+1; j<imports.size(); ++j) {
852 if (import2->resolveType(typeLoader, type, version_return,
nullptr, base,
853 nullptr, registrationType)) {
855 QString u1 = import->url;
856 QString u2 = import2->url;
858 QStringView b(*base);
859 int dot = b.lastIndexOf(Dot);
862 QStringView l = b.left(dot);
863 if (u1.startsWith(b))
864 u1 = u1.mid(b.size());
866 u1 = QQmlImports::tr(
"local directory");
867 if (u2.startsWith(b))
868 u2 = u2.mid(b.size());
870 u2 = QQmlImports::tr(
"local directory");
876 error.setDescription(
878 "is ambiguous. Found in %1 and in %2")
881 error.setDescription(
883 "is ambiguous. Found in %1 in version "
886 .arg(import->version.majorVersion())
887 .arg(import->version.minorVersion())
888 .arg(import2->version.majorVersion())
889 .arg(import2->version.minorVersion()));
891 errors->prepend(error);
902 if (*typeRecursionDetected)
903 error.setDescription(QQmlImports::tr(
"is instantiated recursively"));
905 error.setDescription(QQmlImports::tr(
"is not a type"));
906 errors->prepend(error);
911QQmlImportNamespace *QQmlImports::findQualifiedNamespace(
const QHashedStringRef &prefix)
const
913 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
914 if (prefix == ns->prefix)
921
922
923QTypeRevision QQmlImports::importExtension(
924 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
925 const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
927 Q_ASSERT(qmldir->hasContent());
930 <<
"importExtension:" << qPrintable(m_base) <<
"loaded" << qmldir->qmldirLocation();
932 if (designerSupportRequired && !qmldir->designerSupported()) {
935 error.setDescription(
936 QQmlImports::tr(
"module does not support the designer \"%1\"")
937 .arg(qmldir->typeNamespace()));
938 error.setUrl(QUrl::fromLocalFile(qmldir->qmldirLocation()));
939 errors->prepend(error);
941 return QTypeRevision();
944 if (qmldir->plugins().isEmpty())
945 return validVersion(version);
947 QQmlPluginImporter importer(uri, version, qmldir, typeLoader, errors);
948 return importer.importPlugins();
951void QQmlImports::registerBuiltinModuleTypes(
952 const QQmlTypeLoaderQmldirContent &qmldir, QTypeRevision version)
954 if (!qmldir.plugins().isEmpty())
960 const QString qmldirUri = qmldir.typeNamespace();
961 if (!QQmlMetaType::typeModule(qmldirUri, version))
962 QQmlMetaType::qmlRegisterModuleTypes(qmldirUri);
965QString QQmlImports::redirectQmldirContent(
966 QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
968 const QString preferredPath = qmldir->preferredPath();
969 const QString url = preferredPath.startsWith(u':')
970 ? QStringLiteral(
"qrc") + preferredPath
971 : QUrl::fromLocalFile(preferredPath).toString();
973 QQmlTypeLoaderQmldirContent redirected
974 = typeLoader->qmldirContent(url + QLatin1String(
"qmldir"));
977 if (redirected.hasContent() && !redirected.hasError())
978 *qmldir = std::move(redirected);
983bool QQmlImports::getQmldirContent(
984 QQmlTypeLoader *typeLoader,
const QString &qmldirIdentifier,
const QString &uri,
985 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
990 *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
991 if (!qmldir->hasContent() || !qmldir->hasError())
994 errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
998QString QQmlImports::resolvedUri(
const QString &dir_arg, QQmlTypeLoader *typeLoader)
1000 QString dir = dir_arg;
1001 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
1004 QStringList paths = typeLoader->importPathList();
1005 if (!paths.isEmpty())
1006 std::sort(paths.begin(), paths.end(), std::greater<QString>());
1008 QString stableRelativePath = dir;
1009 for (
const QString &path : std::as_const(paths)) {
1010 if (dir.startsWith(path)) {
1011 stableRelativePath = dir.mid(path.size()+1);
1016 stableRelativePath.replace(Backslash, Slash);
1019 int versionDot = stableRelativePath.lastIndexOf(Dot);
1020 if (versionDot >= 0) {
1021 int nextSlash = stableRelativePath.indexOf(Slash, versionDot);
1023 stableRelativePath.remove(versionDot, nextSlash - versionDot);
1025 stableRelativePath = stableRelativePath.left(versionDot);
1028 stableRelativePath.replace(Slash, Dot);
1030 return stableRelativePath;
1035 QList<QQmlError> *errors)
1037 int bestMajorVersion = -1;
1038 quint8 lowestMinorVersion = std::numeric_limits<quint8>::max();
1039 quint8 highestMinorVersion = 0;
1041 auto addVersion = [&](QTypeRevision newVersion) {
1042 if (!newVersion.hasMajorVersion())
1044 if (!version.hasMajorVersion() || version.majorVersion() == newVersion.majorVersion()) {
1045 if (newVersion.majorVersion() > bestMajorVersion) {
1046 bestMajorVersion = newVersion.majorVersion();
1047 if (newVersion.hasMinorVersion()) {
1048 lowestMinorVersion = newVersion.minorVersion();
1049 highestMinorVersion = newVersion.minorVersion();
1051 }
else if (newVersion.majorVersion() == bestMajorVersion
1052 && newVersion.hasMinorVersion()) {
1053 lowestMinorVersion = qMin(lowestMinorVersion, newVersion.minorVersion());
1054 highestMinorVersion = qMax(highestMinorVersion, newVersion.minorVersion());
1059 typedef QQmlDirComponents::const_iterator ConstIterator;
1060 const QQmlDirComponents &components = qmldir.components();
1062 ConstIterator cend = components.constEnd();
1063 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1064 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1065 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1068 error.setDescription(
1070 "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1071 .arg(cit->typeName).arg(cit->version.majorVersion())
1072 .arg(cit->version.minorVersion()).arg(uri));
1073 errors->prepend(error);
1074 return QTypeRevision();
1078 addVersion(cit->version);
1081 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1082 const QQmlDirScripts &scripts = qmldir.scripts();
1084 SConstIterator send = scripts.constEnd();
1085 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1086 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1087 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1090 error.setDescription(QQmlImports::tr(
"\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1091 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1092 .arg(sit->version.minorVersion()).arg(uri));
1093 errors->prepend(error);
1094 return QTypeRevision();
1098 addVersion(sit->version);
1102 if (version.hasMajorVersion()
1103 && (bestMajorVersion < 0
1104 || (version.hasMinorVersion()
1105 && (lowestMinorVersion > version.minorVersion()
1106 || highestMinorVersion < version.minorVersion())))) {
1107 errors->prepend(QQmlImports::moduleNotFoundError(uri, version));
1108 return QTypeRevision();
1112 if (bestMajorVersion < 0)
1113 return QQmlImports::validVersion();
1115 return QTypeRevision::fromVersion(
1117 (version.hasMajorVersion() && version.hasMinorVersion())
1118 ? version.minorVersion()
1119 : highestMinorVersion);
1122QQmlImportNamespace *QQmlImports::importNamespace(
const QString &prefix)
1124 QQmlImportNamespace *nameSpace =
nullptr;
1126 if (prefix.isEmpty()) {
1127 nameSpace = &m_unqualifiedset;
1129 nameSpace = findQualifiedNamespace(prefix);
1132 nameSpace =
new QQmlImportNamespace;
1133 nameSpace->prefix = prefix;
1134 m_qualifiedSets.append(nameSpace);
1142 return version.isValid() ? QDebug::toString(version) : u"(latest)"_s;
1146 const QString &uri, QTypeRevision version, QList<QQmlError> *errors)
1148 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1149 if (!matchingVersion.isValid())
1150 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1151 return matchingVersion;
1159 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1160 if (matchingVersion.isValid())
1161 return matchingVersion;
1163 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1164 if (qmldir.plugins().isEmpty()) {
1165 if (!qmldir.imports().isEmpty())
1166 return QQmlImports::validVersion();
1167 if (qmldir.hasTypeInfo())
1168 return QQmlImports::validVersion();
1170 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1171 return QTypeRevision();
1174 version = matchingQmldirVersion(qmldir, uri, version, errors);
1175 if (!version.isValid())
1176 return QTypeRevision();
1179 Q_ASSERT(version.isValid());
1183QTypeRevision QQmlImports::addLibraryImport(
1184 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1185 QTypeRevision requestedVersion,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1186 ImportFlags flags, quint8 precedence, QList<QQmlError> *errors)
1188 Q_ASSERT(typeLoader);
1191 if (lcQmlImport().isDebugEnabled()) {
1192 qCDebug(lcQmlImport)
1193 <<
"addLibraryImport:" << qPrintable(baseUrl().toString())
1194 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1197 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1198 Q_ASSERT(nameSpace);
1200 const bool noQmldir = qmldirIdentifier.isEmpty();
1201 const bool isIncomplete = (flags & QQmlImports::ImportIncomplete);
1202 if (noQmldir || isIncomplete) {
1203 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::Yes>(
1204 nameSpace, uri, qmldirUrl, requestedVersion, precedence);
1207 if (noQmldir && !isIncomplete) {
1209 if (!QQmlMetaType::typeModule(uri, requestedVersion))
1210 QQmlMetaType::qmlRegisterModuleTypes(uri);
1211 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1214 return validVersion(requestedVersion);
1217 QQmlTypeLoaderQmldirContent qmldir;
1218 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) {
1220 return QTypeRevision();
1224 if (!qmldir.hasContent())
1225 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1228 const QTypeRevision importedVersion = importExtension(
1229 typeLoader, uri, requestedVersion, &qmldir, errors);
1230 if (!importedVersion.isValid())
1231 return QTypeRevision();
1233 QString resolvedUrl;
1234 QString resolvedUri;
1235 if (qmldir.hasRedirection()) {
1236 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1237 resolvedUri = qmldir.typeNamespace();
1239 resolvedUrl = qmldirUrl;
1243 if (QQmlImportInstance *existing
1244 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion);
1245 existing && existing->isLibrary && existing->uri == resolvedUri) {
1248 nameSpace->imports.removeOne(existing);
1249 existing->precedence = std::min(precedence, existing->precedence);
1250 existing->implicitlyImported = existing->precedence >= QQmlImportInstance::Implicit;
1251 insertImport(nameSpace, existing);
1252 return finalizeLibraryImport(uri, importedVersion, qmldir, existing, errors);
1255 return finalizeImport<IsLibrary::Yes>(
1256 nameSpace, qmldir, resolvedUri, resolvedUrl, precedence, requestedVersion,
1257 importedVersion, errors, [&](QQmlImportInstance *inserted) {
1258 return finalizeLibraryImport(uri, importedVersion, qmldir, inserted, errors);
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283QTypeRevision QQmlImports::addFileImport(
1284 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1285 QTypeRevision requestedVersion, ImportFlags flags, quint8 precedence, QString *localQmldir,
1286 QList<QQmlError> *errors)
1288 Q_ASSERT(typeLoader);
1291 if (lcQmlImport().isDebugEnabled()) {
1292 qCDebug(lcQmlImport)
1293 <<
"addFileImport:" << qPrintable(baseUrl().toString())
1294 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1297 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1299 const QString fix = uri.startsWith(Slash) ? QLatin1String(
"file:") + uri
1300 : QLatin1String(
"qrc") + uri;
1301 error.setDescription(QQmlImports::tr(
1302 "\"%1\" is not a valid import URL. "
1303 "You can pass relative paths or URLs with schema, but not "
1304 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1305 errors->prepend(error);
1306 return QTypeRevision();
1311 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1312 Q_ASSERT(nameSpace);
1316 QString importUri = uri;
1317 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1320 qmldirUrl = typeLoader->interceptUrl(
1321 QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
1322 QString qmldirIdentifier;
1324 if (QQmlFile::isLocalFile(qmldirUrl)) {
1326 const QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1327 Q_ASSERT(!localFileOrQrc.isEmpty());
1329 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1330 if (!typeLoader->directoryExists(dir)) {
1331 if (precedence < QQmlImportInstance::Implicit) {
1333 error.setDescription(QQmlImports::tr(
"\"%1\": no such directory").arg(uri));
1334 error.setUrl(QUrl(qmldirUrl));
1335 errors->prepend(error);
1337 return QTypeRevision();
1342 importUri = resolvedUri(dir, typeLoader);
1343 if (importUri.endsWith(Slash))
1346 if (QString cleanPath = typeLoader->cleanFilePath(localFileOrQrc); !cleanPath.isEmpty()) {
1347 qmldirIdentifier = std::move(cleanPath);
1349 *localQmldir = qmldirIdentifier;
1352 }
else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1354 if (precedence < QQmlImportInstance::Implicit) {
1356 error.setDescription(QQmlImports::tr(
"import \"%1\" has no qmldir and no namespace").arg(importUri));
1357 error.setUrl(QUrl(qmldirUrl));
1358 errors->prepend(error);
1361 return QTypeRevision();
1366 QString url = resolveLocalUrl(m_base, uri);
1367 if (url.isEmpty()) {
1369 error.setDescription(
1370 QQmlImports::tr(
"Cannot resolve URL for import \"%1\"").arg(uri));
1371 error.setUrl(m_baseUrl);
1372 errors->prepend(error);
1373 return QTypeRevision();
1376 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1383 if (precedence >= QQmlImportInstance::Implicit) {
1384 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
1385 it != nameSpace->imports.constEnd(); ++it) {
1386 if ((*it)->url == url) {
1387 (*it)->implicitlyImported =
true;
1388 return validVersion(requestedVersion);
1393 if ((flags & QQmlImports::ImportIncomplete) || qmldirIdentifier.isEmpty()) {
1394 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1395 nameSpace, importUri, url, requestedVersion, precedence);
1397 return validVersion(requestedVersion);
1400 QQmlTypeLoaderQmldirContent qmldir;
1401 if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
1402 return QTypeRevision();
1404 if (!qmldir.hasContent()) {
1405 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1406 nameSpace, importUri, url, requestedVersion, precedence);
1408 return validVersion(requestedVersion);
1412 if (qmldir.hasTypeNamespace())
1413 importUri = qmldir.typeNamespace();
1416 const QTypeRevision importedVersion
1417 = importExtension(typeLoader, importUri, requestedVersion, &qmldir, errors);
1418 if (!importedVersion.isValid())
1419 return QTypeRevision();
1421 if (qmldir.hasRedirection()) {
1422 const QString resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1423 importUri = qmldir.typeNamespace();
1424 if (resolvedUrl != url) {
1425 if (QQmlImportInstance *existing
1426 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) {
1428 return validVersion(existing->version);
1432 return finalizeImport<IsLibrary::No>(
1433 nameSpace, qmldir, importUri, resolvedUrl, precedence, requestedVersion,
1434 importedVersion, errors);
1437 return finalizeImport<IsLibrary::No>(
1438 nameSpace, qmldir, importUri, url, precedence, requestedVersion,
1439 importedVersion, errors);
1444 if (errors->isEmpty()) {
1446 error.setDescription(QQmlTypeLoader::tr(
"Cannot update qmldir content for '%1'").arg(uri));
1447 errors->prepend(error);
1449 return QTypeRevision();
1452QTypeRevision QQmlImports::updateQmldirContent(
1453 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
1454 const QString &prefix,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1455 QList<QQmlError> *errors)
1457 Q_ASSERT(typeLoader);
1460 qCDebug(lcQmlImport)
1461 <<
"updateQmldirContent:" << qPrintable(baseUrl().toString())
1462 << uri <<
"to" << qmldirUrl <<
"as" << prefix;
1464 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1465 Q_ASSERT(nameSpace);
1467 QQmlImportInstance *import = nameSpace->findImportByModuleUri(uri, version);
1469 return qmldirContentError(uri, errors);
1471 QQmlTypeLoaderQmldirContent qmldir;
1472 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1473 return QTypeRevision();
1475 if (!qmldir.hasContent())
1476 return qmldirContentError(uri, errors);
1479 version = importExtension(typeLoader, uri, import->version, &qmldir, errors);
1480 if (!version.isValid())
1481 return QTypeRevision();
1483 QString resolvedUrl;
1484 if (qmldir.hasRedirection()) {
1485 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1486 if (resolvedUrl != import->url) {
1487 if (QQmlImportInstance *existing
1488 = nameSpace->findImportByLocation(resolvedUrl, import->version)) {
1491 nameSpace->imports.removeOne(import);
1493 return validVersion(existing->version);
1495 import->url = resolvedUrl;
1497 import->uri = qmldir.typeNamespace();
1499 resolvedUrl = qmldirUrl;
1502 registerBuiltinModuleTypes(qmldir, version);
1504 if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
1505 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1507 if (uri != QLatin1String(
".")
1508 && !QQmlMetaType::matchingModuleVersion(import->uri, version).isValid()) {
1509 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1510 return QTypeRevision();
1514 version = matchingQmldirVersion(qmldir, import->uri, version, errors);
1515 if (!version.isValid())
1516 return QTypeRevision();
1518 return validVersion(version);
1521 return qmldirContentError(uri, errors);
1525
1526
1527
1528
1529
1530
1531
1532
1536
1537
1538bool QQmlImports::addInlineComponentImport(
1539 QQmlImportInstance *
const importInstance,
const QString &name,
const QUrl &importUrl)
1541 importInstance->url = importUrl.toString();
1542 importInstance->uri = name;
1543 importInstance->isInlineComponent =
true;
1544 importInstance->version = QTypeRevision::zero();
1545 m_unqualifiedset.imports.push_back(importInstance);
1546 m_unqualifiedset.setNeedsSorting(
true);
1550QUrl QQmlImports::urlFromLocalFileOrQrcOrUrl(
const QString &file)
1552 QUrl url(QLatin1String(file.at(0) == Colon ?
"qrc" :
"") + file);
1555 if (url.scheme().size() < 2)
1556 return QUrl::fromLocalFile(file);
1560void QQmlImports::setDesignerSupportRequired(
bool b)
1562 designerSupportRequired = b;
QQmlImportInstance * findImportByModuleUri(const QString &moduleUri, QTypeRevision version) const
bool needsSorting() const
QQmlImportInstance * findImportByLocation(const QString &location, QTypeRevision version) const
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)
void setNeedsSorting(bool needsSorting)
const QLoggingCategory & category() const
~QmlImportCategoryHolder()=default
QmlImportCategoryHolder()
static const QLatin1String String_qmldir("qmldir")
#define RESOLVE_TYPE_DEBUG
static const QString dotuidotqml_string(QStringLiteral(".ui.qml"))
static const QLatin1Char Colon(':')
static QTypeRevision qmldirContentError(const QString &uri, QList< QQmlError > *errors)
static const QString dotqml_string(QStringLiteral(".qml"))
static bool designerSupportRequired
void findCompositeSingletons(const QQmlImportNamespace &set, QList< QQmlImports::CompositeSingletonReference > &resultList, const QUrl &baseUrl)
static QTypeRevision matchingModuleVersionForLibraryImport(const QString &uri, QTypeRevision version, QList< QQmlError > *errors)
static QString getVersionInfo(QTypeRevision version)
static const QLatin1Char Dot('.')
static QTypeRevision matchingQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, QTypeRevision version, QList< QQmlError > *errors)
bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl)
static const QLatin1Char Slash('/')
static const QLatin1Char Backslash('\\')
static const QLatin1String Slash_qmldir("/qmldir")
static QTypeRevision finalizeLibraryImport(const QString &uri, QTypeRevision version, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportInstance *inserted, QList< QQmlError > *errors)
const QLoggingCategory & lcQmlImport()
DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE)
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)