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();
165 if (
const auto *factory = scope.factory())
166 return factory->filePath();
167 return scope->filePath();
172 if (
const auto *factory = scope.factory())
173 return factory->moduleName();
174 return scope->moduleName();
180 return scope.factory() || scope->isComposite();
185 return isComposite(scope)
191
192
193
194QQmlJSImporter::QQmlJSImporter(
const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
195 QQmlJSImporterFlags flags)
196 : m_importPaths(importPaths),
199 m_importVisitor([](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
200 const ImportVisitorPrerequisites &p) {
201 auto visitor = std::unique_ptr<QQmlJS::AST::BaseVisitor>(
new QQmlJSImportVisitor(
202 self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles));
203 QQmlJS::AST::Node::accept(rootNode, visitor.get());
209 const QQmlJSResourceFileMapper *mapper)
211 if (prefer.isEmpty())
214 if (!prefer.endsWith(u'/')) {
215 qWarning() <<
"Ignoring invalid prefer path" << prefer <<
"(has to end with slash)";
219 if (prefer.startsWith(u':')) {
224 Q_ASSERT(prefer.endsWith(u'/'));
225 const auto entry = mapper->entry(
226 QQmlJSResourceFileMapper::resourceFileFilter(ensureSlashQmldir(prefer.mid(1))));
230 return entry.filePath.endsWith(SlashQmldir)
236 const QFileInfo f(ensureSlashQmldir(prefer));
237 const QString canonical = f.canonicalFilePath();
238 if (canonical.isEmpty())
243QQmlJSImporter::Import QQmlJSImporter::readQmldir(
const QString &modulePath)
246 const QString moduleQmldirPath = ensureSlashQmldir(modulePath);
247 auto reader = createQmldirParserForFile(moduleQmldirPath, &result);
249 const QString resolvedQmldirPath
250 = resolvePreferredPath(moduleQmldirPath, reader.preferredPath(), m_mapper);
251 if (resolvedQmldirPath != moduleQmldirPath)
252 reader = createQmldirParserForFile(resolvedQmldirPath, &result);
255 Q_ASSERT(resolvedQmldirPath.endsWith(SlashQmldir));
256 QStringView resolvedPath = QStringView(resolvedQmldirPath).chopped(SlashQmldir.size() - 1);
258 result.name = reader.typeNamespace();
260 result.isStaticModule = reader.isStaticModule();
261 result.isSystemModule = reader.isSystemModule();
262 result.type = Import::Qmldir;
263 result.imports.append(reader.imports());
264 result.dependencies.append(reader.dependencies());
265 result.exports.append(reader.exports());
267 if (result.isSystemModule) {
269 const QString jsrootPath = resolvedPath + JsrootDotQmltypes;
270 if (QFile::exists(jsrootPath)) {
271 readQmltypes(jsrootPath, &result);
273 result.warnings.append({
274 QStringLiteral(
"System module at %1 does not contain jsroot.qmltypes")
277 QQmlJS::SourceLocation()
282 const auto typeInfos = reader.typeInfos();
283 for (
const auto &typeInfo : typeInfos) {
284 const QString typeInfoPath = QFileInfo(typeInfo).isRelative()
285 ? resolvedPath + typeInfo
287 readQmltypes(typeInfoPath, &result);
290 if (typeInfos.isEmpty() && !reader.plugins().isEmpty()) {
291 const QString defaultTypeInfoPath = resolvedPath + PluginsDotQmltypes;
292 if (QFile::exists(defaultTypeInfoPath)) {
293 result.warnings.append({
294 QStringLiteral(
"typeinfo not declared in qmldir file: ")
295 + defaultTypeInfoPath,
297 QQmlJS::SourceLocation()
299 readQmltypes(defaultTypeInfoPath, &result);
303 QHash<QString, QQmlJSExportedScope> qmlComponents;
304 const auto components = reader.components();
305 for (
auto it = components.begin(), end = components.end(); it != end; ++it) {
306 const QString filePath = resolvedPath + it->fileName;
307 if (!QFile::exists(filePath)) {
308 result.warnings.append({
309 it->fileName + QStringLiteral(
" is listed as component in ")
311 + QStringLiteral(
" but does not exist.\n"),
313 QQmlJS::SourceLocation()
318 auto mo = qmlComponents.find(it->fileName);
319 if (mo == qmlComponents.end()) {
320 QQmlJSScope::Ptr imported = localFile2QQmlJSScope(filePath);
321 if (
auto factory = imported.factory())
322 factory->setIsSingleton(it->singleton);
324 imported->setIsSingleton(it->singleton);
325 mo = qmlComponents.insert(it->fileName, {imported, QList<QQmlJSScope::Export>() });
328 mo->exports.append(QQmlJSScope::Export(
329 reader.typeNamespace(), it.key(), it->version, QTypeRevision()));
331 for (
auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it)
332 result.objects.append(it.value());
334 const auto scripts = reader.scripts();
335 for (
const auto &script : scripts) {
336 const QString filePath = resolvedPath + script.fileName;
337 auto mo = result.scripts.find(script.fileName);
338 if (mo == result.scripts.end())
339 mo = result.scripts.insert(script.fileName, { localFile2QQmlJSScope(filePath), {} });
341 mo->exports.append(QQmlJSScope::Export(
342 reader.typeNamespace(), script.nameSpace,
343 script.version, QTypeRevision()));
350 QList<QQmlJS::Export> exports;
351 const bool hasExport = name.front().isUpper();
353 exports << QQmlJSScope::Export(QString(), name, QTypeRevision(), QTypeRevision());
357QQmlJSImporter::Import QQmlJSImporter::readDirectory(
const QString &directory)
360 import.type = Import::Directory;
361 if (directory.startsWith(u':')) {
363 const auto resources = m_mapper->filter(
364 QQmlJSResourceFileMapper::resourceQmlDirectoryFilter(directory.mid(1)));
365 for (
const auto &entry : resources) {
366 const QString name = QFileInfo(entry.resourcePath).baseName();
367 import.objects.append(
368 { localFile2QQmlJSScope(entry.filePath), createExports(name) });
371 qWarning() <<
"Cannot read files from resource directory" << directory
372 <<
"because no resource file mapper was provided";
379 QDirListing::IteratorFlags listingFlags = QDirListing::IteratorFlag::Default
380 | QDirListing::IteratorFlag::IncludeHidden
381 | QDirListing::IteratorFlag::FilesOnly;
382 QDirListing dirListing(
384 QStringList() << QLatin1String(
"*.qml"),
387 for (
const QDirListing::DirEntry &entry: dirListing) {
388 QString name = entry.completeBaseName();
391 if (name.endsWith(u".ui"))
392 name = name.chopped(3);
395 if (name.contains(u'.'))
398 import.objects.append({ localFile2QQmlJSScope(entry.filePath()), createExports(name) });
404
405
406
407
408QTypeRevision QQmlJSImporter::resolveImportVersion(
const Import &import, QTypeRevision version)
411 Q_ASSERT(version.hasMajorVersion() || !version.hasMinorVersion());
413 QTypeRevision resolved;
414 const auto resolve = [&](
const auto &exports,
const auto &getVersion) {
415 for (
const auto &exportEntry : exports) {
416 const QTypeRevision exportVersion = getVersion(exportEntry);
417 if (!exportVersion.hasMajorVersion())
419 if (version.hasMajorVersion() && exportVersion.majorVersion() != version.majorVersion())
422 if (version.hasMinorVersion() && exportVersion >= version) {
427 if (!resolved.isValid() || exportVersion > resolved)
428 resolved = exportVersion;
435 if (resolve(import.exports, [](
const QQmlDirParser::Export &e) {
return e; }))
440 const auto scopeExportVersion = [](
const QQmlJSScope::Export &e) {
return e.version(); };
441 for (
const QQmlJSExportedScope &object : import.objects) {
442 if (resolve(object.exports, scopeExportVersion))
445 for (
const QQmlJSExportedScope &script : import.scripts) {
446 if (resolve(script.exports, scopeExportVersion))
453void QQmlJSImporter::importDependencies(
const QQmlJSImporter::Import &import, quint8 precedence,
454 QQmlJSImporter::AvailableTypes *types,
455 const QString &prefix, QTypeRevision resolvedVersion,
456 QTypeRevision searchVersion,
bool isDependency)
460 for (
auto const &dependency : std::as_const(import.dependencies)) {
461 importHelper(dependency.module, types, precedence + 1, QString(), dependency.version,
462 dependency.version,
true);
465 bool hasOptionalImports =
false;
466 for (
auto const &import : std::as_const(import.imports)) {
467 if (import.flags & QQmlDirParser::Import::Optional) {
468 hasOptionalImports =
true;
469 if (!useOptionalImports()) {
473 if (!(import.flags & QQmlDirParser::Import::OptionalDefault))
477 if (import.flags & QQmlDirParser::Import::Auto) {
478 importHelper(import.module, types, precedence + 1, isDependency ? QString() : prefix,
479 resolvedVersion, searchVersion, isDependency);
481 importHelper(import.module, types, precedence + 1, isDependency ? QString() : prefix,
482 import.version, import.version, isDependency);
486 if (hasOptionalImports && !useOptionalImports()) {
487 types->warnings.append({
488 u"%1 uses optional imports which are not supported. Some types might not be found."_s
490 QtCriticalMsg, QQmlJS::SourceLocation()
498 const QTypeRevision importVersion = importDescription.version();
500 if (!importVersion.hasMajorVersion())
502 if (importVersion.majorVersion() != exportVersion.majorVersion())
504 return !importVersion.hasMinorVersion()
505 || exportVersion.minorVersion() <= importVersion.minorVersion();
508void QQmlJSImporter::insertAliases(
const QQmlJS::ContextualType &type,
509 QQmlJSImporter::AvailableTypes *types)
511 const QStringList cppAliases = aliases(type.scope);
512 for (
const QString &alias : cppAliases)
513 types->cppNames.setType(alias, type);
516void QQmlJSImporter::insertExport(
const QQmlJS::ContextualType &type,
517 const QQmlJS::Export &valExport,
const QString &qmlName,
518 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
519 QQmlJSImporter::AvailableTypes *types)
const
521 if (m_flags.testFlag(TolerateFileSelectors)) {
522 if (
const QString fileSelector = QQmlJSUtils::fileSelectorFor(type.scope);
523 !fileSelector.isEmpty()) {
524 types->qmlNames.setFileSelectedType(fileSelector, qmlName, type);
528 if (!types->qmlNames.hasType(qmlName)) {
529 types->qmlNames.setType(qmlName, type);
530 (*seenExports)[qmlName].append(valExport);
535 types->qmlNames.setType(qmlName, type);
536 (*seenExports)[qmlName].append(valExport);
540QQmlJSImporter::resolveConflictingExports(
const QQmlJS::Import &importDescription,
541 const QQmlJSExportedScope &val, quint8 precedence,
542 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
543 QQmlJSImporter::AvailableTypes *types)
545 QQmlJSScope::Export bestExport;
547 for (
const auto &valExport : val.exports) {
548 const QString qmlName = prefixedName(importDescription.prefix(), valExport.type());
549 if (!isVersionAllowed(valExport, importDescription))
554 if (!bestExport.isValid() || valExport.version() > bestExport.version())
555 bestExport = valExport;
557 auto insertExportWithConflict = [&](
const QQmlJSImportedScope &conflicting) {
563 if (conflicting.scope == val.scope && conflicting.revision == valExport.version())
566 const SeenVersion seenVersion = computeSeenVersion(
567 importDescription, seenExports->value(qmlName), valExport.version());
569 insertExportWithConflictingVersion(val, precedence, qmlName, valExport,
570 conflicting.scope, seenExports, types, seenVersion);
573 if (
const auto it = types->qmlNames.types().find(qmlName);
574 it != types->qmlNames.types().end()) {
575 insertExportWithConflict(*it);
579 const auto [it, end] = types->qmlNames.fileSelectionsEqualRange(qmlName);
581 insertExport({ val.scope, valExport.version(), precedence }, valExport, qmlName,
585 insertExportWithConflict(it->type);
590QQmlJSImporter::SeenVersion
591QQmlJSImporter::computeSeenVersion(
const QQmlJS::Import &importDescription,
592 const QList<QQmlJS::Export> &existingExports,
593 QTypeRevision valExportVersion)
const
595 QQmlJSImporter::SeenVersion seenVersion = LowerVersion;
596 for (
const QQmlJSScope::Export &entry : existingExports) {
597 if (!isVersionAllowed(entry, importDescription))
600 if (valExportVersion < entry.version()) {
601 seenVersion = HigherVersion;
605 if (seenVersion == LowerVersion && valExportVersion == entry.version())
606 seenVersion = SameVersion;
611void QQmlJSImporter::insertExportWithConflictingVersion(
612 const QQmlJSExportedScope &val, quint8 precedence,
const QString &qmlName,
613 const QQmlJSScope::Export &valExport,
const QQmlJSScope::ConstPtr &scope,
614 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
615 QQmlJSImporter::AvailableTypes *types, SeenVersion seenVersion)
const
617 auto onDuplicateImport = [&]() {
618 types->warnings.append({ QStringLiteral(
"Ambiguous type detected. "
619 "%1 %2.%3 is defined multiple times.")
621 .arg(valExport.version().majorVersion())
622 .arg(valExport.version().minorVersion()),
623 QtCriticalMsg, QQmlJS::SourceLocation() });
626 types->qmlNames.clearType(qmlName);
628 switch (seenVersion) {
630 insertExport({ val.scope, valExport.version(), precedence }, valExport, qmlName,
634 if (!m_flags.testAnyFlag(QQmlJSImporterFlag::TolerateFileSelectors) || !scope) {
638 if (
const QString fileSelector = QQmlJSUtils::fileSelectorFor(val.scope);
639 !fileSelector.isEmpty()) {
640 types->qmlNames.setFileSelectedType(fileSelector, qmlName,
641 { val.scope, valExport.version(), precedence });
643 if (!types->qmlNames.hasType(qmlName)) {
644 types->qmlNames.setType(qmlName,
645 { val.scope, valExport.version(), precedence });
646 (*seenExports)[qmlName].append(valExport);
650 if (!QQmlJSUtils::fileSelectorFor(scope).isEmpty()) {
652 types->qmlNames.setType(qmlName, { val.scope, valExport.version(), precedence });
653 (*seenExports)[qmlName].append(valExport);
664void QQmlJSImporter::insertExports(
const QQmlJS::Import &importDescription,
665 const QQmlJSExportedScope &val,
const QString &cppName,
667 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
668 QQmlJSImporter::AvailableTypes *types)
670 const QQmlJSScope::Export bestExport =
671 resolveConflictingExports(importDescription, val, precedence, seenExports, types);
672 const QTypeRevision bestRevision =
673 bestExport.isValid() ? bestExport.revision() : QTypeRevision::zero();
674 const QQmlJS::ContextualType contextualType{ val.scope, bestRevision, precedence };
675 types->cppNames.setType(cppName, contextualType);
677 insertAliases(contextualType, types);
679 const QTypeRevision bestVersion =
680 bestExport.isValid() ? bestExport.version() : QTypeRevision::zero();
681 types->qmlNames.setType(prefixedName(internalPrefix, cppName),
682 { val.scope, bestVersion, precedence });
685void QQmlJSImporter::processImport(
const QQmlJS::Import &importDescription,
686 const QQmlJSImporter::Import &import, quint8 precedence,
687 QQmlJSImporter::AvailableTypes *types)
689 QHash<QString, QList<QQmlJSScope::Export>> seenExports;
692 if (!importDescription.prefix().isEmpty())
693 types->qmlNames.setType(importDescription.prefix(), { {}, precedence });
695 if (!importDescription.isDependency()) {
697 types->qmlNames.setType(prefixedName(modulePrefix, importDescription.name()),
700 if (import.isStaticModule)
701 types->staticModules << import.name;
703 if (import.isSystemModule)
704 types->hasSystemModule =
true;
706 types->warnings.append(import.warnings);
709 for (
auto it = import.scripts.begin(); it != import.scripts.end(); ++it) {
711 Q_ASSERT(!it->exports.isEmpty());
712 insertExports(importDescription, *it, prefixedName(anonPrefix, internalName(it->scope)),
713 precedence, &seenExports, types);
717 QSet<QString> cppExportNames;
718 for (
const auto &val : import.objects) {
719 if (isComposite(val.scope))
721 for (
const auto &valExport : val.exports) {
722 if (isVersionAllowed(valExport, importDescription))
723 cppExportNames.insert(valExport.type());
728 for (
const auto &object : import.objects) {
729 const bool composite = isComposite(object.scope);
730 const QString cppName = composite
731 ? prefixedName(anonPrefix, internalName(object.scope))
732 : internalName(object.scope);
738 QQmlJSExportedScope val = object;
740 val.exports.removeIf([&](
const QQmlJSScope::Export &valExport) {
741 return cppExportNames.contains(valExport.type());
745 if (val.exports.isEmpty()) {
747 if (!object.scope.factory() && object.scope->isOpaqueType()
748 && types->cppNames.hasType(cppName)
749 && !types->cppNames.type(cppName).scope->isOpaqueType()) {
752 const QQmlJS::ContextualType unresolvableDummyName{ val.scope, QTypeRevision(),
754 types->qmlNames.setType(prefixedName(internalPrefix, cppName), unresolvableDummyName);
755 types->cppNames.setType(cppName, unresolvableDummyName);
756 insertAliases(unresolvableDummyName, types);
758 insertExports(importDescription, val, cppName, precedence, &seenExports, types);
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
783 QQmlJSImporter::AvailableTypes tempTypes(builtinImportHelper().cppNames);
784 tempTypes.cppNames.addTypes(types->cppNames);
791 const QQmlJSScope::ConstPtr arrayType = tempTypes.cppNames.type(u"Array"_s).scope;
792 for (
auto it = import.objects.begin(); it != import.objects.end(); ++it) {
793 if (!it->scope.factory()) {
794 QQmlJSScope::resolveEnums(it->scope, tempTypes.cppNames);
795 QQmlJSScope::resolveList(it->scope, arrayType);
799 for (
const auto &val : std::as_const(import.objects)) {
801 if (!val.scope.factory() && val.scope->baseType().isNull()) {
805 if (val.scope->baseTypeName() == s_inProcessMarker)
811 if (val.scope->isComposite()) {
812 types->warnings.append({
813 QStringLiteral(
"Found incomplete composite type %1. Do not use qmlplugindump.")
814 .arg(val.scope->internalName()),
816 QQmlJS::SourceLocation()
820 QQmlJSScope::resolveNonEnumTypes(val.scope, tempTypes.cppNames);
826
827
828
829QQmlJSImporter::ImportedTypes QQmlJSImporter::importHardCodedBuiltins()
831 const auto builtins = builtinImportHelper();
833 QQmlJS::ContextualTypes result(
834 QQmlJS::ContextualTypes::QML, {}, {}, builtins.cppNames.arrayType());
835 for (
const QString hardcoded : {
836 "void"_L1,
"int"_L1,
"bool"_L1,
"double"_L1,
"real"_L1,
"string"_L1,
"url"_L1,
837 "date"_L1,
"regexp"_L1,
"rect"_L1,
"point"_L1,
"size"_L1,
"variant"_L1,
"var"_L1,
840 const auto type = builtins.qmlNames.type(hardcoded);
841 Q_ASSERT(type.scope);
842 result.setType(hardcoded, { type, quint8(QQmlJS::PrecedenceValues::Default) });
845 return ImportedTypes(std::move(result), {});
849QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
854 AvailableTypes builtins(QQmlJS::ContextualTypes(QQmlJS::ContextualTypes::INTERNAL, {}, {}, {}));
856 importHelper(u"QML"_s, &builtins, quint8(QQmlJS::PrecedenceValues::Default), QString(),
857 QTypeRevision::fromVersion(1, 0), QTypeRevision());
859 QQmlJSScope::ConstPtr arrayType = builtins.cppNames.type(u"Array"_s).scope;
862 m_builtins = AvailableTypes(QQmlJS::ContextualTypes(
863 QQmlJS::ContextualTypes::INTERNAL, builtins.cppNames.types(), builtins.cppNames.names(),
865 m_builtins->qmlNames = QQmlJS::ContextualTypes(
866 QQmlJS::ContextualTypes::QML, builtins.qmlNames.types(), builtins.qmlNames.names(),
868 m_builtins->staticModules = std::move(builtins.staticModules);
869 m_builtins->warnings = std::move(builtins.warnings);
870 m_builtins->hasSystemModule = builtins.hasSystemModule;
876
877
878QList<QQmlJS::DiagnosticMessage> QQmlJSImporter::importQmldirs(
const QStringList &qmldirFiles)
880 QList<QQmlJS::DiagnosticMessage> warnings;
881 for (
const auto &file : qmldirFiles) {
884 if (file.endsWith(SlashQmldir)) {
885 result = readQmldir(file.chopped(SlashQmldir.size()));
886 setQualifiedNamesOn(result);
890 QStringLiteral(
"Argument %1 to -i option is not a qmldir file. Assuming qmltypes.")
893 QQmlJS::SourceLocation()
896 readQmltypes(file, &result);
901 qmldirName = file + QStringLiteral(
"_FAKE_QMLDIR");
904 warnings.append(result.warnings);
905 m_seenQmldirFilesAndDirectories.insert(qmldirName, result);
907 for (
const auto &object : std::as_const(result.objects)) {
908 for (
const auto &ex : object.exports) {
909 m_seenImports.insert({ex.package(), ex.version()}, qmldirName);
911 m_seenImports.insert({ex.package(), QTypeRevision()}, qmldirName);
919QQmlJSImporter::ImportedTypes QQmlJSImporter::importModule(
const QString &module, quint8 precedence,
920 const QString &prefix,
921 QTypeRevision version,
922 QStringList *staticModuleList)
924 const AvailableTypes builtins = builtinImportHelper();
925 AvailableTypes result(builtins.cppNames);
926 if (!importHelper(module, &result, precedence, prefix, version, version)) {
928 if (version.hasMinorVersion() && version.hasMajorVersion()) {
929 message = QStringLiteral(
"Failed to import %1 %2.%3. Is that version available?")
930 .arg(module, QString::number(version.majorVersion()),
931 QString::number(version.minorVersion()));
932 }
else if (version.hasMajorVersion()) {
933 message = QStringLiteral(
"Failed to import %1 %2. Is that version available?")
934 .arg(module, QString::number(version.majorVersion()));
936 message = QStringLiteral(
"Failed to import %1. Are your import paths set up properly?")
939 result.warnings.append({ message, QtWarningMsg, QQmlJS::SourceLocation() });
942 if (staticModuleList)
943 *staticModuleList << result.staticModules;
945 return ImportedTypes(std::move(result.qmlNames), std::move(result.warnings),
946 std::move(result.moduleImports));
949QQmlJSImporter::ImportedTypes QQmlJSImporter::builtinInternalNames()
951 auto builtins = builtinImportHelper();
952 return ImportedTypes(std::move(builtins.cppNames), std::move(builtins.warnings));
955bool QQmlJSImporter::getTypesFromCache(AvailableTypes *types,
const CachedImportKey &cacheKey,
958 Q_ASSERT(m_cachedImportTypes.contains(cacheKey));
960 const auto &cacheEntry = m_cachedImportTypes[cacheKey];
962 types->cppNames.addTypes(cacheEntry->cppNames);
963 types->staticModules << cacheEntry->staticModules;
964 types->hasSystemModule |= cacheEntry->hasSystemModule;
968 types->warnings.append(cacheEntry->warnings);
969 types->qmlNames.addTypes(cacheEntry->qmlNames);
970 types->moduleImports.append(cacheEntry->moduleImports);
973 return cacheEntry->wasFound;
976bool QQmlJSImporter::importHelper(
const QString &module, AvailableTypes *types, quint8 precedence,
977 const QString &prefix, QTypeRevision version,
978 QTypeRevision searchVersion,
bool isDependency,
bool isFile)
981 const QString moduleCacheName = QString(module).replace(u'/', u'.');
984 Q_ASSERT(prefix.isEmpty());
986 const QQmlJS::Import importDescription(prefix, moduleCacheName, version, isFile, isDependency);
987 const CachedImportKey cacheKey { importDescription, searchVersion };
990 if (!prefix.isEmpty())
991 types->qmlNames.setType(prefix, { { }, precedence });
993 if (m_cachedImportTypes.contains(cacheKey))
994 return getTypesFromCache(types, cacheKey, isDependency);
996 auto cacheTypes = QSharedPointer<QQmlJSImporter::AvailableTypes>(
997 new QQmlJSImporter::AvailableTypes(QQmlJS::ContextualTypes(
998 QQmlJS::ContextualTypes::INTERNAL, {}, {}, types->cppNames.arrayType())));
999 m_cachedImportTypes[cacheKey] = cacheTypes;
1001 cacheTypes->moduleImports.append(cacheKey.import);
1003 const std::pair<QString, QTypeRevision> importId { module, searchVersion };
1005 const auto doProcess = [&](
const QQmlJSImporter::Import &import,
1006 QTypeRevision resolvedVersion) {
1007 importDependencies(import, precedence, cacheTypes.get(), prefix, resolvedVersion,
1008 searchVersion, isDependency);
1009 processImport(importDescription, import, precedence, cacheTypes.get());
1012 const auto it = m_seenImports.constFind(importId);
1014 if (it != m_seenImports.constEnd()) {
1015 if (it->isEmpty()) {
1016 cacheTypes->wasFound =
false;
1020 Q_ASSERT(m_seenQmldirFilesAndDirectories.contains(*it));
1021 const QQmlJSImporter::Import import = m_seenQmldirFilesAndDirectories.value(*it);
1024 doProcess(import, QTypeRevision());
1025 return getTypesFromCache(types, cacheKey, isDependency);
1026 }
else if (QTypeRevision resolvedVersion = resolveImportVersion(import, version);
1027 resolvedVersion.isValid()) {
1028 doProcess(import, resolvedVersion);
1029 return getTypesFromCache(types, cacheKey, isDependency);
1033 QStringList modulePaths;
1035 const auto import = readDirectory(module);
1036 m_seenQmldirFilesAndDirectories.insert(module, import);
1037 m_seenImports.insert(importId, module);
1038 doProcess(import, QTypeRevision());
1041 modulePaths.append(module);
1042 }
else if (module ==
"QML"_L1) {
1046 modulePaths = {
":/qt-project.org/imports/QML"_L1 };
1048 modulePaths = qQmlResolveImportPaths(module, m_importPaths, searchVersion);
1051 for (
auto const &modulePath : std::as_const(modulePaths)) {
1052 QString qmldirPath = ensureSlashQmldir(modulePath);
1053 if (modulePath.startsWith(u':')) {
1054 if (module ==
"QML"_L1) {
1056 }
else if (m_mapper) {
1057 const QString resourcePath = ensureSlashQmldir(modulePath.mid(1));
1058 const auto entry = m_mapper->entry(
1059 QQmlJSResourceFileMapper::resourceFileFilter(resourcePath));
1060 qmldirPath = entry.filePath;
1063 qWarning() <<
"Cannot read files from resource directory" << modulePath
1064 <<
"because no resource file mapper was provided";
1068 QTypeRevision resolvedVersion;
1069 const auto tryResolveVersion = [&](
const Import &import) {
1071 resolvedVersion = QTypeRevision();
1075 resolvedVersion = resolveImportVersion(import, version);
1076 return resolvedVersion.isValid();
1079 const auto setupImport = [&](
const QQmlJSImporter::Import &import) {
1080 m_seenImports.insert(importId, qmldirPath);
1081 doProcess(import, resolvedVersion);
1082 return getTypesFromCache(types, cacheKey, isDependency);
1086 const auto it = m_seenQmldirFilesAndDirectories.constFind(qmldirPath);
1087 const auto end = m_seenQmldirFilesAndDirectories.constEnd();
1089 if (it != end && it->type == Import::Qmldir && tryResolveVersion(*it))
1090 return setupImport(*it);
1092 if (
const QFileInfo file(qmldirPath); file.exists()) {
1093 if (
const auto import = readQmldir(file.canonicalPath()); tryResolveVersion(import)) {
1094 setQualifiedNamesOn(import);
1095 m_seenQmldirFilesAndDirectories.insert(qmldirPath, import);
1096 return setupImport(import);
1100 if (it != end && tryResolveVersion(*it))
1101 return setupImport(*it);
1106 return getTypesFromCache(types, cacheKey, isDependency);
1109 m_seenImports.insert(importId, QString());
1110 cacheTypes->wasFound =
false;
1114QQmlJSScope::Ptr QQmlJSImporter::localFile2QQmlJSScope(
const QString &filePath)
1117 const QString buildFolderFile = QQmlJSUtils::qmlBuildPathFromSourcePath(m_mapper, filePath);
1118 const QString sourceFolderFile = preferQmlFilesFromSourceFolder()
1119 ? QQmlJSUtils::qmlSourcePathFromBuildPath(m_mapper, filePath)
1122 const auto seen = m_importedFiles.find(buildFolderFile);
1123 if (seen != m_importedFiles.end())
1126 return *m_importedFiles.insert(
1128 { QQmlJSScope::create(),
1129 QSharedPointer<QDeferredFactory<QQmlJSScope>>(
new QDeferredFactory<QQmlJSScope>(
1130 this, {}, sourceFolderFile, QString(),
false)) });
1133QQmlJSScope::Ptr QQmlJSImporter::importFile(
const QString &file)
1135 return localFile2QQmlJSScope(file);
1138QQmlJSImporter::ImportedTypes
1139QQmlJSImporter::importDirectory(
const QString &directory, quint8 precedence,
const QString &prefix)
1141 const AvailableTypes builtins = builtinImportHelper();
1142 QQmlJSImporter::AvailableTypes types(QQmlJS::ContextualTypes(
1143 QQmlJS::ContextualTypes::INTERNAL, {}, {}, builtins.cppNames.arrayType()));
1144 importHelper(directory, &types, precedence, prefix, QTypeRevision(), QTypeRevision(),
false,
1146 return ImportedTypes(std::move(types.qmlNames), std::move(types.warnings),
1147 std::move(types.moduleImports));
1151
1152
1153
1154
1155
1156
1157void QQmlJSImporter::registerVersionedImports(
const ImportedTypes &imported)
1159 const QList<QQmlJS::Import> &moduleImports = imported.moduleImports();
1160 const auto findImport = [&](
const QString &module) ->
const QQmlJS::Import * {
1162 std::find_if(moduleImports.cbegin(), moduleImports.cend(),
1163 [&](
const QQmlJS::Import &import) {
return import.name() == module; });
1164 if (byName != moduleImports.cend())
1166 const auto byDirectory =
1167 std::find_if(moduleImports.cbegin(), moduleImports.cend(),
1168 [](
const QQmlJS::Import &import) {
return import.isFile(); });
1169 return byDirectory != moduleImports.cend() ? &*byDirectory :
nullptr;
1172 const QHash<QString, QQmlJS::ContextualType> &types = imported.contextualTypes().types();
1173 for (
auto it = types.cbegin(), end = types.cend(); it != end; ++it) {
1174 const QQmlJSScope::ConstPtr &scope = it->scope;
1178 const QString module = moduleName(scope);
1179 if (module.isEmpty())
1182 const QQmlJS::Import *import = findImport(module);
1186 QString name = it.key();
1187 if (!import->prefix().isEmpty()) {
1188 const QString qualifier = import->prefix() + u'.';
1189 if (!name.startsWith(qualifier))
1191 name = name.mid(qualifier.size());
1195 if (name.startsWith(u'$'))
1198 const VersionedImport versionedImport {
1199 module, name, import->isFile() ? QTypeRevision() : import->version()
1201 QList<VersionedImport> &versionedImports = m_versionedImports[filePath(scope)];
1202 if (!versionedImports.contains(versionedImport))
1203 versionedImports.append(versionedImport);
1207void QQmlJSImporter::setImportPaths(
const QStringList &importPaths)
1209 if (m_importPaths == importPaths)
1212 m_importPaths = importPaths;
1216 m_seenImports.clear();
1217 m_cachedImportTypes.clear();
1221void QQmlJSImporter::clearCache()
1223 m_seenImports.clear();
1224 m_cachedImportTypes.clear();
1225 m_seenQmldirFilesAndDirectories.clear();
1226 m_importedFiles.clear();
1227 m_versionedImports.clear();
1231QQmlJSScope::ConstPtr QQmlJSImporter::jsGlobalObject()
1233 return builtinImportHelper().cppNames.type(u"GlobalObject"_s).scope;
1236void QQmlJSImporter::setQualifiedNamesOn(
const Import &import)
1238 for (
auto &object : import.objects) {
1239 if (object.exports.isEmpty())
1241 if (
auto *factory = object.scope.factory()) {
1242 factory->setModuleName(import.name);
1244 object.scope->setOwnModuleName(import.name);
1249void QQmlJSImporter::runImportVisitor(QQmlJS::AST::Node *rootNode,
1250 const ImportVisitorPrerequisites &p)
1252 m_importVisitor(rootNode,
this, p);
Combined button and popup list for selecting options.
static QStringList aliases(const QQmlJSScope::ConstPtr &scope)
static QString filePath(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 const QString prefixedName(const QString &prefix, const QString &name)
static QString ensureSlashQmldir(const QString &path)
static QString resolvePreferredPath(const QString &qmldirPath, const QString &prefer, const QQmlJSResourceFileMapper *mapper)
static const QLatin1String SlashQmldir
static QString internalName(const QQmlJSScope::ConstPtr &scope)
static QList< QQmlJS::Export > createExports(const QString &name)
static QString moduleName(const QQmlJSScope::ConstPtr &scope)
static const QLatin1String PluginsDotQmltypes