1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QQMLDOMEXTERNALITEMS_P_H |
5 | #define QQMLDOMEXTERNALITEMS_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include "qqmldomitem_p.h" |
19 | #include "qqmldomelements_p.h" |
20 | #include "qqmldommoduleindex_p.h" |
21 | #include "qqmldomcomments_p.h" |
22 | |
23 | #include <QtQml/private/qqmljsast_p.h> |
24 | #include <QtQml/private/qqmljsengine_p.h> |
25 | #include <QtQml/private/qqmldirparser_p.h> |
26 | #include <QtQmlCompiler/private/qqmljstyperesolver_p.h> |
27 | #include <QtCore/QMetaType> |
28 | |
29 | #include <limits> |
30 | #include <memory> |
31 | |
32 | Q_DECLARE_METATYPE(QQmlDirParser::Plugin) |
33 | |
34 | QT_BEGIN_NAMESPACE |
35 | |
36 | namespace QQmlJS { |
37 | namespace Dom { |
38 | |
39 | /*! |
40 | \internal |
41 | \class QQmlJS::Dom::ExternalOwningItem |
42 | |
43 | \brief A OwningItem that refers to an external resource (file,...) |
44 | |
45 | Every owning item has a file or directory it refers to. |
46 | |
47 | |
48 | */ |
49 | class QMLDOM_EXPORT ExternalOwningItem: public OwningItem { |
50 | public: |
51 | ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path pathFromTop, |
52 | int derivedFrom = 0, QString code = QString()); |
53 | ExternalOwningItem(const ExternalOwningItem &o) = default; |
54 | QString canonicalFilePath(DomItem &) const override; |
55 | QString canonicalFilePath() const; |
56 | Path canonicalPath(DomItem &) const override; |
57 | Path canonicalPath() const; |
58 | bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override |
59 | { |
60 | bool cont = OwningItem::iterateDirectSubpaths(self, visitor); |
61 | cont = cont && self.dvValueLazyField(visitor, f: Fields::canonicalFilePath, valueF: [this]() { |
62 | return canonicalFilePath(); |
63 | }); |
64 | cont = cont |
65 | && self.dvValueLazyField(visitor, f: Fields::isValid, valueF: [this]() { return isValid(); }); |
66 | if (!code().isNull()) |
67 | cont = cont |
68 | && self.dvValueLazyField(visitor, f: Fields::code, valueF: [this]() { return code(); }); |
69 | return cont; |
70 | } |
71 | |
72 | bool iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor) override |
73 | { |
74 | bool cont = OwningItem::iterateSubOwners(self, visitor); |
75 | cont = cont && self.field(name: Fields::components).visitKeys(visitor: [visitor](QString, DomItem &comps) { |
76 | return comps.visitIndexes(visitor: [visitor](DomItem &comp) { |
77 | return comp.field(name: Fields::objects).visitIndexes(visitor: [visitor](DomItem &qmlObj) { |
78 | if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>()) |
79 | return qmlObjPtr->iterateSubOwners(self&: qmlObj, visitor); |
80 | Q_ASSERT(false); |
81 | return true; |
82 | }); |
83 | }); |
84 | }); |
85 | return cont; |
86 | } |
87 | |
88 | bool isValid() const { |
89 | QMutexLocker l(mutex()); |
90 | return m_isValid; |
91 | } |
92 | void setIsValid(bool val) { |
93 | QMutexLocker l(mutex()); |
94 | m_isValid = val; |
95 | } |
96 | // null code means invalid |
97 | const QString &code() const { return m_code; } |
98 | |
99 | protected: |
100 | QString m_canonicalFilePath; |
101 | QString m_code; |
102 | Path m_path; |
103 | bool m_isValid = false; |
104 | }; |
105 | |
106 | class QMLDOM_EXPORT QmlDirectory final : public ExternalOwningItem |
107 | { |
108 | protected: |
109 | std::shared_ptr<OwningItem> doCopy(DomItem &) const override |
110 | { |
111 | return std::make_shared<QmlDirectory>(args: *this); |
112 | } |
113 | |
114 | public: |
115 | constexpr static DomType kindValue = DomType::QmlDirectory; |
116 | DomType kind() const override { return kindValue; } |
117 | QmlDirectory(QString filePath = QString(), QStringList dirList = QStringList(), |
118 | QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC), |
119 | int derivedFrom = 0); |
120 | QmlDirectory(const QmlDirectory &o) = default; |
121 | |
122 | std::shared_ptr<QmlDirectory> makeCopy(DomItem &self) const |
123 | { |
124 | return std::static_pointer_cast<QmlDirectory>(r: doCopy(self)); |
125 | } |
126 | |
127 | bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override; |
128 | |
129 | const QMultiMap<QString, Export> &exports() const & { return m_exports; } |
130 | |
131 | const QMultiMap<QString, QString> &qmlFiles() const & { return m_qmlFiles; } |
132 | |
133 | bool addQmlFilePath(QString relativePath); |
134 | |
135 | private: |
136 | QMultiMap<QString, Export> m_exports; |
137 | QMultiMap<QString, QString> m_qmlFiles; |
138 | }; |
139 | |
140 | class QMLDOM_EXPORT QmldirFile final : public ExternalOwningItem |
141 | { |
142 | Q_DECLARE_TR_FUNCTIONS(QmldirFile) |
143 | protected: |
144 | std::shared_ptr<OwningItem> doCopy(DomItem &) const override |
145 | { |
146 | auto copy = std::make_shared<QmldirFile>(args: *this); |
147 | return copy; |
148 | } |
149 | |
150 | public: |
151 | constexpr static DomType kindValue = DomType::QmldirFile; |
152 | DomType kind() const override { return kindValue; } |
153 | |
154 | static ErrorGroups myParsingErrors(); |
155 | |
156 | QmldirFile(QString filePath = QString(), QString code = QString(), |
157 | QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC), |
158 | int derivedFrom = 0) |
159 | : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmldirFilePath(path: filePath), |
160 | derivedFrom, code) |
161 | { |
162 | } |
163 | QmldirFile(const QmldirFile &o) = default; |
164 | |
165 | static std::shared_ptr<QmldirFile> fromPathAndCode(QString path, QString code); |
166 | |
167 | std::shared_ptr<QmldirFile> makeCopy(DomItem &self) const |
168 | { |
169 | return std::static_pointer_cast<QmldirFile>(r: doCopy(self)); |
170 | } |
171 | |
172 | bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override; |
173 | |
174 | QmlUri uri() const { return m_uri; } |
175 | |
176 | const QSet<int> &majorVersions() const & { return m_majorVersions; } |
177 | |
178 | const QMultiMap<QString, Export> &exports() const & { return m_exports; } |
179 | |
180 | const QList<Import> &imports() const & { return m_imports; } |
181 | |
182 | const QList<Path> &qmltypesFilePaths() const & { return m_qmltypesFilePaths; } |
183 | |
184 | QMap<QString, QString> qmlFiles() const; |
185 | |
186 | bool designerSupported() const { return m_qmldir.designerSupported(); } |
187 | |
188 | QStringList classNames() const { return m_qmldir.classNames(); } |
189 | |
190 | QList<ModuleAutoExport> autoExports() const; |
191 | void setAutoExports(const QList<ModuleAutoExport> &autoExport); |
192 | |
193 | void ensureInModuleIndex(DomItem &self, QString uri); |
194 | |
195 | private: |
196 | void parse(); |
197 | void setFromQmldir(); |
198 | |
199 | QmlUri m_uri; |
200 | QSet<int> m_majorVersions; |
201 | QQmlDirParser m_qmldir; |
202 | QList<QQmlDirParser::Plugin> m_plugins; |
203 | QList<Import> m_imports; |
204 | QList<ModuleAutoExport> m_autoExports; |
205 | QMultiMap<QString, Export> m_exports; |
206 | QList<Path> m_qmltypesFilePaths; |
207 | }; |
208 | |
209 | class QMLDOM_EXPORT JsFile final : public ExternalOwningItem |
210 | { |
211 | protected: |
212 | std::shared_ptr<OwningItem> doCopy(DomItem &) const override |
213 | { |
214 | auto copy = std::make_shared<JsFile>(args: *this); |
215 | return copy; |
216 | } |
217 | |
218 | public: |
219 | constexpr static DomType kindValue = DomType::JsFile; |
220 | DomType kind() const override { return kindValue; } |
221 | JsFile(QString filePath = QString(), |
222 | QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC), |
223 | Path pathFromTop = Path(), int derivedFrom = 0) |
224 | : ExternalOwningItem(filePath, lastDataUpdateAt, pathFromTop, derivedFrom) |
225 | { |
226 | } |
227 | JsFile(const JsFile &o) = default; |
228 | |
229 | std::shared_ptr<JsFile> makeCopy(DomItem &self) const |
230 | { |
231 | return std::static_pointer_cast<JsFile>(r: doCopy(self)); |
232 | } |
233 | |
234 | std::shared_ptr<QQmlJS::Engine> engine() const { return m_engine; } |
235 | JsResource rootComponent() const { return m_rootComponent; } |
236 | |
237 | private: |
238 | std::shared_ptr<QQmlJS::Engine> m_engine; |
239 | JsResource m_rootComponent; |
240 | }; |
241 | |
242 | class QMLDOM_EXPORT QmlFile final : public ExternalOwningItem |
243 | { |
244 | protected: |
245 | std::shared_ptr<OwningItem> doCopy(DomItem &self) const override; |
246 | |
247 | public: |
248 | constexpr static DomType kindValue = DomType::QmlFile; |
249 | DomType kind() const override { return kindValue; } |
250 | |
251 | QmlFile(const QmlFile &o); |
252 | QmlFile(QString filePath = QString(), QString code = QString(), |
253 | QDateTime lastDataUpdate = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC), |
254 | int derivedFrom = 0); |
255 | static ErrorGroups myParsingErrors(); |
256 | bool iterateDirectSubpaths(DomItem &self, DirectVisitor) |
257 | override; // iterates the *direct* subpaths, returns false if a quick end was requested |
258 | DomItem field(DomItem &self, QStringView name) const override |
259 | { |
260 | return const_cast<QmlFile *>(this)->field(self, name); |
261 | } |
262 | DomItem field(DomItem &self, QStringView name); |
263 | std::shared_ptr<QmlFile> makeCopy(DomItem &self) const |
264 | { |
265 | return std::static_pointer_cast<QmlFile>(r: doCopy(self)); |
266 | } |
267 | void addError(DomItem &self, ErrorMessage msg) override; |
268 | |
269 | const QMultiMap<QString, QmlComponent> &components() const & { return m_components; } |
270 | void setComponents(const QMultiMap<QString, QmlComponent> &components) |
271 | { |
272 | m_components = components; |
273 | } |
274 | Path addComponent(const QmlComponent &component, AddOption option = AddOption::Overwrite, |
275 | QmlComponent **cPtr = nullptr) |
276 | { |
277 | QStringList nameEls = component.name().split(sep: QChar::fromLatin1(c: '.')); |
278 | QString key = nameEls.mid(pos: 1).join(sep: QChar::fromLatin1(c: '.')); |
279 | return insertUpdatableElementInMultiMap(mapPathFromOwner: Path::Field(s: Fields::components), mmap&: m_components, key, |
280 | value: component, option, valuePtr: cPtr); |
281 | } |
282 | |
283 | void writeOut(DomItem &self, OutWriter &lw) const override; |
284 | |
285 | AST::UiProgram *ast() const |
286 | { |
287 | return m_ast; // avoid making it public? would make moving away from it easier |
288 | } |
289 | const QList<Import> &imports() const & { return m_imports; } |
290 | void setImports(const QList<Import> &imports) { m_imports = imports; } |
291 | Path addImport(const Import &i) |
292 | { |
293 | index_type idx = index_type(m_imports.size()); |
294 | m_imports.append(t: i); |
295 | if (i.uri.isModule()) { |
296 | m_importScope.addImport(p: (i.importId.isEmpty() |
297 | ? QStringList() |
298 | : i.importId.split(sep: QChar::fromLatin1(c: '.'))), |
299 | targetExports: i.importedPath()); |
300 | } else { |
301 | QString path = i.uri.absoluteLocalPath(basePath: canonicalFilePath()); |
302 | if (!path.isEmpty()) |
303 | m_importScope.addImport(p: (i.importId.isEmpty() |
304 | ? QStringList() |
305 | : i.importId.split(sep: QChar::fromLatin1(c: '.'))), |
306 | targetExports: Paths::qmlDirPath(path)); |
307 | } |
308 | return Path::Field(s: Fields::imports).index(i: idx); |
309 | } |
310 | std::shared_ptr<QQmlJS::Engine> engine() const { return m_engine; } |
311 | RegionComments &() { return m_comments; } |
312 | std::shared_ptr<AstComments> () const { return m_astComments; } |
313 | void (std::shared_ptr<AstComments> comm) { m_astComments = comm; } |
314 | FileLocations::Tree fileLocationsTree() const { return m_fileLocationsTree; } |
315 | void setFileLocationsTree(FileLocations::Tree v) { m_fileLocationsTree = v; } |
316 | const QList<Pragma> &pragmas() const & { return m_pragmas; } |
317 | void setPragmas(QList<Pragma> pragmas) { m_pragmas = pragmas; } |
318 | Path addPragma(const Pragma &pragma) |
319 | { |
320 | int idx = m_pragmas.size(); |
321 | m_pragmas.append(t: pragma); |
322 | return Path::Field(s: Fields::pragmas).index(i: idx); |
323 | } |
324 | ImportScope &importScope() { return m_importScope; } |
325 | const ImportScope &importScope() const { return m_importScope; } |
326 | |
327 | std::optional<std::shared_ptr<QQmlJSTypeResolver>> typeResolver() const |
328 | { |
329 | return m_typeResolver; |
330 | } |
331 | void setTypeResolver(const std::shared_ptr<QQmlJSTypeResolver> &typeResolver) |
332 | { |
333 | m_typeResolver = typeResolver; |
334 | } |
335 | |
336 | private: |
337 | friend class QQmlDomAstCreator; |
338 | std::shared_ptr<Engine> m_engine; |
339 | AST::UiProgram *m_ast; // avoid? would make moving away from it easier |
340 | std::shared_ptr<AstComments> ; |
341 | RegionComments ; |
342 | FileLocations::Tree m_fileLocationsTree; |
343 | QMultiMap<QString, QmlComponent> m_components; |
344 | QList<Pragma> m_pragmas; |
345 | QList<Import> m_imports; |
346 | ImportScope m_importScope; |
347 | std::optional<std::shared_ptr<QQmlJSTypeResolver>> m_typeResolver; |
348 | }; |
349 | |
350 | class QMLDOM_EXPORT QmltypesFile final : public ExternalOwningItem |
351 | { |
352 | protected: |
353 | std::shared_ptr<OwningItem> doCopy(DomItem &) const override |
354 | { |
355 | auto res = std::make_shared<QmltypesFile>(args: *this); |
356 | return res; |
357 | } |
358 | |
359 | public: |
360 | constexpr static DomType kindValue = DomType::QmltypesFile; |
361 | DomType kind() const override { return kindValue; } |
362 | |
363 | QmltypesFile(QString filePath = QString(), QString code = QString(), |
364 | QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC), |
365 | int derivedFrom = 0) |
366 | : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmltypesFilePath(path: filePath), |
367 | derivedFrom, code) |
368 | { |
369 | } |
370 | |
371 | QmltypesFile(const QmltypesFile &o) = default; |
372 | |
373 | void ensureInModuleIndex(DomItem &self); |
374 | |
375 | bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; |
376 | std::shared_ptr<QmltypesFile> makeCopy(DomItem &self) const |
377 | { |
378 | return std::static_pointer_cast<QmltypesFile>(r: doCopy(self)); |
379 | } |
380 | |
381 | void addImport(const Import i) |
382 | { // builder only: not threadsafe... |
383 | m_imports.append(t: i); |
384 | } |
385 | const QList<Import> &imports() const & { return m_imports; } |
386 | const QMultiMap<QString, QmltypesComponent> &components() const & { return m_components; } |
387 | void setComponents(QMultiMap<QString, QmltypesComponent> c) { m_components = std::move(c); } |
388 | Path addComponent(const QmltypesComponent &comp, AddOption option = AddOption::Overwrite, |
389 | QmltypesComponent **cPtr = nullptr) |
390 | { |
391 | for (const Export &e : comp.exports()) |
392 | addExport(e); |
393 | return insertUpdatableElementInMultiMap(mapPathFromOwner: Path::Field(s: u"components" ), mmap&: m_components, |
394 | key: comp.name(), value: comp, option, valuePtr: cPtr); |
395 | } |
396 | const QMultiMap<QString, Export> &exports() const & { return m_exports; } |
397 | void setExports(QMultiMap<QString, Export> e) { m_exports = e; } |
398 | Path addExport(const Export &e) |
399 | { |
400 | index_type i = m_exports.values(key: e.typeName).size(); |
401 | m_exports.insert(key: e.typeName, value: e); |
402 | addUri(uri: e.uri, majorVersion: e.version.majorVersion); |
403 | return canonicalPath().field(name: Fields::exports).index(i); |
404 | } |
405 | |
406 | const QMap<QString, QSet<int>> &uris() const & { return m_uris; } |
407 | void addUri(QString uri, int majorVersion) |
408 | { |
409 | QSet<int> &v = m_uris[uri]; |
410 | if (!v.contains(value: majorVersion)) { |
411 | v.insert(value: majorVersion); |
412 | } |
413 | } |
414 | |
415 | private: |
416 | QList<Import> m_imports; |
417 | QMultiMap<QString, QmltypesComponent> m_components; |
418 | QMultiMap<QString, Export> m_exports; |
419 | QMap<QString, QSet<int>> m_uris; |
420 | }; |
421 | |
422 | class QMLDOM_EXPORT GlobalScope final : public ExternalOwningItem |
423 | { |
424 | protected: |
425 | std::shared_ptr<OwningItem> doCopy(DomItem &) const override; |
426 | |
427 | public: |
428 | constexpr static DomType kindValue = DomType::GlobalScope; |
429 | DomType kind() const override { return kindValue; } |
430 | |
431 | GlobalScope(QString filePath = QString(), |
432 | QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC), |
433 | int derivedFrom = 0) |
434 | : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::globalScopePath(name: filePath), |
435 | derivedFrom) |
436 | { |
437 | setIsValid(true); |
438 | } |
439 | |
440 | bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override; |
441 | std::shared_ptr<GlobalScope> makeCopy(DomItem &self) const |
442 | { |
443 | return std::static_pointer_cast<GlobalScope>(r: doCopy(self)); |
444 | } |
445 | QString name() const { return m_name; } |
446 | Language language() const { return m_language; } |
447 | GlobalComponent rootComponent() const { return m_rootComponent; } |
448 | void setName(QString name) { m_name = name; } |
449 | void setLanguage(Language language) { m_language = language; } |
450 | void setRootComponent(const GlobalComponent &ob) |
451 | { |
452 | m_rootComponent = ob; |
453 | m_rootComponent.updatePathFromOwner(newPath: Path::Field(s: Fields::rootComponent)); |
454 | } |
455 | |
456 | private: |
457 | QString m_name; |
458 | Language m_language; |
459 | GlobalComponent m_rootComponent; |
460 | }; |
461 | |
462 | } // end namespace Dom |
463 | } // end namespace QQmlJS |
464 | QT_END_NAMESPACE |
465 | #endif // QQMLDOMEXTERNALITEMS_P_H |
466 | |