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
547 QQmlType t = QQmlMetaType::qmlType(type, uri, version);
550 *version_return = version;
556 const QString typeStr = type.toString();
558 Q_ASSERT(type_return);
559 bool ret = uri == typeStr;
561 Q_ASSERT(!type_return->isValid());
562 *type_return = QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(QUrl(url));
566 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
568 QString componentUrl;
569 QQmlMetaType::CompositeTypeLookupMode lookupMode = QQmlMetaType::NonSingleton;
570 QQmlDirComponents::ConstIterator candidate = end;
571 for ( ; it != end && it.key() == typeStr; ++it) {
572 const QQmlDirParser::Component &c = *it;
573 switch (registrationType) {
574 case QQmlType::AnyRegistrationType:
576 case QQmlType::CompositeSingletonType:
587 if (!version.hasMajorVersion() || (implicitlyImported && c.internal)
589 || (c.version.majorVersion() == version.majorVersion()
590 && c.version.minorVersion() <= version.minorVersion())) {
592 if ((candidate == end)
593 || (c.version.majorVersion() > candidate->version.majorVersion())
594 || ((c.version.majorVersion() == candidate->version.majorVersion())
595 && (c.version.minorVersion() > candidate->version.minorVersion()))) {
597 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
599 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
603 const bool recursion = *base == componentUrl;
604 if (typeRecursionDetected)
605 *typeRecursionDetected = recursion;
614 lookupMode = c.singleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton;
619 if (candidate != end) {
621 componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
622 QQmlType returnType = QQmlMetaType::typeForUrl(
623 typeLoader->interceptUrl(QUrl(componentUrl), QQmlAbstractUrlInterceptor::QmlFile),
624 type, lookupMode,
nullptr, candidate->version);
626 *version_return = candidate->version;
628 *type_return = returnType;
629 return returnType.isValid();
633 const QString localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
634 if (localDirectoryPath.isEmpty())
639 const QString urlsToTry[2] = {
640 typeStr + dotqml_string,
641 typeStr + dotuidotqml_string
643 for (
const QString &urlToTry : urlsToTry) {
644 if (typeLoader->fileExists(localDirectoryPath, urlToTry)) {
645 qmlUrl = url + urlToTry;
650 if (!qmlUrl.isEmpty()) {
651 const bool recursion = base && *base == qmlUrl;
652 if (typeRecursionDetected)
653 *typeRecursionDetected = recursion;
655 QQmlType returnType = QQmlMetaType::typeForUrl(
656 typeLoader->interceptUrl(QUrl(qmlUrl), QQmlAbstractUrlInterceptor::QmlFile),
657 type, registrationType == QQmlType::CompositeSingletonType
658 ? QQmlMetaType::Singleton
659 : QQmlMetaType::NonSingleton,
662 *type_return = returnType;
663 return returnType.isValid();
671bool QQmlImports::resolveType(
672 QQmlTypeLoader *typeLoader,
const QHashedStringRef &type, QTypeRevision *version_return,
673 QQmlType *type_return, QList<QQmlError> *errors,
674 QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
const
676 const QVector<QHashedStringRef> splitName = type.split(Dot);
677 auto resolveTypeInNamespace = [&](
678 QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
679 QList<QQmlError> *errors) ->
bool {
680 if (nameSpace->resolveType(
681 typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
682 registrationType, typeRecursionDetected))
684 if (nameSpace->imports.size() == 1
685 && !nameSpace->imports.at(0)->isLibrary
687 && nameSpace != &m_unqualifiedset) {
689 const QString urlString = resolveLocalUrl(
690 nameSpace->imports.at(0)->url, unqualifiedtype.toString() + dotqml_string);
691 *type_return = QQmlMetaType::typeForUrl(
692 typeLoader->interceptUrl(QUrl(urlString), QQmlAbstractUrlInterceptor::QmlFile),
693 type, QQmlMetaType::NonSingleton, errors);
694 return type_return->isValid();
698 switch (splitName.size()) {
701 return resolveTypeInNamespace(type, &m_unqualifiedset, errors);
705 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
708 return resolveTypeInNamespace(splitName.at(1), s, errors);
710 if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset,
nullptr)) {
712 *type_return = QQmlMetaType::inlineComponentType(
713 *type_return, splitName.at(1).toString());
719 error.setDescription(QQmlImports::tr(
"- %1 is neither a type nor a namespace").arg(splitName.at(0).toString()));
720 errors->prepend(error);
728 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
731 error.setDescription(QQmlImports::tr(
"- %1 is not a namespace").arg(splitName.at(0).toString()));
733 if (resolveTypeInNamespace(splitName.at(1), s,
nullptr)) {
734 *type_return = QQmlMetaType::inlineComponentType(
735 *type_return, splitName.at(2).toString());
738 error.setDescription(QQmlImports::tr(
"- %1 is not a type").arg(splitName.at(1).toString()));
742 errors->prepend(error);
750 error.setDescription(QQmlImports::tr(
"- nested namespaces not allowed"));
751 errors->prepend(error);
762 const auto end = imports.cend();
763 const auto it = std::find_if(imports.cbegin(), end, [&](
const QQmlImportInstance *import) {
764 return import->uri == moduleUri && import->version == version;
766 return it == end ?
nullptr : *it;
772 const auto end = imports.cend();
773 const auto it = std::find_if(imports.cbegin(), end, [&](
const QQmlImportInstance *import) {
774 return import->url == location && import->version == version;
776 return it == end ?
nullptr : *it;
781 const QString *base, QList<QQmlError> *errors,
782 QQmlType::RegistrationType registrationType,
783 bool *typeRecursionDetected)
788 bool localTypeRecursionDetected =
false;
789 if (!typeRecursionDetected)
790 typeRecursionDetected = &localTypeRecursionDetected;
794 std::stable_partition(imports.begin(), imports.end(), [](QQmlImportInstance *import) {
795 return import->isInlineComponent;
799 for (
int i=0; i<imports.size(); ++i) {
801 if (import->resolveType(typeLoader, type, version_return, type_return, base,
802 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
803 if (qmlCheckTypes()) {
805 for (
int j = i+1; j<imports.size(); ++j) {
807 if (import2->resolveType(typeLoader, type, version_return,
nullptr, base,
808 nullptr, registrationType)) {
810 QString u1 = import->url;
811 QString u2 = import2->url;
813 QStringView b(*base);
814 int dot = b.lastIndexOf(Dot);
817 QStringView l = b.left(dot);
818 if (u1.startsWith(b))
819 u1 = u1.mid(b.size());
821 u1 = QQmlImports::tr(
"local directory");
822 if (u2.startsWith(b))
823 u2 = u2.mid(b.size());
825 u2 = QQmlImports::tr(
"local directory");
831 error.setDescription(
833 "is ambiguous. Found in %1 and in %2")
836 error.setDescription(
838 "is ambiguous. Found in %1 in version "
841 .arg(import->version.majorVersion())
842 .arg(import->version.minorVersion())
843 .arg(import2->version.majorVersion())
844 .arg(import2->version.minorVersion()));
846 errors->prepend(error);
857 if (*typeRecursionDetected)
858 error.setDescription(QQmlImports::tr(
"is instantiated recursively"));
860 error.setDescription(QQmlImports::tr(
"is not a type"));
861 errors->prepend(error);
866QQmlImportNamespace *QQmlImports::findQualifiedNamespace(
const QHashedStringRef &prefix)
const
868 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
869 if (prefix == ns->prefix)
876
877
878QTypeRevision QQmlImports::importExtension(
879 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
880 const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
882 Q_ASSERT(qmldir->hasContent());
885 <<
"importExtension:" << qPrintable(m_base) <<
"loaded" << qmldir->qmldirLocation();
887 if (designerSupportRequired && !qmldir->designerSupported()) {
890 error.setDescription(
891 QQmlImports::tr(
"module does not support the designer \"%1\"")
892 .arg(qmldir->typeNamespace()));
893 error.setUrl(QUrl::fromLocalFile(qmldir->qmldirLocation()));
894 errors->prepend(error);
896 return QTypeRevision();
899 if (qmldir->plugins().isEmpty())
900 return validVersion(version);
902 QQmlPluginImporter importer(uri, version, qmldir, typeLoader, errors);
903 return importer.importPlugins();
906void QQmlImports::registerBuiltinModuleTypes(
907 const QQmlTypeLoaderQmldirContent &qmldir, QTypeRevision version)
909 if (!qmldir.plugins().isEmpty())
915 const QString qmldirUri = qmldir.typeNamespace();
916 if (!QQmlMetaType::typeModule(qmldirUri, version))
917 QQmlMetaType::qmlRegisterModuleTypes(qmldirUri);
920QString QQmlImports::redirectQmldirContent(
921 QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
923 const QString preferredPath = qmldir->preferredPath();
924 const QString url = preferredPath.startsWith(u':')
925 ? QStringLiteral(
"qrc") + preferredPath
926 : QUrl::fromLocalFile(preferredPath).toString();
928 QQmlTypeLoaderQmldirContent redirected
929 = typeLoader->qmldirContent(url + QLatin1String(
"qmldir"));
932 if (redirected.hasContent() && !redirected.hasError())
933 *qmldir = std::move(redirected);
938bool QQmlImports::getQmldirContent(
939 QQmlTypeLoader *typeLoader,
const QString &qmldirIdentifier,
const QString &uri,
940 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
945 *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
946 if (!qmldir->hasContent() || !qmldir->hasError())
949 errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
953QString QQmlImports::resolvedUri(
const QString &dir_arg, QQmlTypeLoader *typeLoader)
955 QString dir = dir_arg;
956 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
959 QStringList paths = typeLoader->importPathList();
960 if (!paths.isEmpty())
961 std::sort(paths.begin(), paths.end(), std::greater<QString>());
963 QString stableRelativePath = dir;
964 for (
const QString &path : std::as_const(paths)) {
965 if (dir.startsWith(path)) {
966 stableRelativePath = dir.mid(path.size()+1);
971 stableRelativePath.replace(Backslash, Slash);
974 int versionDot = stableRelativePath.lastIndexOf(Dot);
975 if (versionDot >= 0) {
976 int nextSlash = stableRelativePath.indexOf(Slash, versionDot);
978 stableRelativePath.remove(versionDot, nextSlash - versionDot);
980 stableRelativePath = stableRelativePath.left(versionDot);
983 stableRelativePath.replace(Slash, Dot);
985 return stableRelativePath;
990 QList<QQmlError> *errors)
992 int bestMajorVersion = -1;
993 quint8 lowestMinorVersion = std::numeric_limits<quint8>::max();
994 quint8 highestMinorVersion = 0;
997 if (!newVersion.hasMajorVersion())
999 if (!version.hasMajorVersion() || version.majorVersion() == newVersion.majorVersion()) {
1000 if (newVersion.majorVersion() > bestMajorVersion) {
1001 bestMajorVersion = newVersion.majorVersion();
1002 if (newVersion.hasMinorVersion()) {
1003 lowestMinorVersion = newVersion.minorVersion();
1004 highestMinorVersion = newVersion.minorVersion();
1006 }
else if (newVersion.majorVersion() == bestMajorVersion
1007 && newVersion.hasMinorVersion()) {
1008 lowestMinorVersion = qMin(lowestMinorVersion, newVersion.minorVersion());
1009 highestMinorVersion = qMax(highestMinorVersion, newVersion.minorVersion());
1014 typedef QQmlDirComponents::const_iterator ConstIterator;
1015 const QQmlDirComponents &components = qmldir.components();
1017 ConstIterator cend = components.constEnd();
1018 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1019 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1020 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1023 error.setDescription(
1025 "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1026 .arg(cit->typeName).arg(cit->version.majorVersion())
1027 .arg(cit->version.minorVersion()).arg(uri));
1028 errors->prepend(error);
1029 return QTypeRevision();
1033 addVersion(cit->version);
1036 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1037 const QQmlDirScripts &scripts = qmldir.scripts();
1039 SConstIterator send = scripts.constEnd();
1040 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1041 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1042 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1045 error.setDescription(QQmlImports::tr(
"\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1046 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1047 .arg(sit->version.minorVersion()).arg(uri));
1048 errors->prepend(error);
1049 return QTypeRevision();
1053 addVersion(sit->version);
1057 if (version.hasMajorVersion()
1058 && (bestMajorVersion < 0
1059 || (version.hasMinorVersion()
1060 && (lowestMinorVersion > version.minorVersion()
1061 || highestMinorVersion < version.minorVersion())))) {
1062 errors->prepend(QQmlImports::moduleNotFoundError(uri, version));
1063 return QTypeRevision();
1067 if (bestMajorVersion < 0)
1068 return QQmlImports::validVersion();
1070 return QTypeRevision::fromVersion(
1072 (version.hasMajorVersion() && version.hasMinorVersion())
1073 ? version.minorVersion()
1074 : highestMinorVersion);
1077QQmlImportNamespace *QQmlImports::importNamespace(
const QString &prefix)
1079 QQmlImportNamespace *nameSpace =
nullptr;
1081 if (prefix.isEmpty()) {
1082 nameSpace = &m_unqualifiedset;
1084 nameSpace = findQualifiedNamespace(prefix);
1087 nameSpace =
new QQmlImportNamespace;
1088 nameSpace->prefix = prefix;
1089 m_qualifiedSets.append(nameSpace);
1097 return version.isValid() ? QDebug::toString(version) : u"(latest)"_s;
1101 const QString &uri,
QTypeRevision version, QList<QQmlError> *errors)
1103 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1104 if (!matchingVersion.isValid())
1105 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1106 return matchingVersion;
1114 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1115 if (matchingVersion.isValid())
1116 return matchingVersion;
1118 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1119 if (qmldir.plugins().isEmpty()) {
1120 if (!qmldir.imports().isEmpty())
1121 return QQmlImports::validVersion();
1122 if (qmldir.hasTypeInfo())
1123 return QQmlImports::validVersion();
1125 errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version)));
1126 return QTypeRevision();
1129 version = matchingQmldirVersion(qmldir, uri, version, errors);
1130 if (!version.isValid())
1131 return QTypeRevision();
1134 Q_ASSERT(version.isValid());
1138QTypeRevision QQmlImports::addLibraryImport(
1139 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1140 QTypeRevision requestedVersion,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1141 ImportFlags flags, quint8 precedence, QList<QQmlError> *errors)
1143 Q_ASSERT(typeLoader);
1146 if (lcQmlImport().isDebugEnabled()) {
1147 qCDebug(lcQmlImport)
1148 <<
"addLibraryImport:" << qPrintable(baseUrl().toString())
1149 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1152 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1153 Q_ASSERT(nameSpace);
1155 const bool noQmldir = qmldirIdentifier.isEmpty();
1156 const bool isIncomplete = (flags & QQmlImports::ImportIncomplete);
1157 if (noQmldir || isIncomplete) {
1158 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::Yes>(
1159 nameSpace, uri, qmldirUrl, requestedVersion, precedence);
1162 if (noQmldir && !isIncomplete) {
1164 if (!QQmlMetaType::typeModule(uri, requestedVersion))
1165 QQmlMetaType::qmlRegisterModuleTypes(uri);
1166 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1169 return validVersion(requestedVersion);
1172 QQmlTypeLoaderQmldirContent qmldir;
1173 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) {
1175 return QTypeRevision();
1179 if (!qmldir.hasContent())
1180 return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors);
1183 const QTypeRevision importedVersion = importExtension(
1184 typeLoader, uri, requestedVersion, &qmldir, errors);
1185 if (!importedVersion.isValid())
1186 return QTypeRevision();
1188 QString resolvedUrl;
1189 QString resolvedUri;
1190 if (qmldir.hasRedirection()) {
1191 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1192 resolvedUri = qmldir.typeNamespace();
1194 resolvedUrl = qmldirUrl;
1198 if (QQmlImportInstance *existing
1199 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion);
1200 existing && existing->isLibrary && existing->uri == resolvedUri) {
1203 nameSpace->imports.removeOne(existing);
1204 existing->precedence = std::min(precedence, existing->precedence);
1205 existing->implicitlyImported = existing->precedence >= QQmlImportInstance::Implicit;
1206 insertImport(nameSpace, existing);
1207 return finalizeLibraryImport(uri, importedVersion, qmldir, existing, errors);
1210 return finalizeImport<IsLibrary::Yes>(
1211 nameSpace, qmldir, resolvedUri, resolvedUrl, precedence, requestedVersion,
1212 importedVersion, errors, [&](QQmlImportInstance *inserted) {
1213 return finalizeLibraryImport(uri, importedVersion, qmldir, inserted, errors);
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238QTypeRevision QQmlImports::addFileImport(
1239 QQmlTypeLoader *typeLoader,
const QString &uri,
const QString &prefix,
1240 QTypeRevision requestedVersion, ImportFlags flags, quint8 precedence, QString *localQmldir,
1241 QList<QQmlError> *errors)
1243 Q_ASSERT(typeLoader);
1246 if (lcQmlImport().isDebugEnabled()) {
1247 qCDebug(lcQmlImport)
1248 <<
"addFileImport:" << qPrintable(baseUrl().toString())
1249 << uri <<
"version" << getVersionInfo(requestedVersion) <<
"as" << prefix;
1252 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1254 const QString fix = uri.startsWith(Slash) ? QLatin1String(
"file:") + uri
1255 : QLatin1String(
"qrc") + uri;
1256 error.setDescription(QQmlImports::tr(
1257 "\"%1\" is not a valid import URL. "
1258 "You can pass relative paths or URLs with schema, but not "
1259 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1260 errors->prepend(error);
1261 return QTypeRevision();
1266 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1267 Q_ASSERT(nameSpace);
1271 QString importUri = uri;
1272 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1275 qmldirUrl = typeLoader->interceptUrl(
1276 QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
1277 QString qmldirIdentifier;
1279 if (QQmlFile::isLocalFile(qmldirUrl)) {
1281 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1282 Q_ASSERT(!localFileOrQrc.isEmpty());
1284 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1285 if (!typeLoader->directoryExists(dir)) {
1286 if (precedence < QQmlImportInstance::Implicit) {
1288 error.setDescription(QQmlImports::tr(
"\"%1\": no such directory").arg(uri));
1289 error.setUrl(QUrl(qmldirUrl));
1290 errors->prepend(error);
1292 return QTypeRevision();
1297 importUri = resolvedUri(dir, typeLoader);
1298 if (importUri.endsWith(Slash))
1301 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
1302 qmldirIdentifier = std::move(localFileOrQrc);
1304 *localQmldir = qmldirIdentifier;
1307 }
else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1309 if (precedence < QQmlImportInstance::Implicit) {
1311 error.setDescription(QQmlImports::tr(
"import \"%1\" has no qmldir and no namespace").arg(importUri));
1312 error.setUrl(QUrl(qmldirUrl));
1313 errors->prepend(error);
1316 return QTypeRevision();
1321 QString url = resolveLocalUrl(m_base, uri);
1322 if (url.isEmpty()) {
1324 error.setDescription(
1325 QQmlImports::tr(
"Cannot resolve URL for import \"%1\"").arg(uri));
1326 error.setUrl(m_baseUrl);
1327 errors->prepend(error);
1328 return QTypeRevision();
1331 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1338 if (precedence >= QQmlImportInstance::Implicit) {
1339 for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin();
1340 it != nameSpace->imports.constEnd(); ++it) {
1341 if ((*it)->url == url) {
1342 (*it)->implicitlyImported =
true;
1343 return validVersion(requestedVersion);
1348 if ((flags & QQmlImports::ImportIncomplete) || qmldirIdentifier.isEmpty()) {
1349 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1350 nameSpace, importUri, url, requestedVersion, precedence);
1352 return validVersion(requestedVersion);
1355 QQmlTypeLoaderQmldirContent qmldir;
1356 if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
1357 return QTypeRevision();
1359 if (!qmldir.hasContent()) {
1360 QQmlImportInstance *inserted = addImportToNamespace<IsLibrary::No>(
1361 nameSpace, importUri, url, requestedVersion, precedence);
1363 return validVersion(requestedVersion);
1367 if (qmldir.hasTypeNamespace())
1368 importUri = qmldir.typeNamespace();
1371 const QTypeRevision importedVersion
1372 = importExtension(typeLoader, importUri, requestedVersion, &qmldir, errors);
1373 if (!importedVersion.isValid())
1374 return QTypeRevision();
1376 if (qmldir.hasRedirection()) {
1377 const QString resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1378 importUri = qmldir.typeNamespace();
1379 if (resolvedUrl != url) {
1380 if (QQmlImportInstance *existing
1381 = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) {
1383 return validVersion(existing->version);
1387 return finalizeImport<IsLibrary::No>(
1388 nameSpace, qmldir, importUri, resolvedUrl, precedence, requestedVersion,
1389 importedVersion, errors);
1392 return finalizeImport<IsLibrary::No>(
1393 nameSpace, qmldir, importUri, url, precedence, requestedVersion,
1394 importedVersion, errors);
1399 if (errors->isEmpty()) {
1401 error.setDescription(QQmlTypeLoader::tr(
"Cannot update qmldir content for '%1'").arg(uri));
1402 errors->prepend(error);
1404 return QTypeRevision();
1407QTypeRevision QQmlImports::updateQmldirContent(
1408 QQmlTypeLoader *typeLoader,
const QString &uri, QTypeRevision version,
1409 const QString &prefix,
const QString &qmldirIdentifier,
const QString &qmldirUrl,
1410 QList<QQmlError> *errors)
1412 Q_ASSERT(typeLoader);
1415 qCDebug(lcQmlImport)
1416 <<
"updateQmldirContent:" << qPrintable(baseUrl().toString())
1417 << uri <<
"to" << qmldirUrl <<
"as" << prefix;
1419 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1420 Q_ASSERT(nameSpace);
1422 QQmlImportInstance *import = nameSpace->findImportByModuleUri(uri, version);
1424 return qmldirContentError(uri, errors);
1426 QQmlTypeLoaderQmldirContent qmldir;
1427 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1428 return QTypeRevision();
1430 if (!qmldir.hasContent())
1431 return qmldirContentError(uri, errors);
1434 version = importExtension(typeLoader, uri, import->version, &qmldir, errors);
1435 if (!version.isValid())
1436 return QTypeRevision();
1438 QString resolvedUrl;
1439 if (qmldir.hasRedirection()) {
1440 resolvedUrl = redirectQmldirContent(typeLoader, &qmldir);
1441 if (resolvedUrl != import->url) {
1442 if (QQmlImportInstance *existing
1443 = nameSpace->findImportByLocation(resolvedUrl, import->version)) {
1446 nameSpace->imports.removeOne(import);
1448 return validVersion(existing->version);
1450 import->url = resolvedUrl;
1452 import->uri = qmldir.typeNamespace();
1454 resolvedUrl = qmldirUrl;
1457 registerBuiltinModuleTypes(qmldir, version);
1459 if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
1460 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1462 if (uri != QLatin1String(
".")
1463 && !QQmlMetaType::matchingModuleVersion(import->uri, version).isValid()) {
1464 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1465 return QTypeRevision();
1469 version = matchingQmldirVersion(qmldir, import->uri, version, errors);
1470 if (!version.isValid())
1471 return QTypeRevision();
1473 return validVersion(version);
1476 return qmldirContentError(uri, errors);
1480
1481
1482
1483
1484
1485
1486
1487
1491
1492
1493bool QQmlImports::addInlineComponentImport(
1494 QQmlImportInstance *
const importInstance,
const QString &name,
const QUrl &importUrl)
1496 importInstance->url = importUrl.toString();
1497 importInstance->uri = name;
1498 importInstance->isInlineComponent =
true;
1499 importInstance->version = QTypeRevision::zero();
1500 m_unqualifiedset.imports.push_back(importInstance);
1501 m_unqualifiedset.setNeedsSorting(
true);
1505QUrl QQmlImports::urlFromLocalFileOrQrcOrUrl(
const QString &file)
1507 QUrl url(QLatin1String(file.at(0) == Colon ?
"qrc" :
"") + file);
1510 if (url.scheme().size() < 2)
1511 return QUrl::fromLocalFile(file);
1515void QQmlImports::setDesignerSupportRequired(
bool b)
1517 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)