Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmljsimporter.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmljsimporter_p.h"
8#include "qqmljslogger_p.h"
9
10#include <QtQml/private/qqmlimportresolver_p.h>
11
12#include <QtCore/qfileinfo.h>
13#include <QtCore/qdiriterator.h>
14
16
17using namespace Qt::StringLiterals;
18
19static const QLatin1String SlashQmldir = QLatin1String("/qmldir");
20static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes");
21
22static const QString prefixedName(const QString &prefix, const QString &name)
23{
24 Q_ASSERT(!prefix.endsWith(u'.'));
25 return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name);
26}
27
29{
30 QFile f(filename);
31 f.open(QFile::ReadOnly);
32 QQmlDirParser parser;
33 parser.parse(QString::fromUtf8(f.readAll()));
34 return parser;
35}
36
37void QQmlJSImporter::readQmltypes(
38 const QString &filename, QList<QQmlJSExportedScope> *objects,
39 QList<QQmlDirParser::Import> *dependencies)
40{
41 const QFileInfo fileInfo(filename);
42 if (!fileInfo.exists()) {
43 m_warnings.append({
44 QStringLiteral("QML types file does not exist: ") + filename,
47 });
48 return;
49 }
50
51 if (fileInfo.isDir()) {
52 m_warnings.append({
53 QStringLiteral("QML types file cannot be a directory: ") + filename,
56 });
57 return;
58 }
59
60 QFile file(filename);
62 m_warnings.append({
63 QStringLiteral("QML types file cannot be opened: ") + filename,
66 });
67 return;
68 }
69
71 QStringList dependencyStrings;
72 auto succ = reader(objects, &dependencyStrings);
73 if (!succ)
74 m_warnings.append({ reader.errorMessage(), QtCriticalMsg, QQmlJS::SourceLocation() });
75
76 const QString warningMessage = reader.warningMessage();
77 if (!warningMessage.isEmpty())
78 m_warnings.append({ warningMessage, QtWarningMsg, QQmlJS::SourceLocation() });
79
80 if (dependencyStrings.isEmpty())
81 return;
82
83 m_warnings.append({
84 QStringLiteral("Found deprecated dependency specifications in %1."
85 "Specify dependencies in qmldir and use qmltyperegistrar "
86 "to generate qmltypes files without dependencies.")
87 .arg(filename),
90 });
91
92 for (const QString &dependency : std::as_const(dependencyStrings)) {
93 const auto blank = dependency.indexOf(u' ');
94 if (blank < 0) {
95 dependencies->append(QQmlDirParser::Import(dependency, {},
97 continue;
98 }
99
100 const QString module = dependency.left(blank);
101 const QString versionString = dependency.mid(blank + 1).trimmed();
102 if (versionString == QStringLiteral("auto")) {
103 dependencies->append(QQmlDirParser::Import(module, {}, QQmlDirParser::Import::Auto));
104 continue;
105 }
106
107 const auto dot = versionString.indexOf(u'.');
108
109 const QTypeRevision version = dot < 0
111 : QTypeRevision::fromVersion(versionString.left(dot).toUShort(),
112 versionString.mid(dot + 1).toUShort());
113
114 dependencies->append(QQmlDirParser::Import(module, version,
116 }
117}
118
120{
121 if (const auto *factory = scope.factory())
122 return factory->internalName();
123 return scope->internalName();
124}
125
126static bool isComposite(const QQmlJSScope::ConstPtr &scope)
127{
128 // The only thing the factory can do is load a composite type.
129 return scope.factory() || scope->isComposite();
130}
131
133 bool useOptionalImports)
134 : m_importPaths(importPaths),
135 m_mapper(mapper),
136 m_useOptionalImports(useOptionalImports),
137 m_createImportVisitor([](const QQmlJSScope::Ptr &target, QQmlJSImporter *importer,
138 QQmlJSLogger *logger, const QString &implicitImportDirectory,
139 const QStringList &qmldirFiles) {
140 return new QQmlJSImportVisitor(target, importer, logger, implicitImportDirectory,
141 qmldirFiles);
142 })
143{
144}
145
146QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path)
147{
150 result.name = reader.typeNamespace();
151
152 result.isStaticModule = reader.isStaticModule();
153 result.isSystemModule = reader.isSystemModule();
154 result.imports.append(reader.imports());
155 result.dependencies.append(reader.dependencies());
156
157 const auto typeInfos = reader.typeInfos();
158 for (const auto &typeInfo : typeInfos) {
159 const QString typeInfoPath = QFileInfo(typeInfo).isRelative()
160 ? path + u'/' + typeInfo : typeInfo;
161 readQmltypes(typeInfoPath, &result.objects, &result.dependencies);
162 }
163
164 if (typeInfos.isEmpty() && !reader.plugins().isEmpty()) {
165 const QString defaultTypeInfoPath = path + SlashPluginsDotQmltypes;
166 if (QFile::exists(defaultTypeInfoPath)) {
167 m_warnings.append({
168 QStringLiteral("typeinfo not declared in qmldir file: ")
169 + defaultTypeInfoPath,
172 });
173 readQmltypes(defaultTypeInfoPath, &result.objects, &result.dependencies);
174 }
175 }
176
178 const auto components = reader.components();
179 for (auto it = components.begin(), end = components.end(); it != end; ++it) {
180 const QString filePath = path + QLatin1Char('/') + it->fileName;
181 if (!QFile::exists(filePath)) {
182 m_warnings.append({
183 it->fileName + QStringLiteral(" is listed as component in ")
184 + path + SlashQmldir
185 + QStringLiteral(" but does not exist.\n"),
188 });
189 continue;
190 }
191
192 auto mo = qmlComponents.find(it->fileName);
193 if (mo == qmlComponents.end()) {
194 QQmlJSScope::Ptr imported = localFile2ScopeTree(filePath);
195 if (auto *factory = imported.factory()) {
196 if (it->singleton) {
197 factory->setIsSingleton(true);
198 }
199 }
200 mo = qmlComponents.insert(it->fileName, {imported, QList<QQmlJSScope::Export>() });
201 }
202
203 mo->exports.append(QQmlJSScope::Export(
204 reader.typeNamespace(), it.key(), it->version, QTypeRevision()));
205 }
206 for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it)
207 result.objects.append(it.value());
208
209 const auto scripts = reader.scripts();
210 for (const auto &script : scripts) {
211 const QString filePath = path + QLatin1Char('/') + script.fileName;
212 auto mo = result.scripts.find(script.fileName);
213 if (mo == result.scripts.end())
214 mo = result.scripts.insert(script.fileName, { localFile2ScopeTree(filePath), {} });
215
216 mo->exports.append(QQmlJSScope::Export(
217 reader.typeNamespace(), script.nameSpace,
218 script.version, QTypeRevision()));
219 }
220 return result;
221}
222
223QQmlJSImporter::Import QQmlJSImporter::readDirectory(const QString &directory)
224{
225 Import import;
226 if (directory.startsWith(u':')) {
227 if (m_mapper) {
228 const auto resources = m_mapper->filter(
230 for (const auto &entry : resources) {
231 const QString name = QFileInfo(entry.resourcePath).baseName();
232 if (name.front().isUpper()) {
233 import.objects.append({
234 localFile2ScopeTree(entry.filePath),
236 });
237 }
238 }
239 } else {
240 qWarning() << "Cannot read files from resource directory" << directory
241 << "because no resource file mapper was provided";
242 }
243
244 return import;
245 }
246
248 directory,
249 QStringList() << QLatin1String("*.qml"),
251 };
252 while (it.hasNext()) {
253 QString name = it.nextFileInfo().completeBaseName();
254
255 // Non-uppercase names cannot be imported anyway.
256 if (!name.front().isUpper())
257 continue;
258
259 // .ui.qml is fine
260 if (name.endsWith(u".ui"))
261 name = name.chopped(3);
262
263 // Names with dots in them cannot be imported either.
264 if (name.contains(u'.'))
265 continue;
266
267 import.objects.append({
268 localFile2ScopeTree(it.filePath()),
270 });
271 }
272 return import;
273}
274
275void QQmlJSImporter::importDependencies(const QQmlJSImporter::Import &import,
276 QQmlJSImporter::AvailableTypes *types,
277 const QString &prefix, QTypeRevision version,
278 bool isDependency)
279{
280 // Import the dependencies with an invalid prefix. The prefix will never be matched by actual
281 // QML code but the C++ types will be visible.
282 for (auto const &dependency : std::as_const(import.dependencies))
283 importHelper(dependency.module, types, QString(), dependency.version, true);
284
285 bool hasOptionalImports = false;
286 for (auto const &import : std::as_const(import.imports)) {
288 hasOptionalImports = true;
289 if (!m_useOptionalImports) {
290 continue;
291 }
292
294 continue;
295 }
296
297 importHelper(import.module, types, isDependency ? QString() : prefix,
298 (import.flags & QQmlDirParser::Import::Auto) ? version : import.version,
299 isDependency);
300 }
301
302 if (hasOptionalImports && !m_useOptionalImports) {
303 m_warnings.append(
304 { u"%1 uses optional imports which are not supported. Some types might not be found."_s
305 .arg(import.name),
307 }
308}
309
310static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry,
311 const QQmlJSScope::Import &importDescription)
312{
313 const QTypeRevision importVersion = importDescription.version();
314 const QTypeRevision exportVersion = exportEntry.version();
315 if (!importVersion.hasMajorVersion())
316 return true;
317 if (importVersion.majorVersion() != exportVersion.majorVersion())
318 return false;
319 return !importVersion.hasMinorVersion()
320 || exportVersion.minorVersion() <= importVersion.minorVersion();
321}
322
323void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription,
324 const QQmlJSImporter::Import &import,
325 QQmlJSImporter::AvailableTypes *types)
326{
327 // In the list of QML types we prefix unresolvable QML names with $anonymous$, and C++
328 // names with $internal$. This is to avoid clashes between them.
329 // In the list of C++ types we insert types that don't have a C++ name as their
330 // QML name prefixed with $anonymous$.
331 const QString anonPrefix = QStringLiteral("$anonymous$");
332 const QString internalPrefix = QStringLiteral("$internal$");
333 const QString modulePrefix = QStringLiteral("$module$");
335
336 const auto insertExports = [&](const QQmlJSExportedScope &val, const QString &cppName) {
337 QQmlJSScope::Export bestExport;
338
339 // Resolve conflicting qmlNames within an import
340 for (const auto &valExport : val.exports) {
341 const QString qmlName = prefixedName(importDescription.prefix(), valExport.type());
342 if (!isVersionAllowed(valExport, importDescription))
343 continue;
344
345 // Even if the QML name is overridden by some other type, we still want
346 // to insert the C++ type, with the highest revision available.
347 if (!bestExport.isValid() || valExport.version() > bestExport.version())
348 bestExport = valExport;
349
350 const auto it = types->qmlNames.types().find(qmlName);
351 if (it != types->qmlNames.types().end()) {
352
353 // The same set of exports can declare the same name multiple times for different
354 // versions. That's the common thing and we would just continue here when we hit
355 // it again after having inserted successfully once.
356 // However, it can also declare *different* names. Then we need to do the whole
357 // thing again.
358 if (it->scope == val.scope && it->revision == valExport.version())
359 continue;
360
361 const auto existingExports = seenExports.value(qmlName);
362 enum { LowerVersion, SameVersion, HigherVersion } seenVersion = LowerVersion;
363 for (const QQmlJSScope::Export &entry : existingExports) {
364 if (!isVersionAllowed(entry, importDescription))
365 continue;
366
367 if (valExport.version() < entry.version()) {
368 seenVersion = HigherVersion;
369 break;
370 }
371
372 if (seenVersion == LowerVersion && valExport.version() == entry.version())
373 seenVersion = SameVersion;
374 }
375
376 switch (seenVersion) {
377 case LowerVersion:
378 break;
379 case SameVersion: {
380 m_warnings.append({
381 QStringLiteral("Ambiguous type detected. "
382 "%1 %2.%3 is defined multiple times.")
383 .arg(qmlName)
384 .arg(valExport.version().majorVersion())
385 .arg(valExport.version().minorVersion()),
388 });
389
390 // Invalidate the type. We don't know which one to use.
391 types->qmlNames.clearType(qmlName);
392 continue;
393 }
394 case HigherVersion:
395 continue;
396 }
397 }
398
399 types->qmlNames.setType(qmlName, { val.scope, valExport.version() });
400 seenExports[qmlName].append(valExport);
401 }
402
403 const QTypeRevision bestRevision = bestExport.isValid()
404 ? bestExport.revision()
406 types->cppNames.setType(cppName, { val.scope, bestRevision });
407
408 const QTypeRevision bestVersion = bestExport.isValid()
409 ? bestExport.version()
411 types->qmlNames.setType(prefixedName(internalPrefix, cppName), { val.scope, bestVersion });
412 };
413
414 // Empty type means "this is the prefix"
415 if (!importDescription.prefix().isEmpty())
416 types->qmlNames.setType(importDescription.prefix(), {});
417
418 // Add a marker to show that this module has been imported
419 if (!importDescription.isDependency())
420 types->qmlNames.setType(prefixedName(modulePrefix, importDescription.name()), {});
421
422 if (!importDescription.isDependency()) {
423 if (import.isStaticModule)
424 types->staticModules << import.name;
425
426 if (import.isSystemModule)
427 types->hasSystemModule = true;
428 }
429
430 for (auto it = import.scripts.begin(); it != import.scripts.end(); ++it) {
431 // You cannot have a script without an export
432 Q_ASSERT(!it->exports.isEmpty());
433 insertExports(*it, prefixedName(anonPrefix, internalName(it->scope)));
434 }
435
436 // add objects
437 for (const auto &val : import.objects) {
438 const QString cppName = isComposite(val.scope)
439 ? prefixedName(anonPrefix, internalName(val.scope))
440 : internalName(val.scope);
441
442 if (val.exports.isEmpty()) {
443 // Insert an unresolvable dummy name
444 types->qmlNames.setType(
445 prefixedName(internalPrefix, cppName), { val.scope, QTypeRevision() });
446 types->cppNames.setType(cppName, { val.scope, QTypeRevision() });
447 } else {
448 insertExports(val, cppName);
449 }
450 }
451
452 /* We need to create a temporary AvailableTypes instance here to make builtins available as
453 QQmlJSScope::resolveTypes relies on them being available. They cannot be part of the regular
454 types as they would keep overwriting existing types when loaded from cache.
455 This is only a problem with builtin types as only builtin types can be overridden by any
456 sibling import. Consider the following qmldir:
457
458 module Things
459 import QtQml 2.0
460 import QtQuick.LocalStorage auto
461
462 The module "Things" sees QtQml's definition of Qt, not the builtins', even though
463 QtQuick.LocalStorage does not depend on QtQml and is imported afterwards. Conversely:
464
465 module Stuff
466 import ModuleOverridingQObject
467 import QtQuick
468
469 The module "Stuff" sees QtQml's definition of QObject (via QtQuick), even if
470 ModuleOverridingQObject has overridden it.
471 */
472
473 QQmlJSImporter::AvailableTypes tempTypes(builtinImportHelper().cppNames);
474 tempTypes.cppNames.addTypes(types->cppNames);
475
476 // At present, there are corner cases that couldn't be resolved in a single
477 // pass of resolveTypes() (e.g. QQmlEasingEnums::Type). However, such cases
478 // only happen when enumerations are involved, thus the strategy is to
479 // resolve enumerations (which can potentially create new child scopes)
480 // before resolving the type fully
481 const QQmlJSScope::ConstPtr arrayType = tempTypes.cppNames.type(u"Array"_s).scope;
482 for (auto it = import.objects.begin(); it != import.objects.end(); ++it) {
483 if (!it->scope.factory()) {
484 QQmlJSScope::resolveEnums(it->scope, tempTypes.cppNames);
485 QQmlJSScope::resolveList(it->scope, arrayType);
486 }
487 }
488
489 for (const auto &val : std::as_const(import.objects)) {
490 // Otherwise we have already done it in localFile2ScopeTree()
491 if (!val.scope.factory() && val.scope->baseType().isNull()) {
492
493 // Composite types use QML names, and we should have resolved those already.
494 // ... except that old qmltypes files might specify composite types with C++ names.
495 // Warn about those.
496 if (val.scope->isComposite()) {
497 m_warnings.append({
498 QStringLiteral("Found incomplete composite type %1. Do not use qmlplugindump.")
499 .arg(val.scope->internalName()),
502 });
503 }
504
505 QQmlJSScope::resolveNonEnumTypes(val.scope, tempTypes.cppNames);
506 }
507 }
508}
509
514{
515 return builtinImportHelper().qmlNames;
516}
517
518
519QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
520{
521 if (m_builtins)
522 return *m_builtins;
523
524 AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {}));
525
527 result.name = QStringLiteral("QML");
528
529 QStringList qmltypesFiles = { QStringLiteral("builtins.qmltypes"),
530 QStringLiteral("jsroot.qmltypes") };
531 const auto importBuiltins = [&](const QStringList &imports) {
532 for (auto const &dir : imports) {
533 QDirIterator it { dir, qmltypesFiles, QDir::NoFilter };
534 while (it.hasNext() && !qmltypesFiles.isEmpty()) {
535 readQmltypes(it.next(), &result.objects, &result.dependencies);
536 qmltypesFiles.removeOne(it.fileName());
537 }
538 setQualifiedNamesOn(result);
539 importDependencies(result, &builtins);
540
541 if (qmltypesFiles.isEmpty())
542 return;
543 }
544 };
545
546 importBuiltins(m_importPaths);
547 if (!qmltypesFiles.isEmpty()) {
548 const QString pathsString =
549 m_importPaths.isEmpty() ? u"<empty>"_s : m_importPaths.join(u"\n\t");
550 m_warnings.append({ QStringLiteral("Failed to find the following builtins: %1 (so will use "
551 "qrc). Import paths used:\n\t%2")
552 .arg(qmltypesFiles.join(u", "), pathsString),
554 importBuiltins({ u":/qt-project.org/qml/builtins"_s }); // use qrc as a "last resort"
555 }
556 Q_ASSERT(qmltypesFiles.isEmpty()); // since qrc must cover it in all the bad cases
557
558 // Process them together since there they have interdependencies that wouldn't get resolved
559 // otherwise
560 const QQmlJSScope::Import builtinImport(
561 QString(), QStringLiteral("QML"), QTypeRevision::fromVersion(1, 0), false, true);
562
563 QQmlJSScope::ConstPtr intType;
564 QQmlJSScope::ConstPtr arrayType;
565
566 for (const QQmlJSExportedScope &exported : result.objects) {
567 if (exported.scope->internalName() == u"int"_s) {
568 intType = exported.scope;
569 if (!arrayType.isNull())
570 break;
571 } else if (exported.scope->internalName() == u"Array"_s) {
572 arrayType = exported.scope;
573 if (!intType.isNull())
574 break;
575 }
576 }
577
578 Q_ASSERT(intType);
579 Q_ASSERT(arrayType);
580
581 m_builtins = AvailableTypes(
582 ImportedTypes(ImportedTypes::INTERNAL, builtins.cppNames.types(), arrayType));
583 m_builtins->qmlNames
584 = ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(), arrayType);
585
586 processImport(builtinImport, result, &(*m_builtins));
587
588 return *m_builtins;
589}
590
595{
596 for (const auto &file : qmldirFiles) {
597 Import result;
598 QString qmldirName;
599 if (file.endsWith(SlashQmldir)) {
600 result = readQmldir(file.chopped(SlashQmldir.size()));
601 qmldirName = file;
602 } else {
603 m_warnings.append({
604 QStringLiteral("Argument %1 to -i option is not a qmldir file. Assuming qmltypes.")
605 .arg(file),
608 });
609
610 readQmltypes(file, &result.objects, &result.dependencies);
611
612 // Append _FAKE_QMLDIR to our made up qmldir name so that if it ever gets used somewhere
613 // else except for cache lookups, it will blow up due to a missing file instead of
614 // producing weird results.
615 qmldirName = file + QStringLiteral("_FAKE_QMLDIR");
616 }
617
618 m_seenQmldirFiles.insert(qmldirName, result);
619
620 for (const auto &object : std::as_const(result.objects)) {
621 for (const auto &ex : object.exports) {
622 m_seenImports.insert({ex.package(), ex.version()}, qmldirName);
623 // We also have to handle the case that no version is provided
624 m_seenImports.insert({ex.package(), QTypeRevision()}, qmldirName);
625 }
626 }
627 }
628}
629
631 const QString &prefix,
632 QTypeRevision version,
633 QStringList *staticModuleList)
634{
635 const AvailableTypes builtins = builtinImportHelper();
636 AvailableTypes result(builtins.cppNames);
637 if (!importHelper(module, &result, prefix, version)) {
638 m_warnings.append({
639 QStringLiteral("Failed to import %1. Are your import paths set up properly?").arg(module),
642 });
643 }
644
645 // If we imported a system module add all builtin QML types
646 if (result.hasSystemModule) {
647 for (auto nameIt = builtins.qmlNames.types().keyBegin(),
648 end = builtins.qmlNames.types().keyEnd();
649 nameIt != end; ++nameIt)
650 result.qmlNames.setType(prefixedName(prefix, *nameIt), builtins.qmlNames.type(*nameIt));
651 }
652
653 if (staticModuleList)
654 *staticModuleList << result.staticModules;
655
656 return result.qmlNames;
657}
658
660{
661 return builtinImportHelper().cppNames;
662}
663
664bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
665 const QString &prefix, QTypeRevision version, bool isDependency,
666 bool isFile)
667{
668 // QtQuick/Controls and QtQuick.Controls are the same module
669 const QString moduleCacheName = QString(module).replace(u'/', u'.');
670
671 if (isDependency)
672 Q_ASSERT(prefix.isEmpty());
673
674 const QQmlJSScope::Import cacheKey(prefix, moduleCacheName, version, isFile, isDependency);
675
676 auto getTypesFromCache = [&]() -> bool {
677 if (!m_cachedImportTypes.contains(cacheKey))
678 return false;
679
680 const auto &cacheEntry = m_cachedImportTypes[cacheKey];
681
682 types->cppNames.addTypes(cacheEntry->cppNames);
683 types->staticModules << cacheEntry->staticModules;
684 types->hasSystemModule |= cacheEntry->hasSystemModule;
685
686 // No need to import qml names for dependencies
687 if (!isDependency)
688 types->qmlNames.addTypes(cacheEntry->qmlNames);
689
690 return true;
691 };
692
693 // The QML module only contains builtins and is not registered declaratively, so ignore requests
694 // for importing it
695 if (module == u"QML"_s)
696 return true;
697
698 if (getTypesFromCache())
699 return true;
700
702 new QQmlJSImporter::AvailableTypes(
703 ImportedTypes(ImportedTypes::INTERNAL, {}, types->cppNames.arrayType())));
704 m_cachedImportTypes[cacheKey] = cacheTypes;
705
706 const QPair<QString, QTypeRevision> importId { module, version };
707 const auto it = m_seenImports.constFind(importId);
708
709 if (it != m_seenImports.constEnd()) {
710 if (it->isEmpty())
711 return false;
712
713 Q_ASSERT(m_seenQmldirFiles.contains(*it));
714 const QQmlJSImporter::Import import = m_seenQmldirFiles.value(*it);
715
716 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
717 processImport(cacheKey, import, cacheTypes.get());
718
719 const bool typesFromCache = getTypesFromCache();
720 Q_ASSERT(typesFromCache);
721 return typesFromCache;
722 }
723
724 QStringList modulePaths;
725 if (isFile) {
726 const auto import = readDirectory(module);
727 m_seenQmldirFiles.insert(module, import);
728 m_seenImports.insert(importId, module);
729 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
730 processImport(cacheKey, import, cacheTypes.get());
731
732 // Try to load a qmldir below, on top of the directory import.
733 modulePaths.append(module);
734 } else {
735 modulePaths = qQmlResolveImportPaths(module, m_importPaths, version);
736 }
737
738 for (auto const &modulePath : modulePaths) {
739 QString qmldirPath;
740 if (modulePath.startsWith(u':')) {
741 if (m_mapper) {
742 const QString resourcePath = modulePath.mid(
743 1, modulePath.endsWith(u'/') ? modulePath.size() - 2 : -1)
744 + SlashQmldir;
745 const auto entry = m_mapper->entry(
747 qmldirPath = entry.filePath;
748 } else {
749 qWarning() << "Cannot read files from resource directory" << modulePath
750 << "because no resource file mapper was provided";
751 }
752 } else {
753 qmldirPath = modulePath + SlashQmldir;
754 }
755
756 const auto it = m_seenQmldirFiles.constFind(qmldirPath);
757 if (it != m_seenQmldirFiles.constEnd()) {
758 const QQmlJSImporter::Import import = *it;
759 m_seenImports.insert(importId, qmldirPath);
760 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
761 processImport(cacheKey, import, cacheTypes.get());
762
763 const bool typesFromCache = getTypesFromCache();
764 Q_ASSERT(typesFromCache);
765 return typesFromCache;
766 }
767
768 const QFileInfo file(qmldirPath);
769 if (file.exists()) {
770 const auto import = readQmldir(file.canonicalPath());
771 setQualifiedNamesOn(import);
772 m_seenQmldirFiles.insert(qmldirPath, import);
773 m_seenImports.insert(importId, qmldirPath);
774 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
775
776 // Potentially merges with the result of readDirectory() above.
777 processImport(cacheKey, import, cacheTypes.get());
778
779 const bool typesFromCache = getTypesFromCache();
780 Q_ASSERT(typesFromCache);
781 return typesFromCache;
782 }
783 }
784
785 if (isFile) {
786 // We've loaded the directory above
787 const bool typesFromCache = getTypesFromCache();
788 Q_ASSERT(typesFromCache);
789 return typesFromCache;
790 }
791
792 m_seenImports.insert(importId, QString());
793 return false;
794}
795
796QQmlJSScope::Ptr QQmlJSImporter::localFile2ScopeTree(const QString &filePath)
797{
798 const auto seen = m_importedFiles.find(filePath);
799 if (seen != m_importedFiles.end())
800 return *seen;
801
802 return *m_importedFiles.insert(filePath, {
805 new QDeferredFactory<QQmlJSScope>(this, filePath))
806 });
807}
808
810{
811 return localFile2ScopeTree(file);
812}
813
815 const QString &directory, const QString &prefix)
816{
817 const AvailableTypes builtins = builtinImportHelper();
818 QQmlJSImporter::AvailableTypes types(
820 ImportedTypes::INTERNAL, {}, builtins.cppNames.arrayType()));
821 importHelper(directory, &types, prefix, QTypeRevision(), false, true);
822 return types.qmlNames;
823}
824
826{
827 m_importPaths = importPaths;
828
829 // We have to get rid off all cache elements directly referencing modules, since changing
830 // importPaths might change which module is found first
831 m_seenImports.clear();
832 m_cachedImportTypes.clear();
833 // Luckily this doesn't apply to m_seenQmldirFiles
834}
835
837{
838 m_seenImports.clear();
839 m_cachedImportTypes.clear();
840 m_seenQmldirFiles.clear();
841 m_importedFiles.clear();
842}
843
845{
846 return m_builtins->cppNames.type(u"GlobalObject"_s).scope;
847}
848
849void QQmlJSImporter::setQualifiedNamesOn(const Import &import)
850{
851 for (auto &object : import.objects) {
852 if (object.exports.isEmpty())
853 continue;
854 const QString qualifiedName = QQmlJSScope::qualifiedNameFrom(
855 import.name, object.exports.first().type(),
856 object.exports.first().revision(),
857 object.exports.last().revision());
858 if (auto *factory = object.scope.factory()) {
859 factory->setQualifiedName(qualifiedName);
860 factory->setModuleName(import.name);
861 } else {
862 object.scope->setQualifiedName(qualifiedName);
863 object.scope->setModuleName(import.name);
864 }
865 }
866}
867
868std::unique_ptr<QQmlJSImportVisitor>
870 QQmlJSLogger *logger, const QString &implicitImportDirectory,
871 const QStringList &qmldirFiles)
872{
873 return std::unique_ptr<QQmlJSImportVisitor>(
874 m_createImportVisitor(target, importer, logger, implicitImportDirectory, qmldirFiles));
875}
876
The QDirIterator class provides an iterator for directory entrylists.
@ NoFilter
Definition qdir.h:45
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString baseName() const
Returns the base name of the file without the path.
bool isRelative() const
Returns true if the file path is relative, otherwise returns false (i.e.
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
\inmodule QtCore
Definition qhash.h:818
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1202
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
T value(const Key &key) const noexcept
Definition qhash.h:1044
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:949
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
constexpr qsizetype size() const noexcept
Definition qlist.h:74
iterator begin()
Definition qlist.h:608
void append(parameter_type t)
Definition qlist.h:441
bool parse(const QString &source)
url is used for generating errors.
QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper, bool useOptionalImports=false)
void importQmldirs(const QStringList &qmltypesFiles)
Imports types from the specified qmltypesFiles.
QQmlJSScope::ContextualTypes ImportedTypes
std::unique_ptr< QQmlJSImportVisitor > makeImportVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger, const QString &implicitImportDirectory, const QStringList &qmldirFiles=QStringList())
QStringList importPaths() const
ImportedTypes builtinInternalNames()
QQmlJSScope::Ptr importFile(const QString &file)
ImportedTypes importBuiltins()
Imports builtins.qmltypes and jsroot.qmltypes found in any of the import paths.
friend class QDeferredFactory< QQmlJSScope >
void setImportPaths(const QStringList &importPaths)
ImportedTypes importDirectory(const QString &directory, const QString &prefix=QString())
ImportedTypes importModule(const QString &module, const QString &prefix=QString(), QTypeRevision version=QTypeRevision(), QStringList *staticModuleList=nullptr)
QQmlJSScope::ConstPtr jsGlobalObject() const
QTypeRevision revision() const
QTypeRevision version() const
QString name() const
bool isDependency() const
QString prefix() const
QTypeRevision version() const
Tracks the types for the QmlCompiler.
bool isComposite() const
static QQmlJSScope::Ptr create()
QString internalName() const
static void resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes, QSet< QString > *usedTypes=nullptr)
static void resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType)
static QString qualifiedNameFrom(const QString &moduleName, const QString &typeName, const QTypeRevision &firstRevision, const QTypeRevision &lastRevision)
static void resolveNonEnumTypes(const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes, QSet< QString > *usedTypes=nullptr)
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
bool isEmpty() const
Definition qset.h:52
iterator find(const T &value)
Definition qset.h:159
\inmodule QtCore
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
ushort toUShort(bool *ok=nullptr, int base=10) const
Returns the string converted to an {unsigned short} using base base, which is 10 by default and must ...
Definition qstring.h:658
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString & append(QChar c)
Definition qstring.cpp:3227
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QString trimmed() const &
Definition qstring.h:380
\inmodule QtCore
static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
Produces a QTypeRevision from the given majorVersion and minorVersion, both of which need to be a val...
constexpr bool hasMinorVersion() const
Returns true if the minor version is known, otherwise false.
static constexpr QTypeRevision zero()
Produces a QTypeRevision with major and minor version {0}.
constexpr bool hasMajorVersion() const
Returns true if the major version is known, otherwise false.
constexpr quint8 minorVersion() const
Returns the minor version encoded in the revision.
static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
Produces a QTypeRevision from the given majorVersion with an invalid minor version.
constexpr quint8 majorVersion() const
Returns the major version encoded in the revision.
QSet< QString >::iterator it
auto mo
[7]
@ Auto
Definition qprint_p.h:85
Combined button and popup list for selecting options.
QImageReader reader("image.png")
[1]
std::pair< T1, T2 > QPair
@ QtCriticalMsg
Definition qlogging.h:32
@ QtWarningMsg
Definition qlogging.h:31
#define qWarning
Definition qlogging.h:162
GLint GLenum GLint components
GLuint GLuint end
GLsizei GLenum GLenum * types
GLfloat GLfloat f
GLint left
GLenum target
GLbitfield flags
GLuint name
GLuint GLfloat * val
GLuint entry
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
static qreal dot(const QPointF &a, const QPointF &b)
QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths, QTypeRevision version)
static const QLatin1String SlashPluginsDotQmltypes
static QQmlDirParser createQmldirParserForFile(const QString &filename)
static bool isComposite(const QQmlJSScope::ConstPtr &scope)
static const QString prefixedName(const QString &prefix, const QString &name)
static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry, const QQmlJSScope::Import &importDescription)
static QString internalName(const QQmlJSScope::ConstPtr &scope)
static const QLatin1String SlashQmldir
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
QFile file
[0]
QString dir
[11]
QDataWidgetMapper * mapper
[0]
QItemEditorFactory * factory
\inmodule QtCore \reentrant
Definition qchar.h:17
Entry entry(const Filter &filter) const
static Filter resourceFileFilter(const QString &file)
static Filter resourceQmlDirectoryFilter(const QString &directory)
QList< Entry > filter(const Filter &filter) const