7#include <QtCore/qdebug.h>
8#include <QtCore/qdir.h>
9#include <QtQml/qqmlfile.h>
10#include <QtCore/qfileinfo.h>
11#include <QtCore/qpluginloader.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtQml/qqmlextensioninterface.h>
14#include <QtQml/qqmlextensionplugin.h>
15#include <private/qqmlextensionplugin_p.h>
16#include <private/qqmlglobal_p.h>
17#include <private/qqmltypenamecache_p.h>
18#include <private/qqmlengine_p.h>
19#include <private/qqmltypemodule_p.h>
20#include <private/qqmltypeloaderqmldircontent_p.h>
21#include <private/qqmlpluginimporter_p.h>
22#include <QtCore/qjsonobject.h>
23#include <QtCore/qjsonarray.h>
24#include <QtQml/private/qqmltype_p_p.h>
25#include <QtQml/private/qqmlimportresolver_p.h>
30using namespace Qt::Literals::StringLiterals;
47 m_category.setEnabled(QtDebugMsg,
true);
55 QLoggingCategory m_category;
61 return holder.category();
80 return QQmlMetaType::latestModuleVersion(uri).isValid() ? version : QTypeRevision();
83QString resolveLocalUrl(
const QString &url,
const QString &relative)
85 if (relative.contains(Colon)) {
87 return QUrl(url).resolved(QUrl(relative)).toString();
88 }
else if (relative.isEmpty()) {
90 }
else if (relative.at(0) == Slash || !url.contains(Slash)) {
93 const QStringView baseRef = QStringView{url}.left(url.lastIndexOf(Slash) + 1);
94 if (relative == QLatin1String(
"."))
95 return baseRef.toString();
97 QString base = baseRef + relative;
100 int length = base.size();
102 while ((index = base.indexOf(QLatin1String(
"/."), index)) != -1) {
103 if ((length > (index + 2)) && (base.at(index + 2) == Dot) &&
104 (length == (index + 3) || (base.at(index + 3) == Slash))) {
106 int previous = base.lastIndexOf(Slash, index - 1);
110 int removeLength = (index - previous) + 3;
111 base.remove(previous + 1, removeLength);
112 length -= removeLength;
114 }
else if ((length == (index + 2)) || (base.at(index + 2) == Slash)) {
116 base.remove(index, 2);
130
131
132
133
134
135
136
137
138
139
140
141
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
163
164
165
166
168QQmlError QQmlImports::moduleNotFoundError(
const QString &uri, QTypeRevision version)
171 if (version.hasMajorVersion()) {
172 error.setDescription(QQmlImports::tr(
173 "module \"%1\" version %2.%3 is not installed")
174 .arg(uri).arg(version.majorVersion())
175 .arg(version.hasMinorVersion()
176 ? QString::number(version.minorVersion())
177 : QLatin1String(
"x")));
179 error.setDescription(QQmlImports::tr(
"module \"%1\" is not installed")
185QTypeRevision QQmlImports::validVersion(QTypeRevision version)
188 return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
192
193
194void QQmlImports::setBaseUrl(
const QUrl& url,
const QString &urlString)
198 if (urlString.isEmpty())
199 m_base = url.toString();
205
206
207
208
209
212
213
214
215
216
217
218
219
220
221void QQmlImports::populateCache(QQmlTypeNameCache *cache)
const
223 const QQmlImportNamespace &set = m_unqualifiedset;
225 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
226 const QQmlImportInstance *import = set.imports.at(ii);
227 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
229 cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->version));
233 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
235 const QQmlImportNamespace &set = *ns;
238 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
239 typeimport.m_qualifier = set.prefix;
241 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
242 const QQmlImportInstance *import = set.imports.at(ii);
243 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
245 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
246 typeimport.modules.append(QQmlTypeModuleVersion(module, import->version));
255bool excludeBaseUrl(
const QString &importUrl,
const QString &fileName,
const QString &baseUrl)
257 if (importUrl.isEmpty())
260 if (baseUrl.startsWith(importUrl))
262 if (fileName == QStringView{baseUrl}.mid(importUrl.size()))
271 typedef QQmlDirComponents::const_iterator ConstIterator;
273 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
276 const QQmlDirComponents &components = import->qmlDirComponents;
279 auto shouldSkipSingleton = [importVersion](
QTypeRevision singletonVersion) ->
bool {
280 return importVersion.hasMajorVersion() &&
281 (singletonVersion.majorVersion() > importVersion.majorVersion()
282 || (singletonVersion.majorVersion() == importVersion.majorVersion()
283 && singletonVersion.minorVersion() > importVersion.minorVersion()));
286 ConstIterator cend = components.constEnd();
287 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
288 if (cit->singleton && excludeBaseUrl(import->url, cit->fileName, baseUrl.toString())) {
289 if (shouldSkipSingleton(cit->version))
291 QQmlImports::CompositeSingletonReference ref;
292 ref.typeName = cit->typeName;
293 ref.prefix = set.prefix;
294 ref.version = cit->version;
295 resultList.append(ref);
299 if (QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version)) {
300 module->walkCompositeSingletons([&resultList, &set, &shouldSkipSingleton](
const QQmlType &singleton) {
301 if (shouldSkipSingleton(singleton.version()))
303 QQmlImports::CompositeSingletonReference ref;
304 ref.typeName = singleton.elementName();
305 ref.prefix = set.prefix;
306 ref.version = singleton.version();
307 resultList.append(ref);
314
315
316
317
318
319
320
321
322QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons()
const
324 QList<QQmlImports::CompositeSingletonReference> compositeSingletons;
326 const QQmlImportNamespace &set = m_unqualifiedset;
327 findCompositeSingletons(set, compositeSingletons, baseUrl());
329 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
330 const QQmlImportNamespace &set = *ns;
331 findCompositeSingletons(set, compositeSingletons, baseUrl());
334 std::stable_sort(compositeSingletons.begin(), compositeSingletons.end(),
335 [](
const QQmlImports::CompositeSingletonReference &lhs,
336 const QQmlImports::CompositeSingletonReference &rhs) {
337 if (lhs.prefix != rhs.prefix)
338 return lhs.prefix < rhs.prefix;
340 if (lhs.typeName != rhs.typeName)
341 return lhs.typeName < rhs.typeName;
343 return lhs.version.majorVersion() != rhs.version.majorVersion()
344 ? lhs.version.majorVersion() < rhs.version.majorVersion()
345 : lhs.version.minorVersion() < rhs.version.minorVersion();
348 return compositeSingletons;
352
353
354
355
356
357QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts()
const
359 QList<QQmlImports::ScriptReference> scripts;
361 const QQmlImportNamespace &set = m_unqualifiedset;
363 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
364 const QQmlImportInstance *import = set.imports.at(ii);
366 for (
const QQmlDirParser::Script &script : import->qmlDirScripts) {
368 ref.nameSpace = script.nameSpace;
369 ref.fileName = QUrl(script.fileName);
370 ref.location = QUrl(import->url).resolved(ref.fileName);
375 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
376 const QQmlImportNamespace &set = *ns;
378 for (
int ii = set.imports.size() - 1; ii >= 0; --ii) {
379 const QQmlImportInstance *import = set.imports.at(ii);
381 for (
const QQmlDirParser::Script &script : import->qmlDirScripts) {
383 ref.nameSpace = script.nameSpace;
384 ref.qualifier = set.prefix;
385 ref.fileName = QUrl(script.fileName);
386 ref.location = QUrl(import->url).resolved(ref.fileName);
396
397
398
399
400
401
402
403
404
405QStringList QQmlImports::completeQmldirPaths(
const QString &uri,
const QStringList &basePaths,
406 QTypeRevision version)
408 QStringList paths = qQmlResolveImportPaths(uri, basePaths, version);
409 for (QString &path : paths)
410 path += Slash_qmldir;
414QString QQmlImports::versionString(QTypeRevision version, ImportVersion versionMode)
416 if (versionMode == QQmlImports::FullyVersioned) {
418 return QString::asprintf(
".%d.%d", version.majorVersion(), version.minorVersion());
419 }
else if (versionMode == QQmlImports::PartiallyVersioned) {
421 return QString::asprintf(
".%d", version.majorVersion());
427
428
429
430
431
432
433
434
435
436
437
438
439bool QQmlImports::resolveType(
440 QQmlTypeLoader *typeLoader,
const QHashedStringRef &type, QQmlType *type_return,
441 QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
442 QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
const
444 QQmlImportNamespace *ns = findQualifiedNamespace(type);
451 if (resolveType(typeLoader, type, version_return, type_return, errors, registrationType,
452 typeRecursionDetected)) {
453 if (lcQmlImport().isDebugEnabled()) {
454#define RESOLVE_TYPE_DEBUG qCDebug(lcQmlImport)
455 << "resolveType:" << qPrintable(baseUrl().toString()) << type.toString() << " => "
457 if (type_return && type_return->isValid()) {
458 if (type_return->isCompositeSingleton())
459 RESOLVE_TYPE_DEBUG << type_return->typeName() <<
' ' << type_return->sourceUrl() <<
" TYPE/URL-SINGLETON";
460 else if (type_return->isComposite())
461 RESOLVE_TYPE_DEBUG << type_return->typeName() <<
' ' << type_return->sourceUrl() <<
" TYPE/URL";
462 else if (type_return->isInlineComponentType())
463 RESOLVE_TYPE_DEBUG << type_return->typeName() <<
' ' << type_return->sourceUrl() <<
" TYPE(INLINECOMPONENT)";
467#undef RESOLVE_TYPE_DEBUG
479 Q_ASSERT(resolvedUrl.endsWith(Slash));
482 qmlDirComponents = qmldir.components();
484 const QQmlDirScripts &scripts = qmldir.scripts();
485 if (!scripts.isEmpty()) {
487 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
488 it != nameSpace->imports.constEnd(); ++it) {
489 if ((*it !=
this) && ((*it)->uri == uri)) {
491 error.setDescription(
492 QQmlImports::tr(
"\"%1\" is ambiguous. Found in %2 and in %3")
493 .arg(uri, url, (*it)->url));
494 errors->prepend(error);
499 qmlDirScripts = getVersionedScripts(scripts, version);
508 QMap<QString, QQmlDirParser::Script> versioned;
510 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
511 sit != qmldirscripts.constEnd(); ++sit) {
513 if ((!version.hasMajorVersion() || (sit->version.majorVersion() == version.majorVersion()))
514 && (!version.hasMinorVersion()
515 || (sit->version.minorVersion() <= version.minorVersion()))) {
517 const auto vit = versioned.constFind(sit->nameSpace);
518 if (vit == versioned.cend()
519 || (vit->version.minorVersion() < sit->version.minorVersion())) {
520 versioned.insert(sit->nameSpace, *sit);
525 return versioned.values();
529
530
531
532
533
534
535
536
537
538
542 const QString *base,
bool *typeRecursionDetected,
543 QQmlType::RegistrationType registrationType,
545 QList<QQmlError> *errors)
const
550 QQmlType t = uri.isEmpty() ? QQmlType() : QQmlMetaType::qmlType(type, uri, version);
553 *version_return = version;
559 const QString typeStr = type.toString();
561 Q_ASSERT(type_return);
562 bool ret = uri == typeStr;
564 Q_ASSERT(!type_return->isValid());
565 *type_return = QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(QUrl(url));
569 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
571 QString componentUrl;
572 QQmlMetaType::CompositeTypeLookupMode lookupMode = QQmlMetaType::NonSingleton;
573 QQmlDirComponents::ConstIterator candidate = end;
574 for ( ; it != end && it.key() == typeStr; ++it) {
575 const QQmlDirParser::Component &c = *it;
576 switch (registrationType) {
577 case QQmlType::AnyRegistrationType:
579 case QQmlType::CompositeSingletonType:
590 if (!version.hasMajorVersion() || (implicitlyImported && c.internal)
592 || (c.version.majorVersion() == version.majorVersion()
593 && c.version.minorVersion() <= version.minorVersion())) {
595 if ((candidate == end)
596 || (c.version.majorVersion() > candidate->version.majorVersion())
597 || ((c.version.majorVersion() == candidate->version.majorVersion())
598 && (c.version.minorVersion() > candidate->version.minorVersion()))) {
600 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
602 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
606 const bool recursion = *base == componentUrl;
607 if (typeRecursionDetected)
608 *typeRecursionDetected = recursion;
617 lookupMode = c.singleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton;
622 if (candidate != end) {
624 componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
625 QQmlType returnType = QQmlMetaType::typeForUrl(
626 typeLoader->interceptUrl(QUrl(componentUrl), QQmlAbstractUrlInterceptor::QmlFile),
627 type, lookupMode,
nullptr, candidate->version);
629 *version_return = candidate->version;
631 *type_return = returnType;
632 return returnType.isValid();
636 const QString localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
637 if (localDirectoryPath.isEmpty())
642 const QString urlsToTry[2] = {
643 typeStr + dotqml_string,
644 typeStr + dotuidotqml_string
646 for (
const QString &urlToTry : urlsToTry) {
647 if (typeLoader->fileExists(localDirectoryPath, urlToTry)) {
648 qmlUrl = url + urlToTry;
653 if (!qmlUrl.isEmpty()) {
654 const bool recursion = base && *base == qmlUrl;
655 if (typeRecursionDetected)
656 *typeRecursionDetected = recursion;
658 QQmlType returnType = QQmlMetaType::typeForUrl(
659 typeLoader->interceptUrl(QUrl(qmlUrl), QQmlAbstractUrlInterceptor::QmlFile),
660 type, registrationType == QQmlType::CompositeSingletonType
661 ? QQmlMetaType::Singleton
662 : QQmlMetaType::NonSingleton,
665 *type_return = returnType;
666 return returnType.isValid();
674bool QQmlImports::resolveType(
675 QQmlTypeLoader *typeLoader,
const QHashedStringRef &type, QTypeRevision *version_return,
676 QQmlType *type_return, QList<QQmlError> *errors,
677 QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
const
679 const QList<QHashedStringRef> splitName = type.split(Dot);
680 auto resolveTypeInNamespace = [&](
681 QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
682 QList<QQmlError> *errors) ->
bool {
683 if (nameSpace->resolveType(
684 typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
685 registrationType, typeRecursionDetected))
687 if (nameSpace->imports.size() == 1
688 && !nameSpace->imports.at(0)->isLibrary
690 && nameSpace != &m_unqualifiedset) {
692 const QString urlString = resolveLocalUrl(
693 nameSpace->imports.at(0)->url, unqualifiedtype.toString() + dotqml_string);
694 *type_return = QQmlMetaType::typeForUrl(
695 typeLoader->interceptUrl(QUrl(urlString), QQmlAbstractUrlInterceptor::QmlFile),
696 type, QQmlMetaType::NonSingleton, errors);
697 return type_return->isValid();
701 switch (splitName.size()) {
704 return resolveTypeInNamespace(type, &m_unqualifiedset, errors);
708 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
711 return resolveTypeInNamespace(splitName.at(1), s, errors);
713 if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset,
nullptr)) {
715 *type_return = QQmlMetaType::inlineComponentType(
716 *type_return, splitName.at(1).toString());
722 error.setDescription(QQmlImports::tr(
"- %1 is neither a type nor a namespace").arg(splitName.at(0).toString()));
723 errors->prepend(error);
731 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
734 error.setDescription(QQmlImports::tr(
"- %1 is not a namespace").arg(splitName.at(0).toString()));
736 if (resolveTypeInNamespace(splitName.at(1), s,
nullptr)) {
737 *type_return = QQmlMetaType::inlineComponentType(
738 *type_return, splitName.at(2).toString());
741 error.setDescription(QQmlImports::tr(
"- %1 is not a type").arg(splitName.at(1).toString()));
745 errors->prepend(error);
753 error.setDescription(QQmlImports::tr(
"- nested namespaces not allowed"));
754 errors->prepend(error);
765 const auto end = imports.cend();
767 return import->uri == moduleUri && import->version == version;
769 return it == end ?
nullptr : *it;
775 const auto end = imports.cend();
777 return import->url == location && import->version == version;
779 return it == end ?
nullptr : *it;
784 const QString *base, QList<QQmlError> *errors,
785 QQmlType::RegistrationType registrationType,
786 bool *typeRecursionDetected)
791 bool localTypeRecursionDetected =
false;
792 if (!typeRecursionDetected)
793 typeRecursionDetected = &localTypeRecursionDetected;
802 for (
int i=0; i<imports.size(); ++i) {
804 if (import->resolveType(typeLoader, type, version_return, type_return, base,
805 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
806 if (qmlCheckTypes()) {
808 for (
int j = i+1; j<imports.size(); ++j) {
810 if (import2->resolveType(typeLoader, type, version_return,
nullptr, base,
811 nullptr, registrationType)) {
813 QString u1 = import->url;
814 QString u2 = import2->url;
816 QStringView b(*base);
817 int dot = b.lastIndexOf(Dot);
820 QStringView l = b.left(dot);
821 if (u1.startsWith(b))
822 u1 = u1.mid(b.size());
824 u1 = QQmlImports::tr(
"local directory");
825 if (u2.startsWith(b))
826 u2 = u2.mid(b.size());
828 u2 = QQmlImports::tr(
"local directory");
834 error.setDescription(
836 "is ambiguous. Found in %1 and in %2")
839 error.setDescription(
841 "is ambiguous. Found in %1 in version "
844 .arg(import->version.majorVersion())
845 .arg(import->version.minorVersion())
846 .arg(import2->version.majorVersion())
847 .arg(import2->version.minorVersion()));
849 errors->prepend(error);
860 if (*typeRecursionDetected)
861 error.setDescription(QQmlImports::tr(
"is instantiated recursively"));
863 error.setDescription(QQmlImports::tr(
"is not a type"));
864 errors->prepend(error);
869QQmlImportNamespace *QQmlImports::findQualifiedNamespace(
const QHashedStringRef &prefix)
const
871 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
872 if (prefix == ns->prefix)
879
880
881QTypeRevision QQmlImports::importExtension(
882 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
883 const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
885 Q_ASSERT(qmldir->hasContent());
888 <<
"importExtension:" << qPrintable(m_base) <<
"loaded" << qmldir->qmldirLocation();
890 if (designerSupportRequired && !qmldir->designerSupported()) {
893 error.setDescription(
894 QQmlImports::tr(
"module does not support the designer \"%1\"")
895 .arg(qmldir->typeNamespace()));
896 error.setUrl(QUrl::fromLocalFile(qmldir->qmldirLocation()));
897 errors->prepend(error);
899 return QTypeRevision();
902 if (qmldir->plugins().isEmpty())
903 return validVersion(version);
905 QQmlPluginImporter importer(uri, version, qmldir, typeLoader, errors);
906 return importer.importPlugins();
909void QQmlImports::registerBuiltinModuleTypes(
910 const QQmlTypeLoaderQmldirContent &qmldir, QTypeRevision version)
912 if (!qmldir.plugins().isEmpty())
918 const QString qmldirUri = qmldir.typeNamespace();
919 if (!QQmlMetaType::typeModule(qmldirUri, version))
920 QQmlMetaType::qmlRegisterModuleTypes(qmldirUri);
923QString QQmlImports::redirectQmldirContent(
924 QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
926 const QString preferredPath = qmldir->preferredPath();
927 const QString url = preferredPath.startsWith(u':')
928 ? QStringLiteral(
"qrc") + preferredPath
929 : QUrl::fromLocalFile(preferredPath).toString();
931 QQmlTypeLoaderQmldirContent redirected
932 = typeLoader->qmldirContent(url + QLatin1String(
"qmldir"));
935 if (redirected.hasContent() && !redirected.hasError())
936 *qmldir = std::move(redirected);
941bool QQmlImports::getQmldirContent(
942 QQmlTypeLoader *typeLoader,
const QString &qmldirIdentifier,
const QString &uri,
943 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
948 *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
949 if (!qmldir->hasContent() || !qmldir->hasError())
952 errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
956QString QQmlImports::resolvedUri(
const QString &dir_arg, QQmlTypeLoader *typeLoader)
958 QString dir = dir_arg;
959 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
962 QStringList paths = typeLoader->importPathList();
963 if (!paths.isEmpty())
964 std::sort(paths.begin(), paths.end(), std::greater<QString>());
966 QString stableRelativePath = dir;
967 for (
const QString &path : std::as_const(paths)) {
968 if (dir.startsWith(path)) {
969 stableRelativePath = dir.mid(path.size()+1);
974 stableRelativePath.replace(Backslash, Slash);
977 int versionDot = stableRelativePath.lastIndexOf(Dot);
978 if (versionDot >= 0) {
979 int nextSlash = stableRelativePath.indexOf(Slash, versionDot);
981 stableRelativePath.remove(versionDot, nextSlash - versionDot);
983 stableRelativePath = stableRelativePath.left(versionDot);
986 stableRelativePath.replace(Slash, Dot);
988 return stableRelativePath;
993 QList<QQmlError> *errors)
995 int bestMajorVersion = -1;
996 quint8 lowestMinorVersion = std::numeric_limits<quint8>::max();
997 quint8 highestMinorVersion = 0;
1000 if (!newVersion.hasMajorVersion())
1002 if (!version.hasMajorVersion() || version.majorVersion() == newVersion.majorVersion()) {
1003 if (newVersion.majorVersion() > bestMajorVersion) {
1004 bestMajorVersion = newVersion.majorVersion();
1005 if (newVersion.hasMinorVersion()) {
1006 lowestMinorVersion = newVersion.minorVersion();
1007 highestMinorVersion = newVersion.minorVersion();
1009 }
else if (newVersion.majorVersion() == bestMajorVersion
1010 && newVersion.hasMinorVersion()) {
1011 lowestMinorVersion = qMin(lowestMinorVersion, newVersion.minorVersion());
1012 highestMinorVersion = qMax(highestMinorVersion, newVersion.minorVersion());
1017 typedef QQmlDirComponents::const_iterator ConstIterator;
1018 const QQmlDirComponents &components = qmldir.components();
1020 ConstIterator cend = components.constEnd();
1021 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1022 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1023 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1026 error.setDescription(
1028 "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1029 .arg(cit->typeName).arg(cit->version.majorVersion())
1030 .arg(cit->version.minorVersion()).arg(uri));
1031 errors->prepend(error);
1032 return QTypeRevision();
1036 addVersion(cit->version);
1039 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1040 const QQmlDirScripts &scripts = qmldir.scripts();
1042 SConstIterator send = scripts.constEnd();
1043 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1044 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1045 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1048 error.setDescription(QQmlImports::tr(
"\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1049 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1050 .arg(sit->version.minorVersion()).arg(uri));
1051 errors->prepend(error);
1052 return QTypeRevision();
1056 addVersion(sit->version);
1060 if (version.hasMajorVersion()
1061 && (bestMajorVersion < 0
1062 || (version.hasMinorVersion()
1063 && (lowestMinorVersion > version.minorVersion()
1064 || highestMinorVersion < version.minorVersion())))) {
1065 errors->prepend(QQmlImports::moduleNotFoundError(uri, version));
1066 return QTypeRevision();
1070 if (bestMajorVersion < 0)
1071 return QQmlImports::validVersion();
1073 return QTypeRevision::fromVersion(
1075 (version.hasMajorVersion() && version.hasMinorVersion())
1076 ? version.minorVersion()
1077 : highestMinorVersion);
1080QQmlImportNamespace *QQmlImports::importNamespace(
const QString &prefix)
1082 QQmlImportNamespace *nameSpace =
nullptr;
1084 if (prefix.isEmpty()) {
1085 nameSpace = &m_unqualifiedset;
1087 nameSpace = findQualifiedNamespace(prefix);
1090 nameSpace =
new QQmlImportNamespace;
1091 nameSpace->prefix = prefix;
1092 m_qualifiedSets.append(nameSpace);
1100 return version.isValid() ? QDebug::toString(version) : u"(latest)"_s;
1104 const QString &uri,
QTypeRevision version, QList<QQmlError> *errors)
1106 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1107 if (!matchingVersion.isValid())
1108 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1109 return matchingVersion;
1117 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1118 if (matchingVersion.isValid())
1119 return matchingVersion;
1121 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1122 if (qmldir.plugins().isEmpty()) {
1123 if (!qmldir.imports().isEmpty())
1124 return QQmlImports::validVersion();
1125 if (qmldir.hasTypeInfo())
1126 return QQmlImports::validVersion();
1128 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1129 return QTypeRevision();
1132 version = matchingQmldirVersion(qmldir, uri, version, errors);
1133 if (!version.isValid())
1134 return QTypeRevision();
1137 Q_ASSERT(version.isValid());
1141QTypeRevision QQmlImports::addLibraryImport(
1142 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1143 QTypeRevision requestedVersion,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1144 ImportFlags flags, quint8 precedence, QList<QQmlError> *errors)
1146 Q_ASSERT(typeLoader);
1149 if (lcQmlImport().isDebugEnabled()) {
1150 qCDebug(lcQmlImport)
1151 <<
"addLibraryImport:" << qPrintable(baseUrl().toString())
1152 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1155 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1156 Q_ASSERT(nameSpace);
1158 const bool noQmldir = qmldirIdentifier.isEmpty();
1159 const bool isIncomplete = (flags & QQmlImports::ImportIncomplete);
1160 if (noQmldir || isIncomplete) {
1161 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::Yes>(
1162 nameSpace, uri, qmldirUrl, requestedVersion, precedence);
1165 if (noQmldir && !isIncomplete) {
1167 if (!QQmlMetaType::typeModule(uri, requestedVersion))
1168 QQmlMetaType::qmlRegisterModuleTypes(uri);
1169 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1172 return validVersion(requestedVersion);
1175 QQmlTypeLoaderQmldirContent qmldir;
1176 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) {
1178 return QTypeRevision();
1182 if (!qmldir.hasContent())
1183 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1186 const QTypeRevision importedVersion = importExtension(
1187 typeLoader, uri, requestedVersion, &qmldir, errors);
1188 if (!importedVersion.isValid())
1189 return QTypeRevision();
1191 QString resolvedUrl;
1192 QString resolvedUri;
1193 if (qmldir.hasRedirection()) {
1194 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1195 resolvedUri = qmldir.typeNamespace();
1197 resolvedUrl = qmldirUrl;
1201 if (QQmlImportInstance *existing
1202 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion);
1203 existing && existing->isLibrary && existing->uri == resolvedUri) {
1206 nameSpace->imports.removeOne(existing);
1207 existing->precedence = std::min(precedence, existing->precedence);
1208 existing->implicitlyImported = existing->precedence >= QQmlImportInstance::Implicit;
1209 insertImport(nameSpace, existing);
1210 return finalizeLibraryImport(uri, importedVersion, qmldir, existing, errors);
1213 return finalizeImport<IsLibrary::Yes>(
1214 nameSpace, qmldir, resolvedUri, resolvedUrl, precedence, requestedVersion,
1215 importedVersion, errors, [&](QQmlImportInstance *inserted) {
1216 return finalizeLibraryImport(uri, importedVersion, qmldir, inserted, errors);
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241QTypeRevision QQmlImports::addFileImport(
1242 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1243 QTypeRevision requestedVersion, ImportFlags flags, quint8 precedence, QString *localQmldir,
1244 QList<QQmlError> *errors)
1246 Q_ASSERT(typeLoader);
1249 if (lcQmlImport().isDebugEnabled()) {
1250 qCDebug(lcQmlImport)
1251 <<
"addFileImport:" << qPrintable(baseUrl().toString())
1252 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1255 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1257 const QString fix = uri.startsWith(Slash) ? QLatin1String(
"file:") + uri
1258 : QLatin1String(
"qrc") + uri;
1259 error.setDescription(QQmlImports::tr(
1260 "\"%1\" is not a valid import URL. "
1261 "You can pass relative paths or URLs with schema, but not "
1262 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1263 errors->prepend(error);
1264 return QTypeRevision();
1269 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1270 Q_ASSERT(nameSpace);
1274 QString importUri = uri;
1275 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1278 qmldirUrl = typeLoader->interceptUrl(
1279 QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
1280 QString qmldirIdentifier;
1282 if (QQmlFile::isLocalFile(qmldirUrl)) {
1284 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1285 Q_ASSERT(!localFileOrQrc.isEmpty());
1287 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1288 if (!typeLoader->directoryExists(dir)) {
1289 if (precedence < QQmlImportInstance::Implicit) {
1291 error.setDescription(QQmlImports::tr(
"\"%1\": no such directory").arg(uri));
1292 error.setUrl(QUrl(qmldirUrl));
1293 errors->prepend(error);
1295 return QTypeRevision();
1300 importUri = resolvedUri(dir, typeLoader);
1301 if (importUri.endsWith(Slash))
1304 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
1305 qmldirIdentifier = std::move(localFileOrQrc);
1307 *localQmldir = qmldirIdentifier;
1310 }
else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1312 if (precedence < QQmlImportInstance::Implicit) {
1314 error.setDescription(QQmlImports::tr(
"import \"%1\" has no qmldir and no namespace").arg(importUri));
1315 error.setUrl(QUrl(qmldirUrl));
1316 errors->prepend(error);
1319 return QTypeRevision();
1324 QString url = resolveLocalUrl(m_base, uri);
1325 if (url.isEmpty()) {
1327 error.setDescription(
1328 QQmlImports::tr(
"Cannot resolve URL for import \"%1\"").arg(uri));
1329 error.setUrl(m_baseUrl);
1330 errors->prepend(error);
1331 return QTypeRevision();
1334 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1341 if (precedence >= QQmlImportInstance::Implicit) {
1342 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
1343 it != nameSpace->imports.constEnd(); ++it) {
1344 if ((*it)->url == url) {
1345 (*it)->implicitlyImported =
true;
1346 return validVersion(requestedVersion);
1351 if ((flags & QQmlImports::ImportIncomplete) || qmldirIdentifier.isEmpty()) {
1352 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1353 nameSpace, importUri, url, requestedVersion, precedence);
1355 return validVersion(requestedVersion);
1358 QQmlTypeLoaderQmldirContent qmldir;
1359 if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
1360 return QTypeRevision();
1362 if (!qmldir.hasContent()) {
1363 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1364 nameSpace, importUri, url, requestedVersion, precedence);
1366 return validVersion(requestedVersion);
1370 if (qmldir.hasTypeNamespace())
1371 importUri = qmldir.typeNamespace();
1374 const QTypeRevision importedVersion
1375 = importExtension(typeLoader, importUri, requestedVersion, &qmldir, errors);
1376 if (!importedVersion.isValid())
1377 return QTypeRevision();
1379 if (qmldir.hasRedirection()) {
1380 const QString resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1381 importUri = qmldir.typeNamespace();
1382 if (resolvedUrl != url) {
1383 if (QQmlImportInstance *existing
1384 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) {
1386 return validVersion(existing->version);
1390 return finalizeImport<IsLibrary::No>(
1391 nameSpace, qmldir, importUri, resolvedUrl, precedence, requestedVersion,
1392 importedVersion, errors);
1395 return finalizeImport<IsLibrary::No>(
1396 nameSpace, qmldir, importUri, url, precedence, requestedVersion,
1397 importedVersion, errors);
1402 if (errors->isEmpty()) {
1404 error.setDescription(QQmlTypeLoader::tr(
"Cannot update qmldir content for '%1'").arg(uri));
1405 errors->prepend(error);
1407 return QTypeRevision();
1410QTypeRevision QQmlImports::updateQmldirContent(
1411 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
1412 const QString &prefix,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1413 QList<QQmlError> *errors)
1415 Q_ASSERT(typeLoader);
1418 qCDebug(lcQmlImport)
1419 <<
"updateQmldirContent:" << qPrintable(baseUrl().toString())
1420 << uri <<
"to" << qmldirUrl <<
"as" << prefix;
1422 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1423 Q_ASSERT(nameSpace);
1425 QQmlImportInstance *import = nameSpace->findImportByModuleUri(uri, version);
1427 return qmldirContentError(uri, errors);
1429 QQmlTypeLoaderQmldirContent qmldir;
1430 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1431 return QTypeRevision();
1433 if (!qmldir.hasContent())
1434 return qmldirContentError(uri, errors);
1437 version = importExtension(typeLoader, uri, import->version, &qmldir, errors);
1438 if (!version.isValid())
1439 return QTypeRevision();
1441 QString resolvedUrl;
1442 if (qmldir.hasRedirection()) {
1443 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1444 if (resolvedUrl != import->url) {
1445 if (QQmlImportInstance *existing
1446 = nameSpace->findImportByLocation(resolvedUrl, import->version)) {
1449 nameSpace->imports.removeOne(import);
1451 return validVersion(existing->version);
1453 import->url = resolvedUrl;
1455 import->uri = qmldir.typeNamespace();
1457 resolvedUrl = qmldirUrl;
1460 registerBuiltinModuleTypes(qmldir, version);
1462 if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
1463 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1465 if (uri != QLatin1String(
".")
1466 && !QQmlMetaType::matchingModuleVersion(import->uri, version).isValid()) {
1467 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1468 return QTypeRevision();
1472 version = matchingQmldirVersion(qmldir, import->uri, version, errors);
1473 if (!version.isValid())
1474 return QTypeRevision();
1476 return validVersion(version);
1479 return qmldirContentError(uri, errors);
1483
1484
1485
1486
1487
1488
1489
1490
1494
1495
1496bool QQmlImports::addInlineComponentImport(
1497 QQmlImportInstance *
const importInstance,
const QString &name,
const QUrl &importUrl)
1499 importInstance->url = importUrl.toString();
1500 importInstance->uri = name;
1501 importInstance->isInlineComponent =
true;
1502 importInstance->version = QTypeRevision::zero();
1503 m_unqualifiedset.imports.push_back(importInstance);
1504 m_unqualifiedset.setNeedsSorting(
true);
1508QUrl QQmlImports::urlFromLocalFileOrQrcOrUrl(
const QString &file)
1510 QUrl url(QLatin1String(file.at(0) == Colon ?
"qrc" :
"") + file);
1513 if (url.scheme().size() < 2)
1514 return QUrl::fromLocalFile(file);
1518void QQmlImports::setDesignerSupportRequired(
bool b)
1520 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)