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();
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;
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())
460 RESOLVE_TYPE_DEBUG << type_return->typeName() <<
' ' << type_return->sourceUrl() <<
" TYPE/URL-SINGLETON";
461 else if (type_return->isComposite())
462 RESOLVE_TYPE_DEBUG << type_return->typeName() <<
' ' << type_return->sourceUrl() <<
" TYPE/URL";
463 else if (type_return->isInlineComponentType())
464 RESOLVE_TYPE_DEBUG << type_return->typeName() <<
' ' << type_return->sourceUrl() <<
" TYPE(INLINECOMPONENT)";
468#undef RESOLVE_TYPE_DEBUG
480 Q_ASSERT(resolvedUrl.endsWith(Slash));
483 qmlDirComponents = qmldir.components();
485 const QQmlDirScripts &scripts = qmldir.scripts();
486 if (!scripts.isEmpty()) {
488 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
489 it != nameSpace->imports.constEnd(); ++it) {
490 if ((*it !=
this) && ((*it)->uri == uri)) {
492 error.setDescription(
493 QQmlImports::tr(
"\"%1\" is ambiguous. Found in %2 and in %3")
494 .arg(uri, url, (*it)->url));
495 errors->prepend(error);
500 qmlDirScripts = getVersionedScripts(scripts, version);
509 QMap<QString, QQmlDirParser::Script> versioned;
511 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
512 sit != qmldirscripts.constEnd(); ++sit) {
514 if ((!version.hasMajorVersion() || (sit->version.majorVersion() == version.majorVersion()))
515 && (!version.hasMinorVersion()
516 || (sit->version.minorVersion() <= version.minorVersion()))) {
518 const auto vit = versioned.constFind(sit->nameSpace);
519 if (vit == versioned.cend()
520 || (vit->version.minorVersion() < sit->version.minorVersion())) {
521 versioned.insert(sit->nameSpace, *sit);
526 return versioned.values();
530
531
532
533
534
535
536
537
538
539
543 const QString *base,
bool *typeRecursionDetected,
544 QQmlType::RegistrationType registrationType,
546 QList<QQmlError> *errors)
const
551 QQmlType t = uri.isEmpty() ? QQmlType() : QQmlMetaType::qmlType(type, uri, version);
554 *version_return = version;
560 const QString typeStr = type.toString();
562 Q_ASSERT(type_return);
563 bool ret = uri == typeStr;
565 Q_ASSERT(!type_return->isValid());
566 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(QUrl(url));
568 if (!type_return->isValid())
573 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
575 QString componentUrl;
576 QQmlMetaType::CompositeTypeLookupMode lookupMode = QQmlMetaType::NonSingleton;
577 QQmlDirComponents::ConstIterator candidate = end;
578 for ( ; it != end && it.key() == typeStr; ++it) {
579 const QQmlDirParser::Component &c = *it;
580 switch (registrationType) {
581 case QQmlType::AnyRegistrationType:
583 case QQmlType::CompositeSingletonType:
594 if (!version.hasMajorVersion() || (implicitlyImported && c.internal)
596 || (c.version.majorVersion() == version.majorVersion()
597 && c.version.minorVersion() <= version.minorVersion())) {
599 if ((candidate == end)
600 || (c.version.majorVersion() > candidate->version.majorVersion())
601 || ((c.version.majorVersion() == candidate->version.majorVersion())
602 && (c.version.minorVersion() > candidate->version.minorVersion()))) {
604 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
606 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
610 const bool recursion = *base == componentUrl;
611 if (typeRecursionDetected)
612 *typeRecursionDetected = recursion;
621 lookupMode = c.singleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton;
626 if (candidate != end) {
628 componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
629 QQmlType returnType = QQmlMetaType::typeForUrl(
630 typeLoader->interceptUrl(QUrl(componentUrl), QQmlAbstractUrlInterceptor::QmlFile),
631 type, lookupMode,
nullptr, candidate->version);
633 *version_return = candidate->version;
635 *type_return = returnType;
636 return returnType.isValid();
640 const QString localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
641 if (localDirectoryPath.isEmpty())
646 const QString urlsToTry[2] = {
647 typeStr + dotqml_string,
648 typeStr + dotuidotqml_string
650 for (
const QString &urlToTry : urlsToTry) {
651 if (typeLoader->fileExists(localDirectoryPath, urlToTry)) {
652 qmlUrl = url + urlToTry;
657 if (!qmlUrl.isEmpty()) {
658 const bool recursion = base && *base == qmlUrl;
659 if (typeRecursionDetected)
660 *typeRecursionDetected = recursion;
662 QQmlType returnType = QQmlMetaType::typeForUrl(
663 typeLoader->interceptUrl(QUrl(qmlUrl), QQmlAbstractUrlInterceptor::QmlFile),
664 type, registrationType == QQmlType::CompositeSingletonType
665 ? QQmlMetaType::Singleton
666 : QQmlMetaType::NonSingleton,
669 *type_return = returnType;
670 return returnType.isValid();
678bool QQmlImports::resolveType(
679 QQmlTypeLoader *typeLoader,
const QHashedStringRef &type, QTypeRevision *version_return,
680 QQmlType *type_return, QList<QQmlError> *errors,
681 QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
const
683 const QList<QHashedStringRef> splitName = type.split(Dot);
684 auto resolveTypeInNamespace = [&](
685 QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
686 QList<QQmlError> *errors) ->
bool {
687 if (nameSpace->resolveType(
688 typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
689 registrationType, typeRecursionDetected))
691 if (nameSpace->imports.size() == 1
692 && !nameSpace->imports.at(0)->isLibrary
694 && nameSpace != &m_unqualifiedset) {
696 const QString urlString = resolveLocalUrl(
697 nameSpace->imports.at(0)->url, unqualifiedtype.toString() + dotqml_string);
698 *type_return = QQmlMetaType::typeForUrl(
699 typeLoader->interceptUrl(QUrl(urlString), QQmlAbstractUrlInterceptor::QmlFile),
700 type, QQmlMetaType::NonSingleton, errors);
701 return type_return->isValid();
705 switch (splitName.size()) {
708 return resolveTypeInNamespace(type, &m_unqualifiedset, errors);
712 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
716 return resolveTypeInNamespace(splitName.at(1), s, errors);
719 if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset,
nullptr)) {
721 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(
722 *type_return, splitName.at(1).toString());
723 if (type_return->isValid())
730 error.setDescription(QQmlImports::tr(
"- %1 is neither a type nor a namespace")
731 .arg(splitName.at(0).toString()));
732 errors->prepend(error);
738 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
741 error.setDescription(QQmlImports::tr(
"- %1 is not a namespace").arg(splitName.at(0).toString()));
743 if (resolveTypeInNamespace(splitName.at(1), s,
nullptr)) {
744 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(
745 *type_return, splitName.at(2).toString());
746 if (type_return->isValid())
750 error.setDescription(QQmlImports::tr(
751 "- %1 is not an inline component")
752 .arg(splitName.at(2).toString()));
754 error.setDescription(QQmlImports::tr(
"- %1 is not a type").arg(splitName.at(1).toString()));
758 errors->prepend(error);
766 error.setDescription(QQmlImports::tr(
"- nested namespaces not allowed"));
767 errors->prepend(error);
778 const auto end = imports.cend();
780 return import->uri == moduleUri && import->version == version;
782 return it == end ?
nullptr : *it;
788 const auto end = imports.cend();
790 return import->url == location && import->version == version;
792 return it == end ?
nullptr : *it;
797 const QString *base, QList<QQmlError> *errors,
798 QQmlType::RegistrationType registrationType,
799 bool *typeRecursionDetected)
804 bool localTypeRecursionDetected =
false;
805 if (!typeRecursionDetected)
806 typeRecursionDetected = &localTypeRecursionDetected;
815 for (
int i=0; i<imports.size(); ++i) {
817 if (import->resolveType(typeLoader, type, version_return, type_return, base,
818 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
819 if (qmlCheckTypes()) {
821 for (
int j = i+1; j<imports.size(); ++j) {
823 if (import2->resolveType(typeLoader, type, version_return,
nullptr, base,
824 nullptr, registrationType)) {
826 QString u1 = import->url;
827 QString u2 = import2->url;
829 QStringView b(*base);
830 int dot = b.lastIndexOf(Dot);
833 QStringView l = b.left(dot);
834 if (u1.startsWith(b))
835 u1 = u1.mid(b.size());
837 u1 = QQmlImports::tr(
"local directory");
838 if (u2.startsWith(b))
839 u2 = u2.mid(b.size());
841 u2 = QQmlImports::tr(
"local directory");
847 error.setDescription(
849 "is ambiguous. Found in %1 and in %2")
852 error.setDescription(
854 "is ambiguous. Found in %1 in version "
857 .arg(import->version.majorVersion())
858 .arg(import->version.minorVersion())
859 .arg(import2->version.majorVersion())
860 .arg(import2->version.minorVersion()));
862 errors->prepend(error);
873 if (*typeRecursionDetected)
874 error.setDescription(QQmlImports::tr(
"is instantiated recursively"));
876 error.setDescription(QQmlImports::tr(
"is not a type"));
877 errors->prepend(error);
882QQmlImportNamespace *QQmlImports::findQualifiedNamespace(
const QHashedStringRef &prefix)
const
884 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
885 if (prefix == ns->prefix)
892
893
894QTypeRevision QQmlImports::importExtension(
895 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
896 const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
898 Q_ASSERT(qmldir->hasContent());
901 <<
"importExtension:" << qPrintable(m_base) <<
"loaded" << qmldir->qmldirLocation();
903 if (designerSupportRequired && !qmldir->designerSupported()) {
906 error.setDescription(
907 QQmlImports::tr(
"module does not support the designer \"%1\"")
908 .arg(qmldir->typeNamespace()));
909 error.setUrl(QUrl::fromLocalFile(qmldir->qmldirLocation()));
910 errors->prepend(error);
912 return QTypeRevision();
915 if (qmldir->plugins().isEmpty())
916 return validVersion(version);
918 QQmlPluginImporter importer(uri, version, qmldir, typeLoader, errors);
919 return importer.importPlugins();
922void QQmlImports::registerBuiltinModuleTypes(
923 const QQmlTypeLoaderQmldirContent &qmldir, QTypeRevision version)
925 if (!qmldir.plugins().isEmpty())
931 const QString qmldirUri = qmldir.typeNamespace();
932 if (!QQmlMetaType::typeModule(qmldirUri, version))
933 QQmlMetaType::qmlRegisterModuleTypes(qmldirUri);
936QString QQmlImports::redirectQmldirContent(
937 QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
939 const QString preferredPath = qmldir->preferredPath();
940 const QString url = preferredPath.startsWith(u':')
941 ? QStringLiteral(
"qrc") + preferredPath
942 : QUrl::fromLocalFile(preferredPath).toString();
944 QQmlTypeLoaderQmldirContent redirected
945 = typeLoader->qmldirContent(url + QLatin1String(
"qmldir"));
948 if (redirected.hasContent() && !redirected.hasError())
949 *qmldir = std::move(redirected);
954bool QQmlImports::getQmldirContent(
955 QQmlTypeLoader *typeLoader,
const QString &qmldirIdentifier,
const QString &uri,
956 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
961 *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
962 if (!qmldir->hasContent() || !qmldir->hasError())
965 errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
969QString QQmlImports::resolvedUri(
const QString &dir_arg, QQmlTypeLoader *typeLoader)
971 QString dir = dir_arg;
972 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
975 QStringList paths = typeLoader->importPathList();
976 if (!paths.isEmpty())
977 std::sort(paths.begin(), paths.end(), std::greater<QString>());
979 QString stableRelativePath = dir;
980 for (
const QString &path : std::as_const(paths)) {
981 if (dir.startsWith(path)) {
982 stableRelativePath = dir.mid(path.size()+1);
987 stableRelativePath.replace(Backslash, Slash);
990 int versionDot = stableRelativePath.lastIndexOf(Dot);
991 if (versionDot >= 0) {
992 int nextSlash = stableRelativePath.indexOf(Slash, versionDot);
994 stableRelativePath.remove(versionDot, nextSlash - versionDot);
996 stableRelativePath = stableRelativePath.left(versionDot);
999 stableRelativePath.replace(Slash, Dot);
1001 return stableRelativePath;
1006 QList<QQmlError> *errors)
1008 int bestMajorVersion = -1;
1009 quint8 lowestMinorVersion = std::numeric_limits<quint8>::max();
1010 quint8 highestMinorVersion = 0;
1013 if (!newVersion.hasMajorVersion())
1015 if (!version.hasMajorVersion() || version.majorVersion() == newVersion.majorVersion()) {
1016 if (newVersion.majorVersion() > bestMajorVersion) {
1017 bestMajorVersion = newVersion.majorVersion();
1018 if (newVersion.hasMinorVersion()) {
1019 lowestMinorVersion = newVersion.minorVersion();
1020 highestMinorVersion = newVersion.minorVersion();
1022 }
else if (newVersion.majorVersion() == bestMajorVersion
1023 && newVersion.hasMinorVersion()) {
1024 lowestMinorVersion = qMin(lowestMinorVersion, newVersion.minorVersion());
1025 highestMinorVersion = qMax(highestMinorVersion, newVersion.minorVersion());
1030 typedef QQmlDirComponents::const_iterator ConstIterator;
1031 const QQmlDirComponents &components = qmldir.components();
1033 ConstIterator cend = components.constEnd();
1034 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1035 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1036 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1039 error.setDescription(
1041 "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1042 .arg(cit->typeName).arg(cit->version.majorVersion())
1043 .arg(cit->version.minorVersion()).arg(uri));
1044 errors->prepend(error);
1045 return QTypeRevision();
1049 addVersion(cit->version);
1052 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1053 const QQmlDirScripts &scripts = qmldir.scripts();
1055 SConstIterator send = scripts.constEnd();
1056 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1057 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1058 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1061 error.setDescription(QQmlImports::tr(
"\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1062 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1063 .arg(sit->version.minorVersion()).arg(uri));
1064 errors->prepend(error);
1065 return QTypeRevision();
1069 addVersion(sit->version);
1073 if (version.hasMajorVersion()
1074 && (bestMajorVersion < 0
1075 || (version.hasMinorVersion()
1076 && (lowestMinorVersion > version.minorVersion()
1077 || highestMinorVersion < version.minorVersion())))) {
1078 errors->prepend(QQmlImports::moduleNotFoundError(uri, version));
1079 return QTypeRevision();
1083 if (bestMajorVersion < 0)
1084 return QQmlImports::validVersion();
1086 return QTypeRevision::fromVersion(
1088 (version.hasMajorVersion() && version.hasMinorVersion())
1089 ? version.minorVersion()
1090 : highestMinorVersion);
1093QQmlImportNamespace *QQmlImports::importNamespace(
const QString &prefix)
1095 QQmlImportNamespace *nameSpace =
nullptr;
1097 if (prefix.isEmpty()) {
1098 nameSpace = &m_unqualifiedset;
1100 nameSpace = findQualifiedNamespace(prefix);
1103 nameSpace =
new QQmlImportNamespace;
1104 nameSpace->prefix = prefix;
1105 m_qualifiedSets.append(nameSpace);
1113 return version.isValid() ? QDebug::toString(version) : u"(latest)"_s;
1117 const QString &uri,
QTypeRevision version, QList<QQmlError> *errors)
1119 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1120 if (!matchingVersion.isValid())
1121 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1122 return matchingVersion;
1130 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1131 if (matchingVersion.isValid())
1132 return matchingVersion;
1134 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1135 if (qmldir.plugins().isEmpty()) {
1136 if (!qmldir.imports().isEmpty())
1137 return QQmlImports::validVersion();
1138 if (qmldir.hasTypeInfo())
1139 return QQmlImports::validVersion();
1141 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1142 return QTypeRevision();
1145 version = matchingQmldirVersion(qmldir, uri, version, errors);
1146 if (!version.isValid())
1147 return QTypeRevision();
1150 Q_ASSERT(version.isValid());
1154QTypeRevision QQmlImports::addLibraryImport(
1155 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1156 QTypeRevision requestedVersion,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1157 ImportFlags flags, quint8 precedence, QList<QQmlError> *errors)
1159 Q_ASSERT(typeLoader);
1162 if (lcQmlImport().isDebugEnabled()) {
1163 qCDebug(lcQmlImport)
1164 <<
"addLibraryImport:" << qPrintable(baseUrl().toString())
1165 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1168 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1169 Q_ASSERT(nameSpace);
1171 const bool noQmldir = qmldirIdentifier.isEmpty();
1172 const bool isIncomplete = (flags & QQmlImports::ImportIncomplete);
1173 if (noQmldir || isIncomplete) {
1174 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::Yes>(
1175 nameSpace, uri, qmldirUrl, requestedVersion, precedence);
1178 if (noQmldir && !isIncomplete) {
1180 if (!QQmlMetaType::typeModule(uri, requestedVersion))
1181 QQmlMetaType::qmlRegisterModuleTypes(uri);
1182 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1185 return validVersion(requestedVersion);
1188 QQmlTypeLoaderQmldirContent qmldir;
1189 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) {
1191 return QTypeRevision();
1195 if (!qmldir.hasContent())
1196 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1199 const QTypeRevision importedVersion = importExtension(
1200 typeLoader, uri, requestedVersion, &qmldir, errors);
1201 if (!importedVersion.isValid())
1202 return QTypeRevision();
1204 QString resolvedUrl;
1205 QString resolvedUri;
1206 if (qmldir.hasRedirection()) {
1207 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1208 resolvedUri = qmldir.typeNamespace();
1210 resolvedUrl = qmldirUrl;
1214 if (QQmlImportInstance *existing
1215 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion);
1216 existing && existing->isLibrary && existing->uri == resolvedUri) {
1219 nameSpace->imports.removeOne(existing);
1220 existing->precedence = std::min(precedence, existing->precedence);
1221 existing->implicitlyImported = existing->precedence >= QQmlImportInstance::Implicit;
1222 insertImport(nameSpace, existing);
1223 return finalizeLibraryImport(uri, importedVersion, qmldir, existing, errors);
1226 return finalizeImport<IsLibrary::Yes>(
1227 nameSpace, qmldir, resolvedUri, resolvedUrl, precedence, requestedVersion,
1228 importedVersion, errors, [&](QQmlImportInstance *inserted) {
1229 return finalizeLibraryImport(uri, importedVersion, qmldir, inserted, errors);
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254QTypeRevision QQmlImports::addFileImport(
1255 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1256 QTypeRevision requestedVersion, ImportFlags flags, quint8 precedence, QString *localQmldir,
1257 QList<QQmlError> *errors)
1259 Q_ASSERT(typeLoader);
1262 if (lcQmlImport().isDebugEnabled()) {
1263 qCDebug(lcQmlImport)
1264 <<
"addFileImport:" << qPrintable(baseUrl().toString())
1265 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1268 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1270 const QString fix = uri.startsWith(Slash) ? QLatin1String(
"file:") + uri
1271 : QLatin1String(
"qrc") + uri;
1272 error.setDescription(QQmlImports::tr(
1273 "\"%1\" is not a valid import URL. "
1274 "You can pass relative paths or URLs with schema, but not "
1275 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1276 errors->prepend(error);
1277 return QTypeRevision();
1282 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1283 Q_ASSERT(nameSpace);
1287 QString importUri = uri;
1288 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1291 qmldirUrl = typeLoader->interceptUrl(
1292 QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
1293 QString qmldirIdentifier;
1295 if (QQmlFile::isLocalFile(qmldirUrl)) {
1297 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1298 Q_ASSERT(!localFileOrQrc.isEmpty());
1300 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1301 if (!typeLoader->directoryExists(dir)) {
1302 if (precedence < QQmlImportInstance::Implicit) {
1304 error.setDescription(QQmlImports::tr(
"\"%1\": no such directory").arg(uri));
1305 error.setUrl(QUrl(qmldirUrl));
1306 errors->prepend(error);
1308 return QTypeRevision();
1313 importUri = resolvedUri(dir, typeLoader);
1314 if (importUri.endsWith(Slash))
1317 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
1318 qmldirIdentifier = std::move(localFileOrQrc);
1320 *localQmldir = qmldirIdentifier;
1323 }
else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1325 if (precedence < QQmlImportInstance::Implicit) {
1327 error.setDescription(QQmlImports::tr(
"import \"%1\" has no qmldir and no namespace").arg(importUri));
1328 error.setUrl(QUrl(qmldirUrl));
1329 errors->prepend(error);
1332 return QTypeRevision();
1337 QString url = resolveLocalUrl(m_base, uri);
1338 if (url.isEmpty()) {
1340 error.setDescription(
1341 QQmlImports::tr(
"Cannot resolve URL for import \"%1\"").arg(uri));
1342 error.setUrl(m_baseUrl);
1343 errors->prepend(error);
1344 return QTypeRevision();
1347 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1354 if (precedence >= QQmlImportInstance::Implicit) {
1355 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
1356 it != nameSpace->imports.constEnd(); ++it) {
1357 if ((*it)->url == url) {
1358 (*it)->implicitlyImported =
true;
1359 return validVersion(requestedVersion);
1364 if ((flags & QQmlImports::ImportIncomplete) || qmldirIdentifier.isEmpty()) {
1365 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1366 nameSpace, importUri, url, requestedVersion, precedence);
1368 return validVersion(requestedVersion);
1371 QQmlTypeLoaderQmldirContent qmldir;
1372 if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
1373 return QTypeRevision();
1375 if (!qmldir.hasContent()) {
1376 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1377 nameSpace, importUri, url, requestedVersion, precedence);
1379 return validVersion(requestedVersion);
1383 if (qmldir.hasTypeNamespace())
1384 importUri = qmldir.typeNamespace();
1387 const QTypeRevision importedVersion
1388 = importExtension(typeLoader, importUri, requestedVersion, &qmldir, errors);
1389 if (!importedVersion.isValid())
1390 return QTypeRevision();
1392 if (qmldir.hasRedirection()) {
1393 const QString resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1394 importUri = qmldir.typeNamespace();
1395 if (resolvedUrl != url) {
1396 if (QQmlImportInstance *existing
1397 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) {
1399 return validVersion(existing->version);
1403 return finalizeImport<IsLibrary::No>(
1404 nameSpace, qmldir, importUri, resolvedUrl, precedence, requestedVersion,
1405 importedVersion, errors);
1408 return finalizeImport<IsLibrary::No>(
1409 nameSpace, qmldir, importUri, url, precedence, requestedVersion,
1410 importedVersion, errors);
1415 if (errors->isEmpty()) {
1417 error.setDescription(QQmlTypeLoader::tr(
"Cannot update qmldir content for '%1'").arg(uri));
1418 errors->prepend(error);
1420 return QTypeRevision();
1423QTypeRevision QQmlImports::updateQmldirContent(
1424 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
1425 const QString &prefix,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1426 QList<QQmlError> *errors)
1428 Q_ASSERT(typeLoader);
1431 qCDebug(lcQmlImport)
1432 <<
"updateQmldirContent:" << qPrintable(baseUrl().toString())
1433 << uri <<
"to" << qmldirUrl <<
"as" << prefix;
1435 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1436 Q_ASSERT(nameSpace);
1438 QQmlImportInstance *import = nameSpace->findImportByModuleUri(uri, version);
1440 return qmldirContentError(uri, errors);
1442 QQmlTypeLoaderQmldirContent qmldir;
1443 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1444 return QTypeRevision();
1446 if (!qmldir.hasContent())
1447 return qmldirContentError(uri, errors);
1450 version = importExtension(typeLoader, uri, import->version, &qmldir, errors);
1451 if (!version.isValid())
1452 return QTypeRevision();
1454 QString resolvedUrl;
1455 if (qmldir.hasRedirection()) {
1456 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1457 if (resolvedUrl != import->url) {
1458 if (QQmlImportInstance *existing
1459 = nameSpace->findImportByLocation(resolvedUrl, import->version)) {
1462 nameSpace->imports.removeOne(import);
1464 return validVersion(existing->version);
1466 import->url = resolvedUrl;
1468 import->uri = qmldir.typeNamespace();
1470 resolvedUrl = qmldirUrl;
1473 registerBuiltinModuleTypes(qmldir, version);
1475 if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
1476 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1478 if (uri != QLatin1String(
".")
1479 && !QQmlMetaType::matchingModuleVersion(import->uri, version).isValid()) {
1480 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1481 return QTypeRevision();
1485 version = matchingQmldirVersion(qmldir, import->uri, version, errors);
1486 if (!version.isValid())
1487 return QTypeRevision();
1489 return validVersion(version);
1492 return qmldirContentError(uri, errors);
1496
1497
1498
1499
1500
1501
1502
1503
1507
1508
1509bool QQmlImports::addInlineComponentImport(
1510 QQmlImportInstance *
const importInstance,
const QString &name,
const QUrl &importUrl)
1512 importInstance->url = importUrl.toString();
1513 importInstance->uri = name;
1514 importInstance->isInlineComponent =
true;
1515 importInstance->version = QTypeRevision::zero();
1516 m_unqualifiedset.imports.push_back(importInstance);
1517 m_unqualifiedset.setNeedsSorting(
true);
1521QUrl QQmlImports::urlFromLocalFileOrQrcOrUrl(
const QString &file)
1523 QUrl url(QLatin1String(file.at(0) == Colon ?
"qrc" :
"") + file);
1526 if (url.scheme().size() < 2)
1527 return QUrl::fromLocalFile(file);
1531void QQmlImports::setDesignerSupportRequired(
bool b)
1533 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)