Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmljsimporter_p.h
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// Qt-Security score:significant
4
5#ifndef QQMLJSIMPORTER_P_H
6#define QQMLJSIMPORTER_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17
18#include <qtqmlcompilerexports.h>
19
21#include "qqmljsscope_p.h"
23#include <QtQml/private/qqmldirparser_p.h>
24#include <QtQml/private/qqmljsast_p.h>
25
26#include <QtCore/qtyperevision.h>
27
29
30namespace QQmlJS {
31class Import
32{
33public:
34 Import() = default;
35 Import(QString prefix, QString name, QTypeRevision version, bool isFile, bool isDependency);
36
37 bool isValid() const;
38
39 QString prefix() const { return m_prefix; }
40 QString name() const { return m_name; }
41 QTypeRevision version() const { return m_version; }
42 bool isFile() const { return m_isFile; }
43 bool isDependency() const { return m_isDependency; }
44
45private:
46 QString m_prefix;
47 QString m_name;
48 QTypeRevision m_version;
49 bool m_isFile = false;
50 bool m_isDependency = false;
51
52 friend inline size_t qHash(const Import &key, size_t seed = 0) noexcept
53 {
54 return qHashMulti(seed, key.m_prefix, key.m_name, key.m_version,
55 key.m_isFile, key.m_isDependency);
56 }
57
58 friend inline bool operator==(const Import &a, const Import &b)
59 {
60 return a.m_prefix == b.m_prefix && a.m_name == b.m_name && a.m_version == b.m_version
61 && a.m_isFile == b.m_isFile && a.m_isDependency == b.m_isDependency;
62 }
63};
64}
65
69 TolerateFileSelectors = 0x4, // if we find a type "twice", check if one looks like it's from a file selector and use the other
70};
71Q_DECLARE_FLAGS(QQmlJSImporterFlags, QQmlJSImporterFlag)
72
73class QQmlJSImportVisitor;
74class QQmlJSLogger;
75class Q_QMLCOMPILER_EXPORT QQmlJSImporter
76{
77 // In the list of QML types we prefix unresolvable QML names with $anonymous$, and C++
78 // names with $internal$. This is to avoid clashes between them.
79 // In the list of C++ types we insert types that don't have a C++ name as their
80 // QML name prefixed with $anonymous$.
81 static inline constexpr QLatin1String anonPrefix = QLatin1String("$anonymous$");
82 static inline constexpr QLatin1String internalPrefix = QLatin1String("$internal$");
83 static inline constexpr QLatin1String modulePrefix = QLatin1String("$module$");
84
85public:
86 static inline constexpr QLatin1String s_inProcessMarker = QLatin1String("$InProcess$");
87
88 struct ImportedTypes {
89 ImportedTypes(QQmlJS::ContextualTypes &&types, QList<QQmlJS::DiagnosticMessage> &&warnings,
90 QList<QQmlJS::Import> &&moduleImports = {})
91 : m_types(std::move(types)),
92 m_warnings(std::move(warnings)),
93 m_moduleImports(std::move(moduleImports))
94 {}
95
96 ImportedTypes(const ImportedTypes &) = default;
97 ImportedTypes(ImportedTypes &&) = default;
98 ImportedTypes &operator=(const ImportedTypes &) = default;
99 ImportedTypes &operator=(ImportedTypes &&) = default;
100 ~ImportedTypes() = default;
101
102 void clear()
103 {
104 m_types.clearTypes();
105 m_warnings.clear();
106 }
107
108 const QQmlJS::ContextualTypes &contextualTypes() const { return m_types; }
109 const QList<QQmlJS::DiagnosticMessage> &warnings() const { return m_warnings; };
110 const QList<QQmlJS::Import> &moduleImports() const { return m_moduleImports; }
111
112 void setCurrentFileSelector(const QString &selector)
113 {
114 m_types.setCurrentFileSelector(selector);
115 }
116
117 bool isEmpty() const { return m_types.types().isEmpty(); }
118
119 bool hasType(const QString &name) const { return m_types.hasType(name); }
120 QQmlJS::ImportedScope<QQmlJSScope::ConstPtr> type(const QString &name) const
121 {
122 return m_types.type(name);
123 }
124 QString name(const QQmlJSScope::ConstPtr &type) const { return m_types.name(type); }
125 void setType(const QString &name, const QQmlJS::ContextualType &type)
126 {
127 m_types.setType(name, type);
128 }
129 bool isNullType(const QString &name) const { return m_types.isNullType(name); }
130 const QHash<QString, QQmlJS::ContextualType> &types() const { return m_types.types(); }
131 const auto &names() const { return m_types.names(); }
132
133 void add(ImportedTypes &&other)
134 {
135 m_types.addTypes(std::move(other.m_types));
136 m_warnings.append(std::move(other.m_warnings));
137 }
138
139 void addWarnings(QList<QQmlJS::DiagnosticMessage> &&warnings)
140 {
141 m_warnings.append(std::move(warnings));
142 }
143
144 private:
145 QQmlJS::ContextualTypes m_types;
146 QList<QQmlJS::DiagnosticMessage> m_warnings;
147
148 // The imports of modules, including the ones forwarded via qmldir "import" lines,
149 // but not the dependencies.
150 QList<QQmlJS::Import> m_moduleImports;
151 };
152
153 struct VersionedImport
154 {
155 QString module;
156 QString name;
157 QTypeRevision version;
158
159 friend bool operator==(const VersionedImport &a, const VersionedImport &b)
160 {
161 return a.module == b.module && a.name == b.name && a.version == b.version;
162 }
163
164 friend bool operator!=(const VersionedImport &a, const VersionedImport &b)
165 {
166 return !(a == b);
167 }
168 };
169
170 void registerVersionedImports(const ImportedTypes &imported);
171 QList<VersionedImport> versionedImports(const QString &filePath) const
172 {
173 return m_versionedImports.value(filePath);
174 }
175
176 QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
177 QQmlJSImporterFlags flags = QQmlJSImporterFlags{});
178
179 const QQmlJSResourceFileMapper *resourceFileMapper() const { return m_mapper; }
180 void setResourceFileMapper(const QQmlJSResourceFileMapper *mapper) { m_mapper = mapper; }
181
182 QQmlJSResourceFileMapper *metaDataMapper() const { return m_metaDataMapper; }
183 void setMetaDataMapper(QQmlJSResourceFileMapper *mapper) { m_metaDataMapper = mapper; }
184
185 ImportedTypes importHardCodedBuiltins();
186 QList<QQmlJS::DiagnosticMessage> importQmldirs(const QStringList &qmltypesFiles);
187
188 QQmlJSScope::Ptr importFile(const QString &file);
189 ImportedTypes importDirectory(const QString &directory, quint8 precedence,
190 const QString &prefix = QString());
191
192 // ### qmltc needs this. once re-written, we no longer need to expose this
193 QHash<QString, QQmlJSScope::Ptr> importedFiles() const { return m_importedFiles; }
194
195 ImportedTypes importModule(const QString &module, quint8 precedence,
196 const QString &prefix = QString(),
197 QTypeRevision version = QTypeRevision(),
198 QStringList *staticModuleList = nullptr);
199
200 ImportedTypes builtinInternalNames();
201
202 QStringList importPaths() const { return m_importPaths; }
203 void setImportPaths(const QStringList &importPaths);
204
205 void clearCache();
206
207 QQmlJSScope::ConstPtr jsGlobalObject();
208 QString pathOfModule(const QString &moduleName, QTypeRevision revision) const
209 {
210 const auto it = m_seenImports.constFind({ moduleName, revision });
211 return it != m_seenImports.constEnd() ? *it : QString();
212 }
213
214 struct ImportVisitorPrerequisites
215 {
216 ImportVisitorPrerequisites(QQmlJSScope::Ptr target, QQmlJSLogger *logger,
217 const QString &implicitImportDirectory = {},
218 const QStringList &qmldirFiles = {})
219 : m_target(target),
220 m_logger(logger),
221 m_implicitImportDirectory(implicitImportDirectory),
222 m_qmldirFiles(qmldirFiles)
223 {
224 Q_ASSERT(target && logger);
225 }
226
227 QQmlJSScope::Ptr m_target;
228 QQmlJSLogger *m_logger;
229 QString m_implicitImportDirectory;
230 QStringList m_qmldirFiles;
231 };
232 void runImportVisitor(QQmlJS::AST::Node *rootNode,
233 const ImportVisitorPrerequisites &prerequisites);
234
235 /*!
236 \internal
237 When a qml file gets lazily loaded, it will be lexed and parsed and finally be constructed
238 via an ImportVisitor. By default, this is done via the QQmlJSImportVisitor, but can also be done
239 via other import visitors like QmltcVisitor, which is used by qmltc to compile a QML file, or
240 QQmlDomAstCreatorWithQQmlJSScope, which is used to construct the Dom of lazily loaded QML files.
241 */
242 using ImportVisitor = std::function<void(QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
243 const ImportVisitorPrerequisites &prerequisites)>;
244
245 void setImportVisitor(ImportVisitor visitor) { m_importVisitor = visitor; }
246 void setFlags(const QQmlJSImporterFlags &flags) { m_flags = flags; }
247 QQmlJSImporterFlags flags() const { return m_flags; }
248
249private:
250 struct AvailableTypes
251 {
252 AvailableTypes(QQmlJS::ContextualTypes builtins)
253 : cppNames(std::move(builtins))
254 , qmlNames(QQmlJS::ContextualTypes::QML, {}, {}, cppNames.arrayType())
255 {
256 }
257
258 // C++ names used in qmltypes files for non-composite types
259 QQmlJS::ContextualTypes cppNames;
260
261 // Names the importing component sees, including any prefixes
262 QQmlJS::ContextualTypes qmlNames;
263
264 // Static modules included here
265 QStringList staticModules;
266
267 // Warnings produced when importing
268 QList<QQmlJS::DiagnosticMessage> warnings;
269
270 // The imports of modules, including the ones forwarded via qmldir "import" lines,
271 // but not the dependencies.
272 QList<QQmlJS::Import> moduleImports;
273
274 // Whether a system module has been imported
275 bool hasSystemModule = false;
276 // Whether QQmlJSImporter::importHelper() returned false on the first time it loaded the
277 // QML Module, so that further calls to importHelper() have the same return value.
278 bool wasFound = true;
279 };
280
281 struct Import {
282 enum Type : bool { Directory, Qmldir };
283
284 QString name;
285 bool isStaticModule = false;
286 bool isSystemModule = false;
287 Type type = Directory;
288
289 QList<QQmlJSExportedScope> objects;
290 QHash<QString, QQmlJSExportedScope> scripts;
291 QList<QQmlDirParser::Import> imports;
292 QList<QQmlDirParser::Import> dependencies;
293 QList<QQmlDirParser::Export> exports;
294
295 // Warnings produced when importing
296 QList<QQmlJS::DiagnosticMessage> warnings;
297 };
298
299 struct CachedImportKey
300 {
301 QQmlJS::Import import;
302
303 // The version-decorated directories searched can be less specific than the import version.
304 QTypeRevision searchVersion;
305
306 private:
307 friend size_t qHash(const CachedImportKey &key, size_t seed = 0) noexcept
308 {
309 return qHashMulti(seed, key.import, key.searchVersion);
310 }
311
312 friend bool operator==(const CachedImportKey &a, const CachedImportKey &b)
313 {
314 return a.import == b.import && a.searchVersion == b.searchVersion;
315 }
316
317 friend bool operator!=(const CachedImportKey &a, const CachedImportKey &b)
318 {
319 return !(a == b);
320 }
321 };
322
323 AvailableTypes builtinImportHelper();
324 bool getTypesFromCache(AvailableTypes *types, const CachedImportKey &cacheKey,
325 bool isDependency);
326 bool importHelper(const QString &module, AvailableTypes *types, quint8 precedence,
327 const QString &prefix, QTypeRevision version, QTypeRevision searchVersion,
328 bool isDependency = false, bool isFile = false);
329 void processImport(const QQmlJS::Import &importDescription, const Import &import,
330 quint8 precedence, AvailableTypes *types);
331 static void insertAliases(const QQmlJS::ContextualType &type,
332 QQmlJSImporter::AvailableTypes *types);
333 void insertExport(const QQmlJS::ContextualType &type, const QQmlJS::Export &valExport,
334 const QString &qmlName,
335 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
336 QQmlJSImporter::AvailableTypes *types) const;
337 QQmlJSScope::Export
338 resolveConflictingExports(const QQmlJS::Import &importDescription,
339 const QQmlJSExportedScope &val, quint8 precedence,
340 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
341 QQmlJSImporter::AvailableTypes *types);
342 enum SeenVersion { LowerVersion, SameVersion, HigherVersion };
343 void insertExportWithConflictingVersion(const QQmlJSExportedScope &val, quint8 precedence,
344 const QString &qmlName,
345 const QQmlJSScope::Export &valExport,
346 const QQmlJSScope::ConstPtr &scope,
347 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
348 QQmlJSImporter::AvailableTypes *types,
349 SeenVersion seenVersion) const;
350 SeenVersion computeSeenVersion(const QQmlJS::Import &importDescription,
351 const QList<QQmlJS::Export> &existingExports,
352 QTypeRevision valExportVersion) const;
353 void insertExports(const QQmlJS::Import &importDescription, const QQmlJSExportedScope &val,
354 const QString &cppName, quint8 precedence,
355 QHash<QString, QList<QQmlJSScope::Export>> *seenExports,
356 QQmlJSImporter::AvailableTypes *types);
357 static QTypeRevision resolveImportVersion(const Import &import, QTypeRevision version);
358 void importDependencies(const Import &import, quint8 precedence, AvailableTypes *types,
359 const QString &prefix, QTypeRevision resolvedVersion,
360 QTypeRevision searchVersion, bool isDependency);
361 QQmlDirParser createQmldirParserForFile(const QString &filename, Import *import);
362 void readQmltypes(const QString &filename, Import *result);
363 Import readQmldir(const QString &dirname);
364 Import readDirectory(const QString &directory);
365
366 QQmlJSScope::Ptr localFile2QQmlJSScope(const QString &filePath);
367 static void setQualifiedNamesOn(const Import &import);
368
369 QStringList m_importPaths;
370
371 QHash<std::pair<QString, QTypeRevision>, QString> m_seenImports;
372
373 QHash<CachedImportKey, QSharedPointer<AvailableTypes>> m_cachedImportTypes;
374 QHash<QString, Import> m_seenQmldirFilesAndDirectories;
375
376 QHash<QString, QQmlJSScope::Ptr> m_importedFiles;
377
378 // The imports through which documents see a type, by file path of the type
379 QHash<QString, QList<VersionedImport>> m_versionedImports;
380 std::optional<AvailableTypes> m_builtins;
381
382 const QQmlJSResourceFileMapper *m_mapper = nullptr;
383 QQmlJSResourceFileMapper *m_metaDataMapper = nullptr;
384 QQmlJSImporterFlags m_flags;
385 bool useOptionalImports() const { return m_flags.testFlag(UseOptionalImports); };
386 bool preferQmlFilesFromSourceFolder() const
387 {
388 return m_flags.testFlag(PreferQmlFilesFromSourceFolder);
389 };
390
391 ImportVisitor m_importVisitor;
392};
393
394QT_END_NAMESPACE
395
396#endif // QQMLJSIMPORTER_P_H
friend bool operator==(const Import &a, const Import &b)
QString prefix() const
bool isValid() const
QTypeRevision version() const
QString name() const
bool isDependency() const
Import()=default
bool isFile() const
friend size_t qHash(const Import &key, size_t seed=0) noexcept
Import(QString prefix, QString name, QTypeRevision version, bool isFile, bool isDependency)
\inmodule QtCore
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
QQmlJSImporterFlag
@ UseOptionalImports
@ PreferQmlFilesFromSourceFolder
@ TolerateFileSelectors