Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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 PluginsDotQmltypes = QLatin1String("plugins.qmltypes");
21
22
24 bool isDependency) :
25 m_prefix(std::move(prefix)),
26 m_name(std::move(name)),
27 m_version(version),
28 m_isFile(isFile),
29 m_isDependency(isDependency)
30{
31}
32
34{
35 return !m_name.isEmpty();
36}
37
38static const QString prefixedName(const QString &prefix, const QString &name)
39{
40 Q_ASSERT(!prefix.endsWith(u'.'));
41 return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name);
42}
43
44QQmlDirParser QQmlJSImporter::createQmldirParserForFile(const QString &filename)
45{
46 QFile f(filename);
47 QQmlDirParser parser;
48 if (f.open(QFile::ReadOnly)) {
49 parser.parse(QString::fromUtf8(f.readAll()));
50 } else {
51 m_warnings.append({
52 QStringLiteral("Could not open qmldir file: ")
53 + filename,
56 });
57 }
58
59 return parser;
60}
61
62void QQmlJSImporter::readQmltypes(
63 const QString &filename, QList<QQmlJSExportedScope> *objects,
64 QList<QQmlDirParser::Import> *dependencies)
65{
66 const QFileInfo fileInfo(filename);
67 if (!fileInfo.exists()) {
68 m_warnings.append({
69 QStringLiteral("QML types file does not exist: ") + filename,
72 });
73 return;
74 }
75
76 if (fileInfo.isDir()) {
77 m_warnings.append({
78 QStringLiteral("QML types file cannot be a directory: ") + filename,
81 });
82 return;
83 }
84
85 QFile file(filename);
87 m_warnings.append({
88 QStringLiteral("QML types file cannot be opened: ") + filename,
91 });
92 return;
93 }
94
96 QStringList dependencyStrings;
97 auto succ = reader(objects, &dependencyStrings);
98 if (!succ)
99 m_warnings.append({ reader.errorMessage(), QtCriticalMsg, QQmlJS::SourceLocation() });
100
101 const QString warningMessage = reader.warningMessage();
102 if (!warningMessage.isEmpty())
103 m_warnings.append({ warningMessage, QtWarningMsg, QQmlJS::SourceLocation() });
104
105 if (dependencyStrings.isEmpty())
106 return;
107
108 m_warnings.append({
109 QStringLiteral("Found deprecated dependency specifications in %1."
110 "Specify dependencies in qmldir and use qmltyperegistrar "
111 "to generate qmltypes files without dependencies.")
112 .arg(filename),
115 });
116
117 for (const QString &dependency : std::as_const(dependencyStrings)) {
118 const auto blank = dependency.indexOf(u' ');
119 if (blank < 0) {
120 dependencies->append(QQmlDirParser::Import(dependency, {},
122 continue;
123 }
124
125 const QString module = dependency.left(blank);
126 const QString versionString = dependency.mid(blank + 1).trimmed();
127 if (versionString == QStringLiteral("auto")) {
128 dependencies->append(QQmlDirParser::Import(module, {}, QQmlDirParser::Import::Auto));
129 continue;
130 }
131
132 const auto dot = versionString.indexOf(u'.');
133
134 const QTypeRevision version = dot < 0
135 ? QTypeRevision::fromMajorVersion(versionString.toUShort())
136 : QTypeRevision::fromVersion(versionString.left(dot).toUShort(),
137 versionString.mid(dot + 1).toUShort());
138
139 dependencies->append(QQmlDirParser::Import(module, version,
141 }
142}
143
145{
146 if (const auto *factory = scope.factory())
147 return factory->internalName();
148 return scope->internalName();
149}
150
151static bool isComposite(const QQmlJSScope::ConstPtr &scope)
152{
153 // The only thing the factory can do is load a composite type.
154 return scope.factory() || scope->isComposite();
155}
156
158{
159 return isComposite(scope)
160 ? QStringList()
161 : scope->aliases();
162}
163
165 bool useOptionalImports)
166 : m_importPaths(importPaths),
167 m_mapper(mapper),
168 m_useOptionalImports(useOptionalImports),
169 m_importVisitor([](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
171 auto visitor = std::unique_ptr<QQmlJS::AST::BaseVisitor>(new QQmlJSImportVisitor(
172 p.m_target, self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles));
173 QQmlJS::AST::Node::accept(rootNode, visitor.get());
174 })
175{
176}
177
179 const QString &qmldirPath, const QString &prefer, QQmlJSResourceFileMapper *mapper)
180{
181 if (prefer.isEmpty())
182 return qmldirPath;
183
184 if (!prefer.endsWith(u'/')) {
185 qWarning() << "Ignoring invalid prefer path" << prefer << "(has to end with slash)";
186 return qmldirPath;
187 }
188
189 if (prefer.startsWith(u':')) {
190 // Resource path: Resolve via resource file mapper if possible.
191 if (!mapper)
192 return qmldirPath;
193
194 Q_ASSERT(prefer.endsWith(u'/'));
195 const auto entry = mapper->entry(
197
198 // This can be empty if the .qrc files does not belong to this module.
199 // In that case we trust the given qmldir file.
200 return entry.filePath.endsWith(SlashQmldir)
201 ? entry.filePath
202 : qmldirPath;
203 }
204
205 // Host file system path. This should be rare. We don't generate it.
206 const QFileInfo f(prefer + SlashQmldir);
207 const QString canonical = f.canonicalFilePath();
208 if (canonical.isEmpty()) {
209 qWarning() << "No qmldir at" << prefer;
210 return qmldirPath;
211 }
212 return canonical;
213}
214
215QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &modulePath)
216{
217 const QString moduleQmldirPath = modulePath + SlashQmldir;
218 auto reader = createQmldirParserForFile(moduleQmldirPath);
219
220 const QString resolvedQmldirPath
221 = resolvePreferredPath(moduleQmldirPath, reader.preferredPath(), m_mapper);
222 if (resolvedQmldirPath != moduleQmldirPath)
223 reader = createQmldirParserForFile(resolvedQmldirPath);
224
225 // Leave the trailing slash
226 Q_ASSERT(resolvedQmldirPath.endsWith(SlashQmldir));
227 QStringView resolvedPath = QStringView(resolvedQmldirPath).chopped(SlashQmldir.size() - 1);
228
230 result.name = reader.typeNamespace();
231
232 result.isStaticModule = reader.isStaticModule();
233 result.isSystemModule = reader.isSystemModule();
234 result.imports.append(reader.imports());
235 result.dependencies.append(reader.dependencies());
236
237 const auto typeInfos = reader.typeInfos();
238 for (const auto &typeInfo : typeInfos) {
239 const QString typeInfoPath = QFileInfo(typeInfo).isRelative()
240 ? resolvedPath + typeInfo
241 : typeInfo;
242 readQmltypes(typeInfoPath, &result.objects, &result.dependencies);
243 }
244
245 if (typeInfos.isEmpty() && !reader.plugins().isEmpty()) {
246 const QString defaultTypeInfoPath = resolvedPath + PluginsDotQmltypes;
247 if (QFile::exists(defaultTypeInfoPath)) {
248 m_warnings.append({
249 QStringLiteral("typeinfo not declared in qmldir file: ")
250 + defaultTypeInfoPath,
253 });
254 readQmltypes(defaultTypeInfoPath, &result.objects, &result.dependencies);
255 }
256 }
257
258 QHash<QString, QQmlJSExportedScope> qmlComponents;
259 const auto components = reader.components();
260 for (auto it = components.begin(), end = components.end(); it != end; ++it) {
261 const QString filePath = resolvedPath + it->fileName;
262 if (!QFile::exists(filePath)) {
263 m_warnings.append({
264 it->fileName + QStringLiteral(" is listed as component in ")
265 + resolvedQmldirPath
266 + QStringLiteral(" but does not exist.\n"),
269 });
270 continue;
271 }
272
273 auto mo = qmlComponents.find(it->fileName);
274 if (mo == qmlComponents.end()) {
275 QQmlJSScope::Ptr imported = localFile2ScopeTree(filePath);
276 if (auto *factory = imported.factory()) {
277 if (it->singleton) {
278 factory->setIsSingleton(true);
279 }
280 }
281 mo = qmlComponents.insert(it->fileName, {imported, QList<QQmlJSScope::Export>() });
282 }
283
284 mo->exports.append(QQmlJSScope::Export(
285 reader.typeNamespace(), it.key(), it->version, QTypeRevision()));
286 }
287 for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it)
288 result.objects.append(it.value());
289
290 const auto scripts = reader.scripts();
291 for (const auto &script : scripts) {
292 const QString filePath = resolvedPath + script.fileName;
293 auto mo = result.scripts.find(script.fileName);
294 if (mo == result.scripts.end())
295 mo = result.scripts.insert(script.fileName, { localFile2ScopeTree(filePath), {} });
296
297 mo->exports.append(QQmlJSScope::Export(
298 reader.typeNamespace(), script.nameSpace,
299 script.version, QTypeRevision()));
300 }
301 return result;
302}
303
304QQmlJSImporter::Import QQmlJSImporter::readDirectory(const QString &directory)
305{
306 Import import;
307 if (directory.startsWith(u':')) {
308 if (m_mapper) {
309 const auto resources = m_mapper->filter(
311 for (const auto &entry : resources) {
312 const QString name = QFileInfo(entry.resourcePath).baseName();
313 if (name.front().isUpper()) {
314 import.objects.append({
315 localFile2ScopeTree(entry.filePath),
317 });
318 }
319 }
320 } else {
321 qWarning() << "Cannot read files from resource directory" << directory
322 << "because no resource file mapper was provided";
323 }
324
325 return import;
326 }
327
329 directory,
330 QStringList() << QLatin1String("*.qml"),
332 };
333 while (it.hasNext()) {
334 QString name = it.nextFileInfo().completeBaseName();
335
336 // Non-uppercase names cannot be imported anyway.
337 if (!name.front().isUpper())
338 continue;
339
340 // .ui.qml is fine
341 if (name.endsWith(u".ui"))
342 name = name.chopped(3);
343
344 // Names with dots in them cannot be imported either.
345 if (name.contains(u'.'))
346 continue;
347
348 import.objects.append({
349 localFile2ScopeTree(it.filePath()),
351 });
352 }
353 return import;
354}
355
356void QQmlJSImporter::importDependencies(const QQmlJSImporter::Import &import,
357 QQmlJSImporter::AvailableTypes *types,
358 const QString &prefix, QTypeRevision version,
359 bool isDependency)
360{
361 // Import the dependencies with an invalid prefix. The prefix will never be matched by actual
362 // QML code but the C++ types will be visible.
363 for (auto const &dependency : std::as_const(import.dependencies))
364 importHelper(dependency.module, types, QString(), dependency.version, true);
365
366 bool hasOptionalImports = false;
367 for (auto const &import : std::as_const(import.imports)) {
369 hasOptionalImports = true;
370 if (!m_useOptionalImports) {
371 continue;
372 }
373
375 continue;
376 }
377
378 importHelper(import.module, types, isDependency ? QString() : prefix,
379 (import.flags & QQmlDirParser::Import::Auto) ? version : import.version,
380 isDependency);
381 }
382
383 if (hasOptionalImports && !m_useOptionalImports) {
384 m_warnings.append(
385 { u"%1 uses optional imports which are not supported. Some types might not be found."_s
386 .arg(import.name),
388 }
389}
390
391static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry,
392 const QQmlJS::Import &importDescription)
393{
394 const QTypeRevision importVersion = importDescription.version();
395 const QTypeRevision exportVersion = exportEntry.version();
396 if (!importVersion.hasMajorVersion())
397 return true;
398 if (importVersion.majorVersion() != exportVersion.majorVersion())
399 return false;
400 return !importVersion.hasMinorVersion()
401 || exportVersion.minorVersion() <= importVersion.minorVersion();
402}
403
404void QQmlJSImporter::processImport(const QQmlJS::Import &importDescription,
405 const QQmlJSImporter::Import &import,
406 QQmlJSImporter::AvailableTypes *types)
407{
408 // In the list of QML types we prefix unresolvable QML names with $anonymous$, and C++
409 // names with $internal$. This is to avoid clashes between them.
410 // In the list of C++ types we insert types that don't have a C++ name as their
411 // QML name prefixed with $anonymous$.
412 const QString anonPrefix = QStringLiteral("$anonymous$");
413 const QString internalPrefix = QStringLiteral("$internal$");
414 const QString modulePrefix = QStringLiteral("$module$");
415 QHash<QString, QList<QQmlJSScope::Export>> seenExports;
416
417 const auto insertAliases = [&](const QQmlJSScope::ConstPtr &scope, QTypeRevision revision) {
418 const QStringList cppAliases = aliases(scope);
419 for (const QString &alias : cppAliases)
420 types->cppNames.setType(alias, { scope, revision });
421 };
422
423 const auto insertExports = [&](const QQmlJSExportedScope &val, const QString &cppName) {
424 QQmlJSScope::Export bestExport;
425
426 // Resolve conflicting qmlNames within an import
427 for (const auto &valExport : val.exports) {
428 const QString qmlName = prefixedName(importDescription.prefix(), valExport.type());
429 if (!isVersionAllowed(valExport, importDescription))
430 continue;
431
432 // Even if the QML name is overridden by some other type, we still want
433 // to insert the C++ type, with the highest revision available.
434 if (!bestExport.isValid() || valExport.version() > bestExport.version())
435 bestExport = valExport;
436
437 const auto it = types->qmlNames.types().find(qmlName);
438 if (it != types->qmlNames.types().end()) {
439
440 // The same set of exports can declare the same name multiple times for different
441 // versions. That's the common thing and we would just continue here when we hit
442 // it again after having inserted successfully once.
443 // However, it can also declare *different* names. Then we need to do the whole
444 // thing again.
445 if (it->scope == val.scope && it->revision == valExport.version())
446 continue;
447
448 const auto existingExports = seenExports.value(qmlName);
449 enum { LowerVersion, SameVersion, HigherVersion } seenVersion = LowerVersion;
450 for (const QQmlJSScope::Export &entry : existingExports) {
451 if (!isVersionAllowed(entry, importDescription))
452 continue;
453
454 if (valExport.version() < entry.version()) {
455 seenVersion = HigherVersion;
456 break;
457 }
458
459 if (seenVersion == LowerVersion && valExport.version() == entry.version())
460 seenVersion = SameVersion;
461 }
462
463 switch (seenVersion) {
464 case LowerVersion:
465 break;
466 case SameVersion: {
467 m_warnings.append({
468 QStringLiteral("Ambiguous type detected. "
469 "%1 %2.%3 is defined multiple times.")
470 .arg(qmlName)
471 .arg(valExport.version().majorVersion())
472 .arg(valExport.version().minorVersion()),
475 });
476
477 // Invalidate the type. We don't know which one to use.
478 types->qmlNames.clearType(qmlName);
479 continue;
480 }
481 case HigherVersion:
482 continue;
483 }
484 }
485
486 types->qmlNames.setType(qmlName, { val.scope, valExport.version() });
487 seenExports[qmlName].append(valExport);
488 }
489
490 const QTypeRevision bestRevision = bestExport.isValid()
491 ? bestExport.revision()
493 types->cppNames.setType(cppName, { val.scope, bestRevision });
494
495 insertAliases(val.scope, bestRevision);
496
497 const QTypeRevision bestVersion = bestExport.isValid()
498 ? bestExport.version()
500 types->qmlNames.setType(prefixedName(internalPrefix, cppName), { val.scope, bestVersion });
501 };
502
503 // Empty type means "this is the prefix"
504 if (!importDescription.prefix().isEmpty())
505 types->qmlNames.setType(importDescription.prefix(), {});
506
507 // Add a marker to show that this module has been imported
508 if (!importDescription.isDependency())
509 types->qmlNames.setType(prefixedName(modulePrefix, importDescription.name()), {});
510
511 if (!importDescription.isDependency()) {
512 if (import.isStaticModule)
513 types->staticModules << import.name;
514
515 if (import.isSystemModule)
516 types->hasSystemModule = true;
517 }
518
519 for (auto it = import.scripts.begin(); it != import.scripts.end(); ++it) {
520 // You cannot have a script without an export
521 Q_ASSERT(!it->exports.isEmpty());
522 insertExports(*it, prefixedName(anonPrefix, internalName(it->scope)));
523 }
524
525 // add objects
526 for (const auto &val : import.objects) {
527 const QString cppName = isComposite(val.scope)
528 ? prefixedName(anonPrefix, internalName(val.scope))
529 : internalName(val.scope);
530
531 if (val.exports.isEmpty()) {
532 // Insert an unresolvable dummy name
533 types->qmlNames.setType(
534 prefixedName(internalPrefix, cppName), { val.scope, QTypeRevision() });
535 types->cppNames.setType(cppName, { val.scope, QTypeRevision() });
536 insertAliases(val.scope, QTypeRevision());
537 } else {
538 insertExports(val, cppName);
539 }
540 }
541
542 /* We need to create a temporary AvailableTypes instance here to make builtins available as
543 QQmlJSScope::resolveTypes relies on them being available. They cannot be part of the regular
544 types as they would keep overwriting existing types when loaded from cache.
545 This is only a problem with builtin types as only builtin types can be overridden by any
546 sibling import. Consider the following qmldir:
547
548 module Things
549 import QtQml 2.0
550 import QtQuick.LocalStorage auto
551
552 The module "Things" sees QtQml's definition of Qt, not the builtins', even though
553 QtQuick.LocalStorage does not depend on QtQml and is imported afterwards. Conversely:
554
555 module Stuff
556 import ModuleOverridingQObject
557 import QtQuick
558
559 The module "Stuff" sees QtQml's definition of QObject (via QtQuick), even if
560 ModuleOverridingQObject has overridden it.
561 */
562
563 QQmlJSImporter::AvailableTypes tempTypes(builtinImportHelper().cppNames);
564 tempTypes.cppNames.addTypes(types->cppNames);
565
566 // At present, there are corner cases that couldn't be resolved in a single
567 // pass of resolveTypes() (e.g. QQmlEasingEnums::Type). However, such cases
568 // only happen when enumerations are involved, thus the strategy is to
569 // resolve enumerations (which can potentially create new child scopes)
570 // before resolving the type fully
571 const QQmlJSScope::ConstPtr arrayType = tempTypes.cppNames.type(u"Array"_s).scope;
572 for (auto it = import.objects.begin(); it != import.objects.end(); ++it) {
573 if (!it->scope.factory()) {
574 QQmlJSScope::resolveEnums(it->scope, tempTypes.cppNames);
575 QQmlJSScope::resolveList(it->scope, arrayType);
576 }
577 }
578
579 for (const auto &val : std::as_const(import.objects)) {
580 // Otherwise we have already done it in localFile2ScopeTree()
581 if (!val.scope.factory() && val.scope->baseType().isNull()) {
582
583 // Composite types use QML names, and we should have resolved those already.
584 // ... except that old qmltypes files might specify composite types with C++ names.
585 // Warn about those.
586 if (val.scope->isComposite()) {
587 m_warnings.append({
588 QStringLiteral("Found incomplete composite type %1. Do not use qmlplugindump.")
589 .arg(val.scope->internalName()),
592 });
593 }
594
595 QQmlJSScope::resolveNonEnumTypes(val.scope, tempTypes.cppNames);
596 }
597 }
598}
599
604{
605 return builtinImportHelper().qmlNames;
606}
607
608
609QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
610{
611 if (m_builtins)
612 return *m_builtins;
613
614 AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {}));
615
617 result.name = QStringLiteral("QML");
618
619 const auto importBuiltins = [&](const QString &qmltypesFile, const QStringList &imports) {
620 for (auto const &dir : imports) {
621 const QDir importDir(dir);
622 if (!importDir.exists(qmltypesFile))
623 continue;
624
625 readQmltypes(
626 importDir.filePath(qmltypesFile), &result.objects, &result.dependencies);
627 setQualifiedNamesOn(result);
628 importDependencies(result, &builtins);
629 return true;
630 }
631
632 return false;
633 };
634
635 {
636 // If the same name (such as "Qt") appears in the JS root and in the builtins,
637 // we want the builtins to override the JS root. Therefore, process jsroot first.
638 const QStringList builtinsPath{ u":/qt-project.org/qml/builtins"_s };
639 for (const QString qmltypesFile : { "jsroot.qmltypes"_L1, "builtins.qmltypes"_L1 }) {
640 if (!importBuiltins(qmltypesFile, builtinsPath))
641 qFatal() << u"Failed to find the following builtin:" << qmltypesFile;
642 }
643 }
644
645 // Process them together since there they have interdependencies that wouldn't get resolved
646 // otherwise
647 const QQmlJS::Import builtinImport(
648 QString(), QStringLiteral("QML"), QTypeRevision::fromVersion(1, 0), false, true);
649
650 QQmlJSScope::ConstPtr intType;
651 QQmlJSScope::ConstPtr arrayType;
652
653 for (const QQmlJSExportedScope &exported : result.objects) {
654 if (exported.scope->internalName() == u"int"_s) {
655 intType = exported.scope;
656 if (!arrayType.isNull())
657 break;
658 } else if (exported.scope->internalName() == u"Array"_s) {
659 arrayType = exported.scope;
660 if (!intType.isNull())
661 break;
662 }
663 }
664
665 Q_ASSERT(intType);
666 Q_ASSERT(arrayType);
667
668 m_builtins = AvailableTypes(
669 ImportedTypes(ImportedTypes::INTERNAL, builtins.cppNames.types(), arrayType));
670 m_builtins->qmlNames
671 = ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(), arrayType);
672
673 processImport(builtinImport, result, &(*m_builtins));
674
675 return *m_builtins;
676}
677
682{
683 for (const auto &file : qmldirFiles) {
684 Import result;
685 QString qmldirName;
686 if (file.endsWith(SlashQmldir)) {
687 result = readQmldir(file.chopped(SlashQmldir.size()));
688 setQualifiedNamesOn(result);
689 qmldirName = file;
690 } else {
691 m_warnings.append({
692 QStringLiteral("Argument %1 to -i option is not a qmldir file. Assuming qmltypes.")
693 .arg(file),
696 });
697
698 readQmltypes(file, &result.objects, &result.dependencies);
699
700 // Append _FAKE_QMLDIR to our made up qmldir name so that if it ever gets used somewhere
701 // else except for cache lookups, it will blow up due to a missing file instead of
702 // producing weird results.
703 qmldirName = file + QStringLiteral("_FAKE_QMLDIR");
704 }
705
706 m_seenQmldirFiles.insert(qmldirName, result);
707
708 for (const auto &object : std::as_const(result.objects)) {
709 for (const auto &ex : object.exports) {
710 m_seenImports.insert({ex.package(), ex.version()}, qmldirName);
711 // We also have to handle the case that no version is provided
712 m_seenImports.insert({ex.package(), QTypeRevision()}, qmldirName);
713 }
714 }
715 }
716}
717
719 const QString &prefix,
720 QTypeRevision version,
721 QStringList *staticModuleList)
722{
723 const AvailableTypes builtins = builtinImportHelper();
724 AvailableTypes result(builtins.cppNames);
725 if (!importHelper(module, &result, prefix, version)) {
726 m_warnings.append({
727 QStringLiteral("Failed to import %1. Are your import paths set up properly?").arg(module),
730 });
731 }
732
733 // If we imported a system module add all builtin QML types
734 if (result.hasSystemModule) {
735 for (auto nameIt = builtins.qmlNames.types().keyBegin(),
736 end = builtins.qmlNames.types().keyEnd();
737 nameIt != end; ++nameIt)
738 result.qmlNames.setType(prefixedName(prefix, *nameIt), builtins.qmlNames.type(*nameIt));
739 }
740
741 if (staticModuleList)
742 *staticModuleList << result.staticModules;
743
744 return result.qmlNames;
745}
746
748{
749 return builtinImportHelper().cppNames;
750}
751
752bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
753 const QString &prefix, QTypeRevision version, bool isDependency,
754 bool isFile)
755{
756 // QtQuick/Controls and QtQuick.Controls are the same module
757 const QString moduleCacheName = QString(module).replace(u'/', u'.');
758
759 if (isDependency)
760 Q_ASSERT(prefix.isEmpty());
761
762 const QQmlJS::Import cacheKey(prefix, moduleCacheName, version, isFile, isDependency);
763
764 auto getTypesFromCache = [&]() -> bool {
765 if (!m_cachedImportTypes.contains(cacheKey))
766 return false;
767
768 const auto &cacheEntry = m_cachedImportTypes[cacheKey];
769
770 types->cppNames.addTypes(cacheEntry->cppNames);
771 types->staticModules << cacheEntry->staticModules;
772 types->hasSystemModule |= cacheEntry->hasSystemModule;
773
774 // No need to import qml names for dependencies
775 if (!isDependency)
776 types->qmlNames.addTypes(cacheEntry->qmlNames);
777
778 return true;
779 };
780
781 // The QML module only contains builtins and is not registered declaratively, so ignore requests
782 // for importing it
783 if (module == u"QML"_s)
784 return true;
785
786 if (getTypesFromCache())
787 return true;
788
789 auto cacheTypes = QSharedPointer<QQmlJSImporter::AvailableTypes>(
790 new QQmlJSImporter::AvailableTypes(
791 ImportedTypes(ImportedTypes::INTERNAL, {}, types->cppNames.arrayType())));
792 m_cachedImportTypes[cacheKey] = cacheTypes;
793
794 const QPair<QString, QTypeRevision> importId { module, version };
795 const auto it = m_seenImports.constFind(importId);
796
797 if (it != m_seenImports.constEnd()) {
798 if (it->isEmpty())
799 return false;
800
801 Q_ASSERT(m_seenQmldirFiles.contains(*it));
802 const QQmlJSImporter::Import import = m_seenQmldirFiles.value(*it);
803
804 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
805 processImport(cacheKey, import, cacheTypes.get());
806
807 const bool typesFromCache = getTypesFromCache();
808 Q_ASSERT(typesFromCache);
809 return typesFromCache;
810 }
811
812 QStringList modulePaths;
813 if (isFile) {
814 const auto import = readDirectory(module);
815 m_seenQmldirFiles.insert(module, import);
816 m_seenImports.insert(importId, module);
817 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
818 processImport(cacheKey, import, cacheTypes.get());
819
820 // Try to load a qmldir below, on top of the directory import.
821 modulePaths.append(module);
822 } else {
823 modulePaths = qQmlResolveImportPaths(module, m_importPaths, version);
824 }
825
826 for (auto const &modulePath : modulePaths) {
827 QString qmldirPath;
828 if (modulePath.startsWith(u':')) {
829 if (m_mapper) {
830 const QString resourcePath = modulePath.mid(
831 1, modulePath.endsWith(u'/') ? modulePath.size() - 2 : -1)
832 + SlashQmldir;
833 const auto entry = m_mapper->entry(
835 qmldirPath = entry.filePath;
836 } else {
837 qWarning() << "Cannot read files from resource directory" << modulePath
838 << "because no resource file mapper was provided";
839 }
840 } else {
841 qmldirPath = modulePath + SlashQmldir;
842 }
843
844 const auto it = m_seenQmldirFiles.constFind(qmldirPath);
845 if (it != m_seenQmldirFiles.constEnd()) {
846 const QQmlJSImporter::Import import = *it;
847 m_seenImports.insert(importId, qmldirPath);
848 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
849 processImport(cacheKey, import, cacheTypes.get());
850
851 const bool typesFromCache = getTypesFromCache();
852 Q_ASSERT(typesFromCache);
853 return typesFromCache;
854 }
855
856 const QFileInfo file(qmldirPath);
857 if (file.exists()) {
858 const auto import = readQmldir(file.canonicalPath());
859 setQualifiedNamesOn(import);
860 m_seenQmldirFiles.insert(qmldirPath, import);
861 m_seenImports.insert(importId, qmldirPath);
862 importDependencies(import, cacheTypes.get(), prefix, version, isDependency);
863
864 // Potentially merges with the result of readDirectory() above.
865 processImport(cacheKey, import, cacheTypes.get());
866
867 const bool typesFromCache = getTypesFromCache();
868 Q_ASSERT(typesFromCache);
869 return typesFromCache;
870 }
871 }
872
873 if (isFile) {
874 // We've loaded the directory above
875 const bool typesFromCache = getTypesFromCache();
876 Q_ASSERT(typesFromCache);
877 return typesFromCache;
878 }
879
880 m_seenImports.insert(importId, QString());
881 return false;
882}
883
884QQmlJSScope::Ptr QQmlJSImporter::localFile2ScopeTree(const QString &filePath)
885{
886 const auto seen = m_importedFiles.find(filePath);
887 if (seen != m_importedFiles.end())
888 return *seen;
889
890 return *m_importedFiles.insert(filePath, {
892 QSharedPointer<QDeferredFactory<QQmlJSScope>>(
893 new QDeferredFactory<QQmlJSScope>(this, filePath))
894 });
895}
896
898{
899 return localFile2ScopeTree(file);
900}
901
903 const QString &directory, const QString &prefix)
904{
905 const AvailableTypes builtins = builtinImportHelper();
906 QQmlJSImporter::AvailableTypes types(
908 ImportedTypes::INTERNAL, {}, builtins.cppNames.arrayType()));
909 importHelper(directory, &types, prefix, QTypeRevision(), false, true);
910 return types.qmlNames;
911}
912
914{
915 m_importPaths = importPaths;
916
917 // We have to get rid off all cache elements directly referencing modules, since changing
918 // importPaths might change which module is found first
919 m_seenImports.clear();
920 m_cachedImportTypes.clear();
921 // Luckily this doesn't apply to m_seenQmldirFiles
922}
923
925{
926 m_seenImports.clear();
927 m_cachedImportTypes.clear();
928 m_seenQmldirFiles.clear();
929 m_importedFiles.clear();
930}
931
933{
934 return m_builtins->cppNames.type(u"GlobalObject"_s).scope;
935}
936
937void QQmlJSImporter::setQualifiedNamesOn(const Import &import)
938{
939 for (auto &object : import.objects) {
940 if (object.exports.isEmpty())
941 continue;
942 if (auto *factory = object.scope.factory()) {
943 factory->setModuleName(import.name);
944 } else {
945 object.scope->setOwnModuleName(import.name);
946 }
947 }
948}
949
952{
953 m_importVisitor(rootNode, this, p);
954}
955
Definition lalr.h:136
The QDirIterator class provides an iterator for directory entrylists.
\inmodule QtCore
Definition qdir.h:20
@ NoFilter
Definition qdir.h:46
QString baseName() const
Returns the base name of the file without the path.
bool isRelative() const
Returns true if the file system entry's path is relative, otherwise returns false (that is,...
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:906
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1300
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:1220
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1008
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:952
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1304
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
void append(parameter_type t)
Definition qlist.h:459
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.
void runImportVisitor(QQmlJS::AST::Node *rootNode, const ImportVisitorPrerequisites &prerequisites)
QQmlJS::ContextualTypes ImportedTypes
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
bool isComposite() const
QQmlJS::Export Export
static QQmlJSScope::Ptr create()
static void resolveNonEnumTypes(const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes, QSet< QString > *usedTypes=nullptr)
QString internalName() const
static void resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType)
static void resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes, QSet< QString > *usedTypes=nullptr)
QStringList aliases() const
void accept(BaseVisitor *visitor)
bool isValid() const
Import()=default
iterator begin()
Definition qset.h:137
iterator end()
Definition qset.h:141
bool isEmpty() const
Definition qset.h:53
iterator find(const T &value)
Definition qset.h:160
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView chopped(qsizetype n) const noexcept
Returns the substring of length size() - length starting at the beginning of this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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:5465
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3829
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5307
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6028
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:5516
QString & append(QChar c)
Definition qstring.cpp:3254
QString trimmed() const &
Definition qstring.h:447
\inmodule QtCore
static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
Produces a QTypeRevision from the given majorVersion with an invalid minor version.
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 isValid() const
Returns true if the major version or the minor version is known, otherwise false.
QSet< QString >::iterator it
auto mo
[7]
@ Auto
Definition qprint_p.h:86
Combined button and popup list for selecting options.
QImageReader reader("image.png")
[1]
QList< QString > QStringList
Constructs a string list that contains the given string, str.
static QByteArray cacheKey(Args &&...args)
@ QtCriticalMsg
Definition qlogging.h:33
@ QtWarningMsg
Definition qlogging.h:32
#define qWarning
Definition qlogging.h:167
#define qFatal
Definition qlogging.h:169
GLint GLenum GLint components
GLuint GLuint end
GLsizei GLenum GLenum * types
GLfloat GLfloat f
GLint left
GLbitfield flags
GLuint name
GLuint GLfloat * val
GLuint entry
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
static qreal dot(const QPointF &a, const QPointF &b)
QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths, QTypeRevision version)
static QStringList aliases(const QQmlJSScope::ConstPtr &scope)
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 internalName(const QQmlJSScope::ConstPtr &scope)
static const QLatin1String PluginsDotQmltypes
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
proxy setType(QNetworkProxy::Socks5Proxy)
\inmodule QtCore \reentrant
Definition qchar.h:18
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