12#include <QtQml/private/qqmlimportresolver_p.h>
14#include <QtCore/qfileinfo.h>
15#include <QtCore/qdiriterator.h>
19using namespace Qt::StringLiterals;
28 m_prefix(std::move(prefix)),
29 m_name(std::move(name)),
32 m_isDependency(isDependency)
38 return !m_name.isEmpty();
43 Q_ASSERT(!prefix.endsWith(u'.'));
44 return prefix.isEmpty() ? name : (prefix + QLatin1Char(
'.') + name);
49 if (path.endsWith(SlashQmldir))
51 if (path.endsWith(u'/'))
52 return path +
"qmldir"_L1;
53 return path + SlashQmldir;
56QQmlDirParser QQmlJSImporter::createQmldirParserForFile(
const QString &filename, Import *import)
61 if (f.open(QFile::ReadOnly)) {
62 parser.parse(QString::fromUtf8(f.readAll()));
63 import->warnings.append(parser.errors(filename));
65 import->warnings.append({
66 QStringLiteral(
"Could not open qmldir file: ") + filename,
68 QQmlJS::SourceLocation()
75void QQmlJSImporter::readQmltypes(
const QString &filename, Import *result)
77 const QFileInfo fileInfo(filename);
78 if (!fileInfo.exists()) {
79 result->warnings.append({
80 QStringLiteral(
"QML types file does not exist: ") + filename,
82 QQmlJS::SourceLocation()
87 if (fileInfo.isDir()) {
88 result->warnings.append({
89 QStringLiteral(
"QML types file cannot be a directory: ") + filename,
91 QQmlJS::SourceLocation()
97 if (!file.open(QFile::ReadOnly)) {
98 result->warnings.append({
99 QStringLiteral(
"QML types file cannot be opened: ") + filename,
101 QQmlJS::SourceLocation()
106 QQmlJSTypeDescriptionReader reader { filename, QString::fromUtf8(file.readAll()) };
107 QStringList dependencyStrings;
108 auto succ = reader(&result->objects, &dependencyStrings);
110 result->warnings.append({ reader.errorMessage(), QtCriticalMsg, QQmlJS::SourceLocation() });
112 const QString warningMessage = reader.warningMessage();
113 if (!warningMessage.isEmpty())
114 result->warnings.append({ warningMessage, QtWarningMsg, QQmlJS::SourceLocation() });
116 if (dependencyStrings.isEmpty())
119 result->warnings.append({
120 QStringLiteral(
"Found deprecated dependency specifications in %1."
121 "Specify dependencies in qmldir and use qmltyperegistrar "
122 "to generate qmltypes files without dependencies.")
125 QQmlJS::SourceLocation()
128 for (
const QString &dependency : std::as_const(dependencyStrings)) {
129 const auto blank = dependency.indexOf(u' ');
131 result->dependencies.append(
132 QQmlDirParser::Import(dependency, {}, QQmlDirParser::Import::Default));
136 const QString module = dependency.left(blank);
137 const QString versionString = dependency.mid(blank + 1).trimmed();
138 if (versionString == QStringLiteral(
"auto")) {
139 result->dependencies.append(
140 QQmlDirParser::Import(module, {}, QQmlDirParser::Import::Auto));
144 const auto dot = versionString.indexOf(u'.');
146 const QTypeRevision version = dot < 0
147 ? QTypeRevision::fromMajorVersion(versionString.toUShort())
148 : QTypeRevision::fromVersion(versionString.left(dot).toUShort(),
149 versionString.mid(dot + 1).toUShort());
151 result->dependencies.append(
152 QQmlDirParser::Import(module, version, QQmlDirParser::Import::Default));
158 if (
const auto *factory = scope.factory())
159 return factory->internalName();
160 return scope->internalName();
166 return scope.factory() || scope->isComposite();
171 return isComposite(scope)
177
178
179
180QQmlJSImporter::QQmlJSImporter(
const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
181 QQmlJSImporterFlags flags)
182 : m_importPaths(importPaths),
185 m_importVisitor([](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
186 const ImportVisitorPrerequisites &p) {
187 auto visitor = std::unique_ptr<QQmlJS::AST::BaseVisitor>(
new QQmlJSImportVisitor(
188 self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles));
189 QQmlJS::AST::Node::accept(rootNode, visitor.get());
195 const QString &qmldirPath,
const QString &prefer, QQmlJSResourceFileMapper *mapper)
197 if (prefer.isEmpty())
200 if (!prefer.endsWith(u'/')) {
201 qWarning() <<
"Ignoring invalid prefer path" << prefer <<
"(has to end with slash)";
205 if (prefer.startsWith(u':')) {
210 Q_ASSERT(prefer.endsWith(u'/'));
211 const auto entry = mapper->entry(
212 QQmlJSResourceFileMapper::resourceFileFilter(ensureSlashQmldir(prefer.mid(1))));
216 return entry.filePath.endsWith(SlashQmldir)
222 const QFileInfo f(ensureSlashQmldir(prefer));
223 const QString canonical = f.canonicalFilePath();
224 if (canonical.isEmpty())
229QQmlJSImporter::Import QQmlJSImporter::readQmldir(
const QString &modulePath)
232 const QString moduleQmldirPath = ensureSlashQmldir(modulePath);
233 auto reader = createQmldirParserForFile(moduleQmldirPath, &result);
235 const QString resolvedQmldirPath
236 = resolvePreferredPath(moduleQmldirPath, reader.preferredPath(), m_mapper);
237 if (resolvedQmldirPath != moduleQmldirPath)
238 reader = createQmldirParserForFile(resolvedQmldirPath, &result);
241 Q_ASSERT(resolvedQmldirPath.endsWith(SlashQmldir));
242 QStringView resolvedPath = QStringView(resolvedQmldirPath).chopped(SlashQmldir.size() - 1);
244 result.name = reader.typeNamespace();
246 result.isStaticModule = reader.isStaticModule();
247 result.isSystemModule = reader.isSystemModule();
248 result.type = Import::Qmldir;
249 result.imports.append(reader.imports());
250 result.dependencies.append(reader.dependencies());
252 if (result.isSystemModule) {
254 const QString jsrootPath = resolvedPath + JsrootDotQmltypes;
255 if (QFile::exists(jsrootPath)) {
256 readQmltypes(jsrootPath, &result);
258 result.warnings.append({
259 QStringLiteral(
"System module at %1 does not contain jsroot.qmltypes")
262 QQmlJS::SourceLocation()
267 const auto typeInfos = reader.typeInfos();
268 for (
const auto &typeInfo : typeInfos) {
269 const QString typeInfoPath = QFileInfo(typeInfo).isRelative()
270 ? resolvedPath + typeInfo
272 readQmltypes(typeInfoPath, &result);
275 if (typeInfos.isEmpty() && !reader.plugins().isEmpty()) {
276 const QString defaultTypeInfoPath = resolvedPath + PluginsDotQmltypes;
277 if (QFile::exists(defaultTypeInfoPath)) {
278 result.warnings.append({
279 QStringLiteral(
"typeinfo not declared in qmldir file: ")
280 + defaultTypeInfoPath,
282 QQmlJS::SourceLocation()
284 readQmltypes(defaultTypeInfoPath, &result);
288 QHash<QString, QQmlJSExportedScope> qmlComponents;
289 const auto components = reader.components();
290 for (
auto it = components.begin(), end = components.end(); it != end; ++it) {
291 const QString filePath = resolvedPath + it->fileName;
292 if (!QFile::exists(filePath)) {
293 result.warnings.append({
294 it->fileName + QStringLiteral(
" is listed as component in ")
296 + QStringLiteral(
" but does not exist.\n"),
298 QQmlJS::SourceLocation()
303 auto mo = qmlComponents.find(it->fileName);
304 if (mo == qmlComponents.end()) {
305 QQmlJSScope::Ptr imported = localFile2QQmlJSScope(filePath);
306 if (
auto factory = imported.factory())
307 factory->setIsSingleton(it->singleton);
309 imported->setIsSingleton(it->singleton);
310 mo = qmlComponents.insert(it->fileName, {imported, QList<QQmlJSScope::Export>() });
313 mo->exports.append(QQmlJSScope::Export(
314 reader.typeNamespace(), it.key(), it->version, QTypeRevision()));
316 for (
auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it)
317 result.objects.append(it.value());
319 const auto scripts = reader.scripts();
320 for (
const auto &script : scripts) {
321 const QString filePath = resolvedPath + script.fileName;
322 auto mo = result.scripts.find(script.fileName);
323 if (mo == result.scripts.end())
324 mo = result.scripts.insert(script.fileName, { localFile2QQmlJSScope(filePath), {} });
326 mo->exports.append(QQmlJSScope::Export(
327 reader.typeNamespace(), script.nameSpace,
328 script.version, QTypeRevision()));
333QQmlJSImporter::Import QQmlJSImporter::readDirectory(
const QString &directory)
336 import.type = Import::Directory;
337 if (directory.startsWith(u':')) {
339 const auto resources = m_mapper->filter(
340 QQmlJSResourceFileMapper::resourceQmlDirectoryFilter(directory.mid(1)));
341 for (
const auto &entry : resources) {
342 const QString name = QFileInfo(entry.resourcePath).baseName();
343 if (name.front().isUpper()) {
344 import.objects.append({
345 localFile2QQmlJSScope(entry.filePath),
346 { QQmlJSScope::Export(QString(), name, QTypeRevision(), QTypeRevision()) }
351 qWarning() <<
"Cannot read files from resource directory" << directory
352 <<
"because no resource file mapper was provided";
359 QDirListing::IteratorFlags listingFlags = QDirListing::IteratorFlag::Default
360 | QDirListing::IteratorFlag::IncludeHidden
361 | QDirListing::IteratorFlag::FilesOnly;
362 QDirListing dirListing(
364 QStringList() << QLatin1String(
"*.qml"),
367 for (
const QDirListing::DirEntry &entry: dirListing) {
368 QString name = entry.completeBaseName();
371 if (!name.front().isUpper())
375 if (name.endsWith(u".ui"))
376 name = name.chopped(3);
379 if (name.contains(u'.'))
382 import.objects.append({
383 localFile2QQmlJSScope(entry.filePath()),
384 { QQmlJSScope::Export(QString(), name, QTypeRevision(), QTypeRevision()) }
390void QQmlJSImporter::importDependencies(
const QQmlJSImporter::Import &import, quint8 precedence,
391 QQmlJSImporter::AvailableTypes *types,
392 const QString &prefix, QTypeRevision version,
397 for (
auto const &dependency : std::as_const(import.dependencies))
398 importHelper(dependency.module, types, precedence + 1, QString(), dependency.version,
true);
400 bool hasOptionalImports =
false;
401 for (
auto const &import : std::as_const(import.imports)) {
402 if (import.flags & QQmlDirParser::Import::Optional) {
403 hasOptionalImports =
true;
404 if (!useOptionalImports()) {
408 if (!(import.flags & QQmlDirParser::Import::OptionalDefault))
412 importHelper(import.module, types, precedence + 1, isDependency ? QString() : prefix,
413 (import.flags & QQmlDirParser::Import::Auto) ? version : import.version,
417 if (hasOptionalImports && !useOptionalImports()) {
418 types->warnings.append({
419 u"%1 uses optional imports which are not supported. Some types might not be found."_s
421 QtCriticalMsg, QQmlJS::SourceLocation()
429 const QTypeRevision importVersion = importDescription.version();
431 if (!importVersion.hasMajorVersion())
433 if (importVersion.majorVersion() != exportVersion.majorVersion())
435 return !importVersion.hasMinorVersion()
436 || exportVersion.minorVersion() <= importVersion.minorVersion();
439void QQmlJSImporter::insertAliases(
const QQmlJS::ContextualType &type,
440 QQmlJSImporter::AvailableTypes *types)
442 const QStringList cppAliases = aliases(type.scope);
443 for (
const QString &alias : cppAliases)
444 types->cppNames.setType(alias, type);
447void QQmlJSImporter::insertExport(
const QQmlJS::ContextualType &type,
448 const QQmlJS::Export &valExport,
const QString &qmlName,
449 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
450 QQmlJSImporter::AvailableTypes *types)
const
452 if (m_flags.testFlag(TolerateFileSelectors)) {
453 if (
const QString fileSelector = QQmlJSUtils::fileSelectorFor(type.scope);
454 !fileSelector.isEmpty()) {
455 types->qmlNames.setFileSelectedType(fileSelector, qmlName, type);
459 if (!types->qmlNames.hasType(qmlName)) {
460 types->qmlNames.setType(qmlName, type);
461 (*seenExports)[qmlName].append(valExport);
466 types->qmlNames.setType(qmlName, type);
467 (*seenExports)[qmlName].append(valExport);
471QQmlJSImporter::resolveConflictingExports(
const QQmlJS::Import &importDescription,
472 const QQmlJSExportedScope &val, quint8 precedence,
473 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
474 QQmlJSImporter::AvailableTypes *types)
476 QQmlJSScope::Export bestExport;
478 for (
const auto &valExport : val.exports) {
479 const QString qmlName = prefixedName(importDescription.prefix(), valExport.type());
480 if (!isVersionAllowed(valExport, importDescription))
485 if (!bestExport.isValid() || valExport.version() > bestExport.version())
486 bestExport = valExport;
488 auto insertExportWithConflict = [&](
const QQmlJSImportedScope &conflicting) {
494 if (conflicting.scope == val.scope && conflicting.revision == valExport.version())
497 const SeenVersion seenVersion = computeSeenVersion(
498 importDescription, seenExports->value(qmlName), valExport.version());
500 insertExportWithConflictingVersion(val, precedence, qmlName, valExport,
501 conflicting.scope, seenExports, types, seenVersion);
504 if (
const auto it = types->qmlNames.types().find(qmlName);
505 it != types->qmlNames.types().end()) {
506 insertExportWithConflict(*it);
510 const auto [it, end] = types->qmlNames.fileSelectionsEqualRange(qmlName);
512 insertExport({ val.scope, valExport.version(), precedence }, valExport, qmlName,
516 insertExportWithConflict(it->type);
521QQmlJSImporter::SeenVersion
522QQmlJSImporter::computeSeenVersion(
const QQmlJS::Import &importDescription,
523 const QList<QQmlJS::Export> &existingExports,
524 QTypeRevision valExportVersion)
const
526 QQmlJSImporter::SeenVersion seenVersion = LowerVersion;
527 for (
const QQmlJSScope::Export &entry : existingExports) {
528 if (!isVersionAllowed(entry, importDescription))
531 if (valExportVersion < entry.version()) {
532 seenVersion = HigherVersion;
536 if (seenVersion == LowerVersion && valExportVersion == entry.version())
537 seenVersion = SameVersion;
542void QQmlJSImporter::insertExportWithConflictingVersion(
543 const QQmlJSExportedScope &val, quint8 precedence,
const QString &qmlName,
544 const QQmlJSScope::Export &valExport,
const QQmlJSScope::ConstPtr &scope,
545 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
546 QQmlJSImporter::AvailableTypes *types, SeenVersion seenVersion)
const
548 auto onDuplicateImport = [&]() {
549 types->warnings.append({ QStringLiteral(
"Ambiguous type detected. "
550 "%1 %2.%3 is defined multiple times.")
552 .arg(valExport.version().majorVersion())
553 .arg(valExport.version().minorVersion()),
554 QtCriticalMsg, QQmlJS::SourceLocation() });
557 types->qmlNames.clearType(qmlName);
559 switch (seenVersion) {
561 insertExport({ val.scope, valExport.version(), precedence }, valExport, qmlName,
565 if (!m_flags.testAnyFlag(QQmlJSImporterFlag::TolerateFileSelectors) || !scope) {
569 if (
const QString fileSelector = QQmlJSUtils::fileSelectorFor(val.scope);
570 !fileSelector.isEmpty()) {
571 types->qmlNames.setFileSelectedType(fileSelector, qmlName,
572 { val.scope, valExport.version(), precedence });
574 if (!types->qmlNames.hasType(qmlName)) {
575 types->qmlNames.setType(qmlName,
576 { val.scope, valExport.version(), precedence });
577 (*seenExports)[qmlName].append(valExport);
581 if (!QQmlJSUtils::fileSelectorFor(scope).isEmpty()) {
583 types->qmlNames.setType(qmlName, { val.scope, valExport.version(), precedence });
584 (*seenExports)[qmlName].append(valExport);
595void QQmlJSImporter::insertExports(
const QQmlJS::Import &importDescription,
596 const QQmlJSExportedScope &val,
const QString &cppName,
598 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
599 QQmlJSImporter::AvailableTypes *types)
601 const QQmlJSScope::Export bestExport =
602 resolveConflictingExports(importDescription, val, precedence, seenExports, types);
603 const QTypeRevision bestRevision =
604 bestExport.isValid() ? bestExport.revision() : QTypeRevision::zero();
605 const QQmlJS::ContextualType contextualType{ val.scope, bestRevision, precedence };
606 types->cppNames.setType(cppName, contextualType);
608 insertAliases(contextualType, types);
610 const QTypeRevision bestVersion =
611 bestExport.isValid() ? bestExport.version() : QTypeRevision::zero();
612 types->qmlNames.setType(prefixedName(internalPrefix, cppName),
613 { val.scope, bestVersion, precedence });
616void QQmlJSImporter::processImport(
const QQmlJS::Import &importDescription,
617 const QQmlJSImporter::Import &import, quint8 precedence,
618 QQmlJSImporter::AvailableTypes *types)
620 QHash<QString, QList<QQmlJSScope::Export>> seenExports;
623 if (!importDescription.prefix().isEmpty())
624 types->qmlNames.setType(importDescription.prefix(), { {}, precedence });
626 if (!importDescription.isDependency()) {
628 types->qmlNames.setType(prefixedName(modulePrefix, importDescription.name()),
631 if (import.isStaticModule)
632 types->staticModules << import.name;
634 if (import.isSystemModule)
635 types->hasSystemModule =
true;
637 types->warnings.append(import.warnings);
640 for (
auto it = import.scripts.begin(); it != import.scripts.end(); ++it) {
642 Q_ASSERT(!it->exports.isEmpty());
643 insertExports(importDescription, *it, prefixedName(anonPrefix, internalName(it->scope)),
644 precedence, &seenExports, types);
648 for (
const auto &val : import.objects) {
649 const QString cppName = isComposite(val.scope)
650 ? prefixedName(anonPrefix, internalName(val.scope))
651 : internalName(val.scope);
653 if (val.exports.isEmpty()) {
654 const QQmlJS::ContextualType unresolvableDummyName{ val.scope, QTypeRevision(),
656 types->qmlNames.setType(prefixedName(internalPrefix, cppName), unresolvableDummyName);
657 types->cppNames.setType(cppName, unresolvableDummyName);
658 insertAliases(unresolvableDummyName, types);
660 insertExports(importDescription, val, cppName, precedence, &seenExports, types);
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
685 QQmlJSImporter::AvailableTypes tempTypes(builtinImportHelper().cppNames);
686 tempTypes.cppNames.addTypes(types->cppNames);
693 const QQmlJSScope::ConstPtr arrayType = tempTypes.cppNames.type(u"Array"_s).scope;
694 for (
auto it = import.objects.begin(); it != import.objects.end(); ++it) {
695 if (!it->scope.factory()) {
696 QQmlJSScope::resolveEnums(it->scope, tempTypes.cppNames);
697 QQmlJSScope::resolveList(it->scope, arrayType);
701 for (
const auto &val : std::as_const(import.objects)) {
703 if (!val.scope.factory() && val.scope->baseType().isNull()) {
707 if (val.scope->baseTypeName() == s_inProcessMarker)
713 if (val.scope->isComposite()) {
714 types->warnings.append({
715 QStringLiteral(
"Found incomplete composite type %1. Do not use qmlplugindump.")
716 .arg(val.scope->internalName()),
718 QQmlJS::SourceLocation()
722 QQmlJSScope::resolveNonEnumTypes(val.scope, tempTypes.cppNames);
728
729
730
731QQmlJSImporter::ImportedTypes QQmlJSImporter::importHardCodedBuiltins()
733 const auto builtins = builtinImportHelper();
735 QQmlJS::ContextualTypes result(
736 QQmlJS::ContextualTypes::QML, {}, {}, builtins.cppNames.arrayType());
737 for (
const QString hardcoded : {
738 "void"_L1,
"int"_L1,
"bool"_L1,
"double"_L1,
"real"_L1,
"string"_L1,
"url"_L1,
739 "date"_L1,
"regexp"_L1,
"rect"_L1,
"point"_L1,
"size"_L1,
"variant"_L1,
"var"_L1,
742 const auto type = builtins.qmlNames.type(hardcoded);
743 Q_ASSERT(type.scope);
744 result.setType(hardcoded, { type, quint8(QQmlJS::PrecedenceValues::Default) });
747 return ImportedTypes(std::move(result), {});
751QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
756 AvailableTypes builtins(QQmlJS::ContextualTypes(QQmlJS::ContextualTypes::INTERNAL, {}, {}, {}));
758 importHelper(u"QML"_s, &builtins, quint8(QQmlJS::PrecedenceValues::Default), QString(),
759 QTypeRevision::fromVersion(1, 0));
761 QQmlJSScope::ConstPtr arrayType = builtins.cppNames.type(u"Array"_s).scope;
764 m_builtins = AvailableTypes(QQmlJS::ContextualTypes(
765 QQmlJS::ContextualTypes::INTERNAL, builtins.cppNames.types(), builtins.cppNames.names(),
767 m_builtins->qmlNames = QQmlJS::ContextualTypes(
768 QQmlJS::ContextualTypes::QML, builtins.qmlNames.types(), builtins.qmlNames.names(),
770 m_builtins->staticModules = std::move(builtins.staticModules);
771 m_builtins->warnings = std::move(builtins.warnings);
772 m_builtins->hasSystemModule = builtins.hasSystemModule;
778
779
780QList<QQmlJS::DiagnosticMessage> QQmlJSImporter::importQmldirs(
const QStringList &qmldirFiles)
782 QList<QQmlJS::DiagnosticMessage> warnings;
783 for (
const auto &file : qmldirFiles) {
786 if (file.endsWith(SlashQmldir)) {
787 result = readQmldir(file.chopped(SlashQmldir.size()));
788 setQualifiedNamesOn(result);
792 QStringLiteral(
"Argument %1 to -i option is not a qmldir file. Assuming qmltypes.")
795 QQmlJS::SourceLocation()
798 readQmltypes(file, &result);
803 qmldirName = file + QStringLiteral(
"_FAKE_QMLDIR");
806 warnings.append(result.warnings);
807 m_seenQmldirFilesAndDirectories.insert(qmldirName, result);
809 for (
const auto &object : std::as_const(result.objects)) {
810 for (
const auto &ex : object.exports) {
811 m_seenImports.insert({ex.package(), ex.version()}, qmldirName);
813 m_seenImports.insert({ex.package(), QTypeRevision()}, qmldirName);
821QQmlJSImporter::ImportedTypes QQmlJSImporter::importModule(
const QString &module, quint8 precedence,
822 const QString &prefix,
823 QTypeRevision version,
824 QStringList *staticModuleList)
826 const AvailableTypes builtins = builtinImportHelper();
827 AvailableTypes result(builtins.cppNames);
828 if (!importHelper(module, &result, precedence, prefix, version)) {
829 result.warnings.append({
830 QStringLiteral(
"Failed to import %1. Are your import paths set up properly?")
833 QQmlJS::SourceLocation()
837 if (staticModuleList)
838 *staticModuleList << result.staticModules;
840 return ImportedTypes(std::move(result.qmlNames), std::move(result.warnings));
843QQmlJSImporter::ImportedTypes QQmlJSImporter::builtinInternalNames()
845 auto builtins = builtinImportHelper();
846 return ImportedTypes(std::move(builtins.cppNames), std::move(builtins.warnings));
849bool QQmlJSImporter::importHelper(
const QString &module, AvailableTypes *types, quint8 precedence,
850 const QString &prefix, QTypeRevision version,
bool isDependency,
854 const QString moduleCacheName = QString(module).replace(u'/', u'.');
857 Q_ASSERT(prefix.isEmpty());
859 const QQmlJS::Import cacheKey(prefix, moduleCacheName, version, isFile, isDependency);
862 if (!prefix.isEmpty())
863 types->qmlNames.setType(prefix, { { }, precedence });
865 auto getTypesFromCache = [&]() ->
bool {
866 Q_ASSERT(m_cachedImportTypes.contains(cacheKey));
868 const auto &cacheEntry = m_cachedImportTypes[cacheKey];
870 types->cppNames.addTypes(cacheEntry->cppNames);
871 types->staticModules << cacheEntry->staticModules;
872 types->hasSystemModule |= cacheEntry->hasSystemModule;
876 types->warnings.append(cacheEntry->warnings);
877 types->qmlNames.addTypes(cacheEntry->qmlNames);
880 return cacheEntry->wasFound;
883 if (m_cachedImportTypes.contains(cacheKey))
884 return getTypesFromCache();
886 auto cacheTypes = QSharedPointer<QQmlJSImporter::AvailableTypes>(
887 new QQmlJSImporter::AvailableTypes(QQmlJS::ContextualTypes(
888 QQmlJS::ContextualTypes::INTERNAL, {}, {}, types->cppNames.arrayType())));
889 m_cachedImportTypes[cacheKey] = cacheTypes;
891 const std::pair<QString, QTypeRevision> importId { module, version };
892 const auto it = m_seenImports.constFind(importId);
894 if (it != m_seenImports.constEnd()) {
896 cacheTypes->wasFound =
false;
900 Q_ASSERT(m_seenQmldirFilesAndDirectories.contains(*it));
901 const QQmlJSImporter::Import import = m_seenQmldirFilesAndDirectories.value(*it);
903 importDependencies(import, precedence, cacheTypes.get(), prefix, version, isDependency);
904 processImport(cacheKey, import, precedence, cacheTypes.get());
906 return getTypesFromCache();
909 QStringList modulePaths;
911 const auto import = readDirectory(module);
912 m_seenQmldirFilesAndDirectories.insert(module, import);
913 m_seenImports.insert(importId, module);
914 importDependencies(import, precedence, cacheTypes.get(), prefix, version, isDependency);
915 processImport(cacheKey, import, precedence, cacheTypes.get());
918 modulePaths.append(module);
919 }
else if (module ==
"QML"_L1) {
923 modulePaths = {
":/qt-project.org/imports/QML"_L1 };
925 modulePaths = qQmlResolveImportPaths(module, m_importPaths, version);
928 for (
auto const &modulePath : std::as_const(modulePaths)) {
929 QString qmldirPath = ensureSlashQmldir(modulePath);
930 if (modulePath.startsWith(u':')) {
931 if (module ==
"QML"_L1) {
933 }
else if (m_mapper) {
934 const QString resourcePath = ensureSlashQmldir(modulePath.mid(1));
935 const auto entry = m_mapper->entry(
936 QQmlJSResourceFileMapper::resourceFileFilter(resourcePath));
937 qmldirPath = entry.filePath;
940 qWarning() <<
"Cannot read files from resource directory" << modulePath
941 <<
"because no resource file mapper was provided";
945 const auto setupImport = [&](
const QQmlJSImporter::Import &import) {
946 m_seenImports.insert(importId, qmldirPath);
947 importDependencies(import, precedence, cacheTypes.get(), prefix, version, isDependency);
949 processImport(cacheKey, import, precedence, cacheTypes.get());
950 return getTypesFromCache();
954 const auto it = m_seenQmldirFilesAndDirectories.constFind(qmldirPath);
955 if (it != m_seenQmldirFilesAndDirectories.constEnd() && it->type == Import::Qmldir)
956 return setupImport(*it);
958 const QFileInfo file(qmldirPath);
960 const auto import = readQmldir(file.canonicalPath());
961 setQualifiedNamesOn(import);
962 m_seenQmldirFilesAndDirectories.insert(qmldirPath, import);
963 return setupImport(import);
966 if (it != m_seenQmldirFilesAndDirectories.constEnd())
967 return setupImport(*it);
972 return getTypesFromCache();
975 m_seenImports.insert(importId, QString());
976 cacheTypes->wasFound =
false;
980QQmlJSScope::Ptr QQmlJSImporter::localFile2QQmlJSScope(
const QString &filePath)
983 const QString buildFolderFile = QQmlJSUtils::qmlBuildPathFromSourcePath(m_mapper, filePath);
984 const QString sourceFolderFile = preferQmlFilesFromSourceFolder()
985 ? QQmlJSUtils::qmlSourcePathFromBuildPath(m_mapper, filePath)
988 const auto seen = m_importedFiles.find(buildFolderFile);
989 if (seen != m_importedFiles.end())
992 return *m_importedFiles.insert(
994 { QQmlJSScope::create(),
995 QSharedPointer<QDeferredFactory<QQmlJSScope>>(
new QDeferredFactory<QQmlJSScope>(
996 this, {}, sourceFolderFile, QString(),
false)) });
999QQmlJSScope::Ptr QQmlJSImporter::importFile(
const QString &file)
1001 return localFile2QQmlJSScope(file);
1004QQmlJSImporter::ImportedTypes
1005QQmlJSImporter::importDirectory(
const QString &directory, quint8 precedence,
const QString &prefix)
1007 const AvailableTypes builtins = builtinImportHelper();
1008 QQmlJSImporter::AvailableTypes types(QQmlJS::ContextualTypes(
1009 QQmlJS::ContextualTypes::INTERNAL, {}, {}, builtins.cppNames.arrayType()));
1010 importHelper(directory, &types, precedence, prefix, QTypeRevision(),
false,
true);
1011 return ImportedTypes(std::move(types.qmlNames), std::move(types.warnings));
1014void QQmlJSImporter::setImportPaths(
const QStringList &importPaths)
1016 if (m_importPaths == importPaths)
1019 m_importPaths = importPaths;
1023 m_seenImports.clear();
1024 m_cachedImportTypes.clear();
1028void QQmlJSImporter::clearCache()
1030 m_seenImports.clear();
1031 m_cachedImportTypes.clear();
1032 m_seenQmldirFilesAndDirectories.clear();
1033 m_importedFiles.clear();
1037QQmlJSScope::ConstPtr QQmlJSImporter::jsGlobalObject()
1039 return builtinImportHelper().cppNames.type(u"GlobalObject"_s).scope;
1042void QQmlJSImporter::setQualifiedNamesOn(
const Import &import)
1044 for (
auto &object : import.objects) {
1045 if (object.exports.isEmpty())
1047 if (
auto *factory = object.scope.factory()) {
1048 factory->setModuleName(import.name);
1050 object.scope->setOwnModuleName(import.name);
1055void QQmlJSImporter::runImportVisitor(QQmlJS::AST::Node *rootNode,
1056 const ImportVisitorPrerequisites &p)
1058 m_importVisitor(rootNode,
this, p);
Combined button and popup list for selecting options.
static QStringList aliases(const QQmlJSScope::ConstPtr &scope)
static const QLatin1String JsrootDotQmltypes
static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry, const QQmlJS::Import &importDescription)
static bool isComposite(const QQmlJSScope::ConstPtr &scope)
static QString resolvePreferredPath(const QString &qmldirPath, const QString &prefer, QQmlJSResourceFileMapper *mapper)
static const QString prefixedName(const QString &prefix, const QString &name)
static QString ensureSlashQmldir(const QString &path)
static const QLatin1String SlashQmldir
static QString internalName(const QQmlJSScope::ConstPtr &scope)
static const QLatin1String PluginsDotQmltypes