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
484 Q_ASSERT(resolvedUrl.endsWith(Slash));
487 qmlDirComponents = qmldir.components();
489 const QQmlDirScripts &scripts = qmldir.scripts();
490 if (!scripts.isEmpty()) {
492 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
493 it != nameSpace->imports.constEnd(); ++it) {
494 if ((*it !=
this) && ((*it)->uri == uri)) {
496 error.setDescription(
497 QQmlImports::tr(
"\"%1\" is ambiguous. Found in %2 and in %3")
498 .arg(uri, url, (*it)->url));
499 errors->prepend(error);
504 qmlDirScripts = getVersionedScripts(scripts, version);
511 QTypeRevision version)
513 QMap<QString, QQmlDirParser::Script> versioned;
515 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
516 sit != qmldirscripts.constEnd(); ++sit) {
518 if ((!version.hasMajorVersion() || (sit->version.majorVersion() == version.majorVersion()))
519 && (!version.hasMinorVersion()
520 || (sit->version.minorVersion() <= version.minorVersion()))) {
522 const auto vit = versioned.constFind(sit->nameSpace);
523 if (vit == versioned.cend()
524 || (vit->version.minorVersion() < sit->version.minorVersion())) {
525 versioned.insert(sit->nameSpace, *sit);
530 return versioned.values();
534
535
536
537
538
539
540
541
542
543
546 QTypeRevision *version_return, QQmlType *type_return,
547 const QString *base,
bool *typeRecursionDetected,
548 QQmlType::RegistrationType registrationType,
550 QList<QQmlError> *errors)
const
555 QQmlType t = uri.isEmpty() ? QQmlType() : QQmlMetaType::qmlType(type, uri, version);
558 *version_return = version;
564 const QString typeStr = type.toString();
566 Q_ASSERT(type_return);
567 bool ret = uri == typeStr;
569 Q_ASSERT(!type_return->isValid());
570 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(QUrl(url));
572 if (!type_return->isValid())
577 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
579 QString componentUrl;
580 QQmlMetaType::CompositeTypeLookupMode lookupMode = QQmlMetaType::NonSingleton;
581 QQmlDirComponents::ConstIterator candidate = end;
582 for ( ; it != end && it.key() == typeStr; ++it) {
583 const QQmlDirParser::Component &c = *it;
584 switch (registrationType) {
585 case QQmlType::AnyRegistrationType:
587 case QQmlType::CompositeSingletonType:
598 if (!version.hasMajorVersion() || (implicitlyImported && c.internal)
600 || (c.version.majorVersion() == version.majorVersion()
601 && c.version.minorVersion() <= version.minorVersion())) {
603 if ((candidate == end)
604 || (c.version.majorVersion() > candidate->version.majorVersion())
605 || ((c.version.majorVersion() == candidate->version.majorVersion())
606 && (c.version.minorVersion() > candidate->version.minorVersion()))) {
608 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
609 if (c.internal && resolveLocalUrl(*base, c.fileName) != componentUrl) {
613 const auto componentsBegin = qmlDirComponents.cbegin();
614 const auto componentsEnd = qmlDirComponents.cend();
615 if (
std::find_if(componentsBegin, componentsEnd,
616 [&](
const QQmlDirParser::Component &component) {
617 return resolveLocalUrl(url, component.fileName)
625 const bool recursion = *base == componentUrl;
626 if (typeRecursionDetected)
627 *typeRecursionDetected = recursion;
636 lookupMode = c.singleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton;
641 if (candidate != end) {
643 componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
644 QQmlType returnType = QQmlMetaType::typeForUrl(
645 typeLoader->interceptUrl(QUrl(componentUrl), QQmlAbstractUrlInterceptor::QmlFile),
646 type, lookupMode,
nullptr, candidate->version);
648 *version_return = candidate->version;
650 *type_return = returnType;
651 return returnType.isValid();
655 const QString localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
656 if (localDirectoryPath.isEmpty())
661 const QString urlsToTry[2] = {
662 typeStr + dotqml_string,
663 typeStr + dotuidotqml_string
665 for (
const QString &urlToTry : urlsToTry) {
666 if (typeLoader->fileExists(localDirectoryPath, urlToTry)) {
667 qmlUrl = url + urlToTry;
672 if (!qmlUrl.isEmpty()) {
673 const bool recursion = base && *base == qmlUrl;
674 if (typeRecursionDetected)
675 *typeRecursionDetected = recursion;
677 QQmlType returnType = QQmlMetaType::typeForUrl(
678 typeLoader->interceptUrl(QUrl(qmlUrl), QQmlAbstractUrlInterceptor::QmlFile),
679 type, registrationType == QQmlType::CompositeSingletonType
680 ? QQmlMetaType::Singleton
681 : QQmlMetaType::NonSingleton,
684 *type_return = returnType;
685 return returnType.isValid();
693bool QQmlImports::resolveType(
694 QQmlTypeLoader *typeLoader,
const QHashedStringRef &type, QTypeRevision *version_return,
695 QQmlType *type_return, QList<QQmlError> *errors,
696 QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
const
698 const QList<QHashedStringRef> splitName = type.split(Dot);
699 auto resolveTypeInNamespace = [&](
700 QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
701 QList<QQmlError> *errors) ->
bool {
702 if (nameSpace->resolveType(
703 typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
704 registrationType, typeRecursionDetected))
706 if (nameSpace->imports.size() == 1
707 && !nameSpace->imports.at(0)->isLibrary
709 && nameSpace != &m_unqualifiedset) {
711 const QString urlString = resolveLocalUrl(
712 nameSpace->imports.at(0)->url, unqualifiedtype.toString() + dotqml_string);
713 *type_return = QQmlMetaType::typeForUrl(
714 typeLoader->interceptUrl(QUrl(urlString), QQmlAbstractUrlInterceptor::QmlFile),
715 type, QQmlMetaType::NonSingleton, errors);
716 return type_return->isValid();
720 switch (splitName.size()) {
723 return resolveTypeInNamespace(type, &m_unqualifiedset, errors);
727 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
731 return resolveTypeInNamespace(splitName.at(1), s, errors);
734 if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset,
nullptr)) {
736 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(
737 *type_return, splitName.at(1).toString());
738 if (type_return->isValid())
745 error.setDescription(QQmlImports::tr(
"- %1 is neither a type nor a namespace")
746 .arg(splitName.at(0).toString()));
747 errors->prepend(error);
753 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
756 error.setDescription(QQmlImports::tr(
"- %1 is not a namespace").arg(splitName.at(0).toString()));
758 if (resolveTypeInNamespace(splitName.at(1), s,
nullptr)) {
759 *type_return = QQmlMetaType::findOrCreateSpeculativeInlineComponentType(
760 *type_return, splitName.at(2).toString());
761 if (type_return->isValid())
765 error.setDescription(QQmlImports::tr(
766 "- %1 is not an inline component")
767 .arg(splitName.at(2).toString()));
769 error.setDescription(QQmlImports::tr(
"- %1 is not a type").arg(splitName.at(1).toString()));
773 errors->prepend(error);
781 error.setDescription(QQmlImports::tr(
"- nested namespaces not allowed"));
782 errors->prepend(error);
791 const QString &moduleUri, QTypeRevision version)
const
793 const auto end = imports.cend();
795 return import->uri == moduleUri && import->version == version;
797 return it == end ?
nullptr : *it;
801 const QString &location, QTypeRevision version)
const
803 const auto end = imports.cend();
805 return import->url == location && import->version == version;
807 return it == end ?
nullptr : *it;
811 QTypeRevision *version_return, QQmlType *type_return,
812 const QString *base, QList<QQmlError> *errors,
813 QQmlType::RegistrationType registrationType,
814 bool *typeRecursionDetected)
819 bool localTypeRecursionDetected =
false;
820 if (!typeRecursionDetected)
821 typeRecursionDetected = &localTypeRecursionDetected;
830 for (
int i=0; i<imports.size(); ++i) {
832 if (import->resolveType(typeLoader, type, version_return, type_return, base,
833 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
834 if (qmlCheckTypes()) {
836 for (
int j = i+1; j<imports.size(); ++j) {
838 if (import2->resolveType(typeLoader, type, version_return,
nullptr, base,
839 nullptr, registrationType)) {
841 QString u1 = import->url;
842 QString u2 = import2->url;
844 QStringView b(*base);
845 int dot = b.lastIndexOf(Dot);
848 QStringView l = b.left(dot);
849 if (u1.startsWith(b))
850 u1 = u1.mid(b.size());
852 u1 = QQmlImports::tr(
"local directory");
853 if (u2.startsWith(b))
854 u2 = u2.mid(b.size());
856 u2 = QQmlImports::tr(
"local directory");
862 error.setDescription(
864 "is ambiguous. Found in %1 and in %2")
867 error.setDescription(
869 "is ambiguous. Found in %1 in version "
872 .arg(import->version.majorVersion())
873 .arg(import->version.minorVersion())
874 .arg(import2->version.majorVersion())
875 .arg(import2->version.minorVersion()));
877 errors->prepend(error);
888 if (*typeRecursionDetected)
889 error.setDescription(QQmlImports::tr(
"is instantiated recursively"));
891 error.setDescription(QQmlImports::tr(
"is not a type"));
892 errors->prepend(error);
897QQmlImportNamespace *QQmlImports::findQualifiedNamespace(
const QHashedStringRef &prefix)
const
899 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
900 if (prefix == ns->prefix)
907
908
909QTypeRevision QQmlImports::importExtension(
910 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
911 const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
913 Q_ASSERT(qmldir->hasContent());
916 <<
"importExtension:" << qPrintable(m_base) <<
"loaded" << qmldir->qmldirLocation();
918 if (designerSupportRequired && !qmldir->designerSupported()) {
921 error.setDescription(
922 QQmlImports::tr(
"module does not support the designer \"%1\"")
923 .arg(qmldir->typeNamespace()));
924 error.setUrl(QUrl::fromLocalFile(qmldir->qmldirLocation()));
925 errors->prepend(error);
927 return QTypeRevision();
930 if (qmldir->plugins().isEmpty())
931 return validVersion(version);
933 QQmlPluginImporter importer(uri, version, qmldir, typeLoader, errors);
934 return importer.importPlugins();
937void QQmlImports::registerBuiltinModuleTypes(
938 const QQmlTypeLoaderQmldirContent &qmldir, QTypeRevision version)
940 if (!qmldir.plugins().isEmpty())
946 const QString qmldirUri = qmldir.typeNamespace();
947 if (!QQmlMetaType::typeModule(qmldirUri, version))
948 QQmlMetaType::qmlRegisterModuleTypes(qmldirUri);
951QString QQmlImports::redirectQmldirContent(
952 QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
954 const QString preferredPath = qmldir->preferredPath();
955 const QString url = preferredPath.startsWith(u':')
956 ? QStringLiteral(
"qrc") + preferredPath
957 : QUrl::fromLocalFile(preferredPath).toString();
959 QQmlTypeLoaderQmldirContent redirected
960 = typeLoader->qmldirContent(url + QLatin1String(
"qmldir"));
963 if (redirected.hasContent() && !redirected.hasError())
964 *qmldir = std::move(redirected);
969bool QQmlImports::getQmldirContent(
970 QQmlTypeLoader *typeLoader,
const QString &qmldirIdentifier,
const QString &uri,
971 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
976 *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
977 if (!qmldir->hasContent() || !qmldir->hasError())
980 errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
984QString QQmlImports::resolvedUri(
const QString &dir_arg, QQmlTypeLoader *typeLoader)
986 QString dir = dir_arg;
987 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
990 QStringList paths = typeLoader->importPathList();
991 if (!paths.isEmpty())
992 std::sort(paths.begin(), paths.end(), std::greater<QString>());
994 QString stableRelativePath = dir;
995 for (
const QString &path : std::as_const(paths)) {
996 if (dir.startsWith(path)) {
997 stableRelativePath = dir.mid(path.size()+1);
1002 stableRelativePath.replace(Backslash, Slash);
1005 int versionDot = stableRelativePath.lastIndexOf(Dot);
1006 if (versionDot >= 0) {
1007 int nextSlash = stableRelativePath.indexOf(Slash, versionDot);
1009 stableRelativePath.remove(versionDot, nextSlash - versionDot);
1011 stableRelativePath = stableRelativePath.left(versionDot);
1014 stableRelativePath.replace(Slash, Dot);
1016 return stableRelativePath;
1021 QList<QQmlError> *errors)
1023 int bestMajorVersion = -1;
1024 quint8 lowestMinorVersion = std::numeric_limits<quint8>::max();
1025 quint8 highestMinorVersion = 0;
1027 auto addVersion = [&](QTypeRevision newVersion) {
1028 if (!newVersion.hasMajorVersion())
1030 if (!version.hasMajorVersion() || version.majorVersion() == newVersion.majorVersion()) {
1031 if (newVersion.majorVersion() > bestMajorVersion) {
1032 bestMajorVersion = newVersion.majorVersion();
1033 if (newVersion.hasMinorVersion()) {
1034 lowestMinorVersion = newVersion.minorVersion();
1035 highestMinorVersion = newVersion.minorVersion();
1037 }
else if (newVersion.majorVersion() == bestMajorVersion
1038 && newVersion.hasMinorVersion()) {
1039 lowestMinorVersion = qMin(lowestMinorVersion, newVersion.minorVersion());
1040 highestMinorVersion = qMax(highestMinorVersion, newVersion.minorVersion());
1045 typedef QQmlDirComponents::const_iterator ConstIterator;
1046 const QQmlDirComponents &components = qmldir.components();
1048 ConstIterator cend = components.constEnd();
1049 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1050 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1051 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1054 error.setDescription(
1056 "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1057 .arg(cit->typeName).arg(cit->version.majorVersion())
1058 .arg(cit->version.minorVersion()).arg(uri));
1059 errors->prepend(error);
1060 return QTypeRevision();
1064 addVersion(cit->version);
1067 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1068 const QQmlDirScripts &scripts = qmldir.scripts();
1070 SConstIterator send = scripts.constEnd();
1071 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1072 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1073 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1076 error.setDescription(QQmlImports::tr(
"\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1077 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1078 .arg(sit->version.minorVersion()).arg(uri));
1079 errors->prepend(error);
1080 return QTypeRevision();
1084 addVersion(sit->version);
1088 if (version.hasMajorVersion()
1089 && (bestMajorVersion < 0
1090 || (version.hasMinorVersion()
1091 && (lowestMinorVersion > version.minorVersion()
1092 || highestMinorVersion < version.minorVersion())))) {
1093 errors->prepend(QQmlImports::moduleNotFoundError(uri, version));
1094 return QTypeRevision();
1098 if (bestMajorVersion < 0)
1099 return QQmlImports::validVersion();
1101 return QTypeRevision::fromVersion(
1103 (version.hasMajorVersion() && version.hasMinorVersion())
1104 ? version.minorVersion()
1105 : highestMinorVersion);
1108QQmlImportNamespace *QQmlImports::importNamespace(
const QString &prefix)
1110 QQmlImportNamespace *nameSpace =
nullptr;
1112 if (prefix.isEmpty()) {
1113 nameSpace = &m_unqualifiedset;
1115 nameSpace = findQualifiedNamespace(prefix);
1118 nameSpace =
new QQmlImportNamespace;
1119 nameSpace->prefix = prefix;
1120 m_qualifiedSets.append(nameSpace);
1128 return version.isValid() ? QDebug::toString(version) : u"(latest)"_s;
1132 const QString &uri, QTypeRevision version, QList<QQmlError> *errors)
1134 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1135 if (!matchingVersion.isValid())
1136 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1137 return matchingVersion;
1145 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1146 if (matchingVersion.isValid())
1147 return matchingVersion;
1149 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1150 if (qmldir.plugins().isEmpty()) {
1151 if (!qmldir.imports().isEmpty())
1152 return QQmlImports::validVersion();
1153 if (qmldir.hasTypeInfo())
1154 return QQmlImports::validVersion();
1156 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1157 return QTypeRevision();
1160 version = matchingQmldirVersion(qmldir, uri, version, errors);
1161 if (!version.isValid())
1162 return QTypeRevision();
1165 Q_ASSERT(version.isValid());
1169QTypeRevision QQmlImports::addLibraryImport(
1170 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1171 QTypeRevision requestedVersion,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1172 ImportFlags flags, quint8 precedence, QList<QQmlError> *errors)
1174 Q_ASSERT(typeLoader);
1177 if (lcQmlImport().isDebugEnabled()) {
1178 qCDebug(lcQmlImport)
1179 <<
"addLibraryImport:" << qPrintable(baseUrl().toString())
1180 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1183 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1184 Q_ASSERT(nameSpace);
1186 const bool noQmldir = qmldirIdentifier.isEmpty();
1187 const bool isIncomplete = (flags & QQmlImports::ImportIncomplete);
1188 if (noQmldir || isIncomplete) {
1189 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::Yes>(
1190 nameSpace, uri, qmldirUrl, requestedVersion, precedence);
1193 if (noQmldir && !isIncomplete) {
1195 if (!QQmlMetaType::typeModule(uri, requestedVersion))
1196 QQmlMetaType::qmlRegisterModuleTypes(uri);
1197 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1200 return validVersion(requestedVersion);
1203 QQmlTypeLoaderQmldirContent qmldir;
1204 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) {
1206 return QTypeRevision();
1210 if (!qmldir.hasContent())
1211 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1214 const QTypeRevision importedVersion = importExtension(
1215 typeLoader, uri, requestedVersion, &qmldir, errors);
1216 if (!importedVersion.isValid())
1217 return QTypeRevision();
1219 QString resolvedUrl;
1220 QString resolvedUri;
1221 if (qmldir.hasRedirection()) {
1222 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1223 resolvedUri = qmldir.typeNamespace();
1225 resolvedUrl = qmldirUrl;
1229 if (QQmlImportInstance *existing
1230 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion);
1231 existing && existing->isLibrary && existing->uri == resolvedUri) {
1234 nameSpace->imports.removeOne(existing);
1235 existing->precedence = std::min(precedence, existing->precedence);
1236 existing->implicitlyImported = existing->precedence >= QQmlImportInstance::Implicit;
1237 insertImport(nameSpace, existing);
1238 return finalizeLibraryImport(uri, importedVersion, qmldir, existing, errors);
1241 return finalizeImport<IsLibrary::Yes>(
1242 nameSpace, qmldir, resolvedUri, resolvedUrl, precedence, requestedVersion,
1243 importedVersion, errors, [&](QQmlImportInstance *inserted) {
1244 return finalizeLibraryImport(uri, importedVersion, qmldir, inserted, errors);
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269QTypeRevision QQmlImports::addFileImport(
1270 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1271 QTypeRevision requestedVersion, ImportFlags flags, quint8 precedence, QString *localQmldir,
1272 QList<QQmlError> *errors)
1274 Q_ASSERT(typeLoader);
1277 if (lcQmlImport().isDebugEnabled()) {
1278 qCDebug(lcQmlImport)
1279 <<
"addFileImport:" << qPrintable(baseUrl().toString())
1280 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1283 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1285 const QString fix = uri.startsWith(Slash) ? QLatin1String(
"file:") + uri
1286 : QLatin1String(
"qrc") + uri;
1287 error.setDescription(QQmlImports::tr(
1288 "\"%1\" is not a valid import URL. "
1289 "You can pass relative paths or URLs with schema, but not "
1290 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1291 errors->prepend(error);
1292 return QTypeRevision();
1297 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1298 Q_ASSERT(nameSpace);
1302 QString importUri = uri;
1303 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1306 qmldirUrl = typeLoader->interceptUrl(
1307 QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
1308 QString qmldirIdentifier;
1310 if (QQmlFile::isLocalFile(qmldirUrl)) {
1312 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1313 Q_ASSERT(!localFileOrQrc.isEmpty());
1315 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1316 if (!typeLoader->directoryExists(dir)) {
1317 if (precedence < QQmlImportInstance::Implicit) {
1319 error.setDescription(QQmlImports::tr(
"\"%1\": no such directory").arg(uri));
1320 error.setUrl(QUrl(qmldirUrl));
1321 errors->prepend(error);
1323 return QTypeRevision();
1328 importUri = resolvedUri(dir, typeLoader);
1329 if (importUri.endsWith(Slash))
1332 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
1333 qmldirIdentifier = std::move(localFileOrQrc);
1335 *localQmldir = qmldirIdentifier;
1338 }
else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1340 if (precedence < QQmlImportInstance::Implicit) {
1342 error.setDescription(QQmlImports::tr(
"import \"%1\" has no qmldir and no namespace").arg(importUri));
1343 error.setUrl(QUrl(qmldirUrl));
1344 errors->prepend(error);
1347 return QTypeRevision();
1352 QString url = resolveLocalUrl(m_base, uri);
1353 if (url.isEmpty()) {
1355 error.setDescription(
1356 QQmlImports::tr(
"Cannot resolve URL for import \"%1\"").arg(uri));
1357 error.setUrl(m_baseUrl);
1358 errors->prepend(error);
1359 return QTypeRevision();
1362 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1369 if (precedence >= QQmlImportInstance::Implicit) {
1370 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
1371 it != nameSpace->imports.constEnd(); ++it) {
1372 if ((*it)->url == url) {
1373 (*it)->implicitlyImported =
true;
1374 return validVersion(requestedVersion);
1379 if ((flags & QQmlImports::ImportIncomplete) || qmldirIdentifier.isEmpty()) {
1380 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1381 nameSpace, importUri, url, requestedVersion, precedence);
1383 return validVersion(requestedVersion);
1386 QQmlTypeLoaderQmldirContent qmldir;
1387 if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
1388 return QTypeRevision();
1390 if (!qmldir.hasContent()) {
1391 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1392 nameSpace, importUri, url, requestedVersion, precedence);
1394 return validVersion(requestedVersion);
1398 if (qmldir.hasTypeNamespace())
1399 importUri = qmldir.typeNamespace();
1402 const QTypeRevision importedVersion
1403 = importExtension(typeLoader, importUri, requestedVersion, &qmldir, errors);
1404 if (!importedVersion.isValid())
1405 return QTypeRevision();
1407 if (qmldir.hasRedirection()) {
1408 const QString resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1409 importUri = qmldir.typeNamespace();
1410 if (resolvedUrl != url) {
1411 if (QQmlImportInstance *existing
1412 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) {
1414 return validVersion(existing->version);
1418 return finalizeImport<IsLibrary::No>(
1419 nameSpace, qmldir, importUri, resolvedUrl, precedence, requestedVersion,
1420 importedVersion, errors);
1423 return finalizeImport<IsLibrary::No>(
1424 nameSpace, qmldir, importUri, url, precedence, requestedVersion,
1425 importedVersion, errors);
1430 if (errors->isEmpty()) {
1432 error.setDescription(QQmlTypeLoader::tr(
"Cannot update qmldir content for '%1'").arg(uri));
1433 errors->prepend(error);
1435 return QTypeRevision();
1438QTypeRevision QQmlImports::updateQmldirContent(
1439 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
1440 const QString &prefix,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1441 QList<QQmlError> *errors)
1443 Q_ASSERT(typeLoader);
1446 qCDebug(lcQmlImport)
1447 <<
"updateQmldirContent:" << qPrintable(baseUrl().toString())
1448 << uri <<
"to" << qmldirUrl <<
"as" << prefix;
1450 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1451 Q_ASSERT(nameSpace);
1453 QQmlImportInstance *import = nameSpace->findImportByModuleUri(uri, version);
1455 return qmldirContentError(uri, errors);
1457 QQmlTypeLoaderQmldirContent qmldir;
1458 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1459 return QTypeRevision();
1461 if (!qmldir.hasContent())
1462 return qmldirContentError(uri, errors);
1465 version = importExtension(typeLoader, uri, import->version, &qmldir, errors);
1466 if (!version.isValid())
1467 return QTypeRevision();
1469 QString resolvedUrl;
1470 if (qmldir.hasRedirection()) {
1471 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1472 if (resolvedUrl != import->url) {
1473 if (QQmlImportInstance *existing
1474 = nameSpace->findImportByLocation(resolvedUrl, import->version)) {
1477 nameSpace->imports.removeOne(import);
1479 return validVersion(existing->version);
1481 import->url = resolvedUrl;
1483 import->uri = qmldir.typeNamespace();
1485 resolvedUrl = qmldirUrl;
1488 registerBuiltinModuleTypes(qmldir, version);
1490 if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
1491 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1493 if (uri != QLatin1String(
".")
1494 && !QQmlMetaType::matchingModuleVersion(import->uri, version).isValid()) {
1495 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1496 return QTypeRevision();
1500 version = matchingQmldirVersion(qmldir, import->uri, version, errors);
1501 if (!version.isValid())
1502 return QTypeRevision();
1504 return validVersion(version);
1507 return qmldirContentError(uri, errors);
1511
1512
1513
1514
1515
1516
1517
1518
1522
1523
1524bool QQmlImports::addInlineComponentImport(
1525 QQmlImportInstance *
const importInstance,
const QString &name,
const QUrl &importUrl)
1527 importInstance->url = importUrl.toString();
1528 importInstance->uri = name;
1529 importInstance->isInlineComponent =
true;
1530 importInstance->version = QTypeRevision::zero();
1531 m_unqualifiedset.imports.push_back(importInstance);
1532 m_unqualifiedset.setNeedsSorting(
true);
1536QUrl QQmlImports::urlFromLocalFileOrQrcOrUrl(
const QString &file)
1538 QUrl url(QLatin1String(file.at(0) == Colon ?
"qrc" :
"") + file);
1541 if (url.scheme().size() < 2)
1542 return QUrl::fromLocalFile(file);
1546void QQmlImports::setDesignerSupportRequired(
bool b)
1548 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)