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 const QQmlDirExports exports = qmldir.exports();
1063 for (QQmlDirParser::Export exp : exports) {
1064 Q_ASSERT(exp.hasMajorVersion());
1065 addVersion(QTypeRevision::fromVersion(exp.majorVersion(), 0));
1066 addVersion(exp.hasMinorVersion() ? exp
1067 : QTypeRevision::fromVersion(exp.majorVersion(), 254));
1070 ConstIterator cend = components.constEnd();
1071 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1072 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1073 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1076 error.setDescription(
1078 "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1079 .arg(cit->typeName).arg(cit->version.majorVersion())
1080 .arg(cit->version.minorVersion()).arg(uri));
1081 errors->prepend(error);
1082 return QTypeRevision();
1086 addVersion(cit->version);
1089 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1090 const QQmlDirScripts &scripts = qmldir.scripts();
1092 SConstIterator send = scripts.constEnd();
1093 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1094 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1095 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1098 error.setDescription(QQmlImports::tr(
"\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1099 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1100 .arg(sit->version.minorVersion()).arg(uri));
1101 errors->prepend(error);
1102 return QTypeRevision();
1106 addVersion(sit->version);
1110 if (version.hasMajorVersion()
1111 && (bestMajorVersion < 0
1112 || (version.hasMinorVersion()
1113 && (lowestMinorVersion > version.minorVersion()
1114 || highestMinorVersion < version.minorVersion())))) {
1115 errors->prepend(QQmlImports::moduleNotFoundError(uri, version));
1116 return QTypeRevision();
1120 if (bestMajorVersion < 0)
1121 return QQmlImports::validVersion();
1123 return QTypeRevision::fromVersion(
1125 (version.hasMajorVersion() && version.hasMinorVersion())
1126 ? version.minorVersion()
1127 : highestMinorVersion);
1130QQmlImportNamespace *QQmlImports::importNamespace(
const QString &prefix)
1132 QQmlImportNamespace *nameSpace =
nullptr;
1134 if (prefix.isEmpty()) {
1135 nameSpace = &m_unqualifiedset;
1137 nameSpace = findQualifiedNamespace(prefix);
1140 nameSpace =
new QQmlImportNamespace;
1141 nameSpace->prefix = prefix;
1142 m_qualifiedSets.append(nameSpace);
1150 return version.isValid() ? QDebug::toString(version) : u"(latest)"_s;
1154 const QString &uri, QTypeRevision version, QList<QQmlError> *errors)
1156 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1157 if (!matchingVersion.isValid())
1158 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1159 return matchingVersion;
1167 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1168 if (matchingVersion.isValid())
1169 return matchingVersion;
1171 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1172 if (qmldir.plugins().isEmpty()) {
1173 if (!qmldir.imports().isEmpty())
1174 return QQmlImports::validVersion();
1175 if (qmldir.hasTypeInfo())
1176 return QQmlImports::validVersion();
1178 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1179 return QTypeRevision();
1182 version = matchingQmldirVersion(qmldir, uri, version, errors);
1183 if (!version.isValid())
1184 return QTypeRevision();
1187 Q_ASSERT(version.isValid());
1191QTypeRevision QQmlImports::addLibraryImport(
1192 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1193 QTypeRevision requestedVersion,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1194 ImportFlags flags, quint8 precedence, QList<QQmlError> *errors)
1196 Q_ASSERT(typeLoader);
1199 if (lcQmlImport().isDebugEnabled()) {
1200 qCDebug(lcQmlImport)
1201 <<
"addLibraryImport:" << qPrintable(baseUrl().toString())
1202 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1205 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1206 Q_ASSERT(nameSpace);
1208 const bool noQmldir = qmldirIdentifier.isEmpty();
1209 const bool isIncomplete = (flags & QQmlImports::ImportIncomplete);
1210 if (noQmldir || isIncomplete) {
1211 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::Yes>(
1212 nameSpace, uri, qmldirUrl, requestedVersion, precedence);
1215 if (noQmldir && !isIncomplete) {
1217 if (!QQmlMetaType::typeModule(uri, requestedVersion))
1218 QQmlMetaType::qmlRegisterModuleTypes(uri);
1219 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1222 return validVersion(requestedVersion);
1225 QQmlTypeLoaderQmldirContent qmldir;
1226 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) {
1228 return QTypeRevision();
1232 if (!qmldir.hasContent())
1233 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1236 const QTypeRevision importedVersion = importExtension(
1237 typeLoader, uri, requestedVersion, &qmldir, errors);
1238 if (!importedVersion.isValid())
1239 return QTypeRevision();
1241 QString resolvedUrl;
1242 QString resolvedUri;
1243 if (qmldir.hasRedirection()) {
1244 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1245 resolvedUri = qmldir.typeNamespace();
1247 resolvedUrl = qmldirUrl;
1251 if (QQmlImportInstance *existing
1252 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion);
1253 existing && existing->isLibrary && existing->uri == resolvedUri) {
1256 nameSpace->imports.removeOne(existing);
1257 existing->precedence = std::min(precedence, existing->precedence);
1258 existing->implicitlyImported = existing->precedence >= QQmlImportInstance::Implicit;
1259 insertImport(nameSpace, existing);
1260 return finalizeLibraryImport(uri, importedVersion, qmldir, existing, errors);
1263 return finalizeImport<IsLibrary::Yes>(
1264 nameSpace, qmldir, resolvedUri, resolvedUrl, precedence, requestedVersion,
1265 importedVersion, errors, [&](QQmlImportInstance *inserted) {
1266 return finalizeLibraryImport(uri, importedVersion, qmldir, inserted, errors);
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291QTypeRevision QQmlImports::addFileImport(
1292 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1293 QTypeRevision requestedVersion, ImportFlags flags, quint8 precedence, QString *localQmldir,
1294 QList<QQmlError> *errors)
1296 Q_ASSERT(typeLoader);
1299 if (lcQmlImport().isDebugEnabled()) {
1300 qCDebug(lcQmlImport)
1301 <<
"addFileImport:" << qPrintable(baseUrl().toString())
1302 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1305 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1307 const QString fix = uri.startsWith(Slash) ? QLatin1String(
"file:") + uri
1308 : QLatin1String(
"qrc") + uri;
1309 error.setDescription(QQmlImports::tr(
1310 "\"%1\" is not a valid import URL. "
1311 "You can pass relative paths or URLs with schema, but not "
1312 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1313 errors->prepend(error);
1314 return QTypeRevision();
1319 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1320 Q_ASSERT(nameSpace);
1324 QString importUri = uri;
1325 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1328 qmldirUrl = typeLoader->interceptUrl(
1329 QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
1330 QString qmldirIdentifier;
1332 if (QQmlFile::isLocalFile(qmldirUrl)) {
1334 const QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1335 Q_ASSERT(!localFileOrQrc.isEmpty());
1337 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1338 if (!typeLoader->directoryExists(dir)) {
1339 if (precedence < QQmlImportInstance::Implicit) {
1341 error.setDescription(QQmlImports::tr(
"\"%1\": no such directory").arg(uri));
1342 error.setUrl(QUrl(qmldirUrl));
1343 errors->prepend(error);
1345 return QTypeRevision();
1350 importUri = resolvedUri(dir, typeLoader);
1351 if (importUri.endsWith(Slash))
1354 if (QString cleanPath = typeLoader->cleanFilePath(localFileOrQrc); !cleanPath.isEmpty()) {
1355 qmldirIdentifier = std::move(cleanPath);
1357 *localQmldir = qmldirIdentifier;
1360 }
else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1362 if (precedence < QQmlImportInstance::Implicit) {
1364 error.setDescription(QQmlImports::tr(
"import \"%1\" has no qmldir and no namespace").arg(importUri));
1365 error.setUrl(QUrl(qmldirUrl));
1366 errors->prepend(error);
1369 return QTypeRevision();
1374 QString url = resolveLocalUrl(m_base, uri);
1375 if (url.isEmpty()) {
1377 error.setDescription(
1378 QQmlImports::tr(
"Cannot resolve URL for import \"%1\"").arg(uri));
1379 error.setUrl(m_baseUrl);
1380 errors->prepend(error);
1381 return QTypeRevision();
1384 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1391 if (precedence >= QQmlImportInstance::Implicit) {
1392 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
1393 it != nameSpace->imports.constEnd(); ++it) {
1394 if ((*it)->url == url) {
1395 (*it)->implicitlyImported =
true;
1396 return validVersion(requestedVersion);
1401 if ((flags & QQmlImports::ImportIncomplete) || qmldirIdentifier.isEmpty()) {
1402 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1403 nameSpace, importUri, url, requestedVersion, precedence);
1405 return validVersion(requestedVersion);
1408 QQmlTypeLoaderQmldirContent qmldir;
1409 if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
1410 return QTypeRevision();
1412 if (!qmldir.hasContent()) {
1413 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1414 nameSpace, importUri, url, requestedVersion, precedence);
1416 return validVersion(requestedVersion);
1420 if (qmldir.hasTypeNamespace())
1421 importUri = qmldir.typeNamespace();
1424 const QTypeRevision importedVersion
1425 = importExtension(typeLoader, importUri, requestedVersion, &qmldir, errors);
1426 if (!importedVersion.isValid())
1427 return QTypeRevision();
1429 if (qmldir.hasRedirection()) {
1430 const QString resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1431 importUri = qmldir.typeNamespace();
1432 if (resolvedUrl != url) {
1433 if (QQmlImportInstance *existing
1434 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) {
1436 return validVersion(existing->version);
1440 return finalizeImport<IsLibrary::No>(
1441 nameSpace, qmldir, importUri, resolvedUrl, precedence, requestedVersion,
1442 importedVersion, errors);
1445 return finalizeImport<IsLibrary::No>(
1446 nameSpace, qmldir, importUri, url, precedence, requestedVersion,
1447 importedVersion, errors);
1452 if (errors->isEmpty()) {
1454 error.setDescription(QQmlTypeLoader::tr(
"Cannot update qmldir content for '%1'").arg(uri));
1455 errors->prepend(error);
1457 return QTypeRevision();
1460QTypeRevision QQmlImports::updateQmldirContent(
1461 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
1462 const QString &prefix,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1463 QList<QQmlError> *errors)
1465 Q_ASSERT(typeLoader);
1468 qCDebug(lcQmlImport)
1469 <<
"updateQmldirContent:" << qPrintable(baseUrl().toString())
1470 << uri <<
"to" << qmldirUrl <<
"as" << prefix;
1472 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1473 Q_ASSERT(nameSpace);
1475 QQmlImportInstance *import = nameSpace->findImportByModuleUri(uri, version);
1477 return qmldirContentError(uri, errors);
1479 QQmlTypeLoaderQmldirContent qmldir;
1480 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1481 return QTypeRevision();
1483 if (!qmldir.hasContent())
1484 return qmldirContentError(uri, errors);
1487 version = importExtension(typeLoader, uri, import->version, &qmldir, errors);
1488 if (!version.isValid())
1489 return QTypeRevision();
1491 QString resolvedUrl;
1492 if (qmldir.hasRedirection()) {
1493 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1494 if (resolvedUrl != import->url) {
1495 if (QQmlImportInstance *existing
1496 = nameSpace->findImportByLocation(resolvedUrl, import->version)) {
1499 nameSpace->imports.removeOne(import);
1501 return validVersion(existing->version);
1503 import->url = resolvedUrl;
1505 import->uri = qmldir.typeNamespace();
1507 resolvedUrl = qmldirUrl;
1510 registerBuiltinModuleTypes(qmldir, version);
1512 if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
1513 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1515 if (uri != QLatin1String(
".")
1516 && !QQmlMetaType::matchingModuleVersion(import->uri, version).isValid()) {
1517 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1518 return QTypeRevision();
1522 version = matchingQmldirVersion(qmldir, import->uri, version, errors);
1523 if (!version.isValid())
1524 return QTypeRevision();
1526 return validVersion(version);
1529 return qmldirContentError(uri, errors);
1533
1534
1535
1536
1537
1538
1539
1540
1544
1545
1546bool QQmlImports::addInlineComponentImport(
1547 QQmlImportInstance *
const importInstance,
const QString &name,
const QUrl &importUrl)
1549 importInstance->url = importUrl.toString();
1550 importInstance->uri = name;
1551 importInstance->isInlineComponent =
true;
1552 importInstance->version = QTypeRevision::zero();
1553 m_unqualifiedset.imports.push_back(importInstance);
1554 m_unqualifiedset.setNeedsSorting(
true);
1558QUrl QQmlImports::urlFromLocalFileOrQrcOrUrl(
const QString &file)
1560 QUrl url(QLatin1String(file.at(0) == Colon ?
"qrc" :
"") + file);
1563 if (url.scheme().size() < 2)
1564 return QUrl::fromLocalFile(file);
1568void QQmlImports::setDesignerSupportRequired(
bool b)
1570 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)