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 DOMTOP_H
5#define DOMTOP_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 "qqmldomexternalitems_p.h"
21
22#include <QtCore/QQueue>
23#include <QtCore/QString>
24#include <QtCore/QDateTime>
25
26#include <QtCore/QCborValue>
27#include <QtCore/QCborMap>
28
29#include <memory>
30#include <optional>
31
32QT_BEGIN_NAMESPACE
33
34using namespace Qt::Literals::StringLiterals;
35
36namespace QQmlJS {
37namespace Dom {
38
39class QMLDOM_EXPORT ExternalItemPairBase: public OwningItem { // all access should have the lock of the DomUniverse containing this
40 Q_DECLARE_TR_FUNCTIONS(ExternalItemPairBase);
41public:
42 constexpr static DomType kindValue = DomType::ExternalItemPair;
43 DomType kind() const final override { return kindValue; }
44 ExternalItemPairBase(
45 const QDateTime &validExposedAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC),
46 const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC),
47 int derivedFrom = 0,
48 const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC))
49 : OwningItem(derivedFrom, lastDataUpdateAt),
50 validExposedAt(validExposedAt),
51 currentExposedAt(currentExposedAt)
52 {}
53 ExternalItemPairBase(const ExternalItemPairBase &o):
54 OwningItem(o), validExposedAt(o.validExposedAt), currentExposedAt(o.currentExposedAt)
55 {}
56 virtual std::shared_ptr<ExternalOwningItem> validItem() const = 0;
57 virtual DomItem validItem(const DomItem &self) const = 0;
58 virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
59 virtual DomItem currentItem(const DomItem &self) const = 0;
60
61 QString canonicalFilePath(const DomItem &) const final override;
62 Path canonicalPath(const DomItem &self) const final override;
63 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override;
64 DomItem field(const DomItem &self, QStringView name) const final override
65 {
66 return OwningItem::field(self, name);
67 }
68
69 bool currentIsValid() const;
70
71 std::shared_ptr<ExternalItemPairBase> makeCopy(const DomItem &self) const
72 {
73 return std::static_pointer_cast<ExternalItemPairBase>(r: doCopy(self));
74 }
75
76 QDateTime lastDataUpdateAt() const final override
77 {
78 if (currentItem())
79 return currentItem()->lastDataUpdateAt();
80 return ExternalItemPairBase::lastDataUpdateAt();
81 }
82
83 void refreshedDataAt(QDateTime tNew) final override
84 {
85 if (currentItem())
86 currentItem()->refreshedDataAt(tNew);
87 return OwningItem::refreshedDataAt(tNew);
88 }
89
90 friend class DomUniverse;
91
92 QDateTime validExposedAt;
93 QDateTime currentExposedAt;
94};
95
96template<class T>
97class QMLDOM_EXPORT ExternalItemPair final : public ExternalItemPairBase
98{ // all access should have the lock of the DomUniverse containing this
99protected:
100 std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
101 {
102 return std::make_shared<ExternalItemPair>(*this);
103 }
104
105public:
106 constexpr static DomType kindValue = DomType::ExternalItemPair;
107 friend class DomUniverse;
108 ExternalItemPair(
109 const std::shared_ptr<T> &valid = {}, const std::shared_ptr<T> &current = {},
110 const QDateTime &validExposedAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC),
111 const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC),
112 int derivedFrom = 0,
113 const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC))
114 : ExternalItemPairBase(validExposedAt, currentExposedAt, derivedFrom, lastDataUpdateAt),
115 valid(valid),
116 current(current)
117 {}
118 ExternalItemPair(const ExternalItemPair &o):
119 ExternalItemPairBase(o), valid(o.valid), current(o.current)
120 {
121 }
122 std::shared_ptr<ExternalOwningItem> validItem() const override { return valid; }
123 DomItem validItem(const DomItem &self) const override { return self.copy(valid); }
124 std::shared_ptr<ExternalOwningItem> currentItem() const override { return current; }
125 DomItem currentItem(const DomItem &self) const override { return self.copy(current); }
126 std::shared_ptr<ExternalItemPair> makeCopy(const DomItem &self) const
127 {
128 return std::static_pointer_cast<ExternalItemPair>(doCopy(self));
129 }
130
131 std::shared_ptr<T> valid;
132 std::shared_ptr<T> current;
133};
134
135class QMLDOM_EXPORT DomTop: public OwningItem {
136public:
137 DomTop(QMap<QString, OwnerT> extraOwningItems = {}, int derivedFrom = 0)
138 : OwningItem(derivedFrom), m_extraOwningItems(extraOwningItems)
139 {}
140 DomTop(const DomTop &o):
141 OwningItem(o)
142 {
143 QMap<QString, OwnerT> items = o.extraOwningItems();
144 {
145 QMutexLocker l(mutex());
146 m_extraOwningItems = items;
147 }
148 }
149 using Callback = DomItem::Callback;
150
151 virtual Path canonicalPath() const = 0;
152
153 Path canonicalPath(const DomItem &) const override;
154 DomItem containingObject(const DomItem &) const override;
155 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
156 template<typename T>
157 void setExtraOwningItem(const QString &fieldName, const std::shared_ptr<T> &item)
158 {
159 QMutexLocker l(mutex());
160 if (!item)
161 m_extraOwningItems.remove(key: fieldName);
162 else
163 m_extraOwningItems.insert(fieldName, item);
164 }
165
166 void clearExtraOwningItems();
167 QMap<QString, OwnerT> extraOwningItems() const;
168
169private:
170 QMap<QString, OwnerT> m_extraOwningItems;
171};
172
173class QMLDOM_EXPORT DomUniverse final : public DomTop,
174 public std::enable_shared_from_this<DomUniverse>
175{
176 Q_GADGET
177 Q_DECLARE_TR_FUNCTIONS(DomUniverse);
178protected:
179 std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
180
181public:
182 constexpr static DomType kindValue = DomType::DomUniverse;
183 DomType kind() const override { return kindValue; }
184
185 static ErrorGroups myErrors();
186
187 DomUniverse(const QString &universeName);
188 DomUniverse(const DomUniverse &) = delete;
189 static std::shared_ptr<DomUniverse> guaranteeUniverse(const std::shared_ptr<DomUniverse> &univ);
190 static DomItem create(const QString &universeName);
191
192 Path canonicalPath() const override;
193 using DomTop::canonicalPath;
194 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
195 std::shared_ptr<DomUniverse> makeCopy(const DomItem &self) const
196 {
197 return std::static_pointer_cast<DomUniverse>(r: doCopy(self));
198 }
199
200 // Helper structure reflecting the change in the map once loading && parsing is completed
201 // formerItem - DomItem representing value (ExternalItemPair) existing in the map before the
202 // loading && parsing. Might be empty (if didn't exist / failure) or equal to currentItem
203 // currentItem - DomItem representing current map value
204 struct LoadResult
205 {
206 DomItem formerItem;
207 DomItem currentItem;
208 };
209
210 LoadResult loadFile(const FileToLoad &file, DomType fileType,
211 DomCreationOption creationOption = {});
212
213 void removePath(const QString &dir);
214
215 std::shared_ptr<ExternalItemPair<GlobalScope>> globalScopeWithName(const QString &name) const
216 {
217 QMutexLocker l(mutex());
218 return m_globalScopeWithName.value(key: name);
219 }
220
221 std::shared_ptr<ExternalItemPair<GlobalScope>> ensureGlobalScopeWithName(const QString &name)
222 {
223 if (auto current = globalScopeWithName(name))
224 return current;
225 auto newScope = std::make_shared<GlobalScope>(args: name);
226 auto newValue = std::make_shared<ExternalItemPair<GlobalScope>>(
227 args&: newScope, args&: newScope);
228 QMutexLocker l(mutex());
229 if (auto current = m_globalScopeWithName.value(key: name))
230 return current;
231 m_globalScopeWithName.insert(key: name, value: newValue);
232 return newValue;
233 }
234
235 QSet<QString> globalScopeNames() const
236 {
237 QMap<QString, std::shared_ptr<ExternalItemPair<GlobalScope>>> map;
238 {
239 QMutexLocker l(mutex());
240 map = m_globalScopeWithName;
241 }
242 return QSet<QString>(map.keyBegin(), map.keyEnd());
243 }
244
245 std::shared_ptr<ExternalItemPair<QmlDirectory>> qmlDirectoryWithPath(const QString &path) const
246 {
247 QMutexLocker l(mutex());
248 return m_qmlDirectoryWithPath.value(key: path);
249 }
250 QSet<QString> qmlDirectoryPaths() const
251 {
252 QMap<QString, std::shared_ptr<ExternalItemPair<QmlDirectory>>> map;
253 {
254 QMutexLocker l(mutex());
255 map = m_qmlDirectoryWithPath;
256 }
257 return QSet<QString>(map.keyBegin(), map.keyEnd());
258 }
259
260 std::shared_ptr<ExternalItemPair<QmldirFile>> qmldirFileWithPath(const QString &path) const
261 {
262 QMutexLocker l(mutex());
263 return m_qmldirFileWithPath.value(key: path);
264 }
265 QSet<QString> qmldirFilePaths() const
266 {
267 QMap<QString, std::shared_ptr<ExternalItemPair<QmldirFile>>> map;
268 {
269 QMutexLocker l(mutex());
270 map = m_qmldirFileWithPath;
271 }
272 return QSet<QString>(map.keyBegin(), map.keyEnd());
273 }
274
275 std::shared_ptr<ExternalItemPair<QmlFile>> qmlFileWithPath(const QString &path) const
276 {
277 QMutexLocker l(mutex());
278 return m_qmlFileWithPath.value(key: path);
279 }
280 QSet<QString> qmlFilePaths() const
281 {
282 QMap<QString, std::shared_ptr<ExternalItemPair<QmlFile>>> map;
283 {
284 QMutexLocker l(mutex());
285 map = m_qmlFileWithPath;
286 }
287 return QSet<QString>(map.keyBegin(), map.keyEnd());
288 }
289
290 std::shared_ptr<ExternalItemPair<JsFile>> jsFileWithPath(const QString &path) const
291 {
292 QMutexLocker l(mutex());
293 return m_jsFileWithPath.value(key: path);
294 }
295 QSet<QString> jsFilePaths() const
296 {
297 QMap<QString, std::shared_ptr<ExternalItemPair<JsFile>>> map;
298 {
299 QMutexLocker l(mutex());
300 map = m_jsFileWithPath;
301 }
302 return QSet<QString>(map.keyBegin(), map.keyEnd());
303 }
304
305 std::shared_ptr<ExternalItemPair<QmltypesFile>> qmltypesFileWithPath(const QString &path) const
306 {
307 QMutexLocker l(mutex());
308 return m_qmltypesFileWithPath.value(key: path);
309 }
310 QSet<QString> qmltypesFilePaths() const
311 {
312 QMap<QString, std::shared_ptr<ExternalItemPair<QmltypesFile>>> map;
313 {
314 QMutexLocker l(mutex());
315 map = m_qmltypesFileWithPath;
316 }
317 return QSet<QString>(map.keyBegin(), map.keyEnd());
318 }
319
320 QString name() const {
321 return m_name;
322 }
323
324private:
325 struct ContentWithDate
326 {
327 QString content;
328 QDateTime date;
329 };
330 // contains either Content with the timestamp when it was read or an Error
331 using ReadResult = std::variant<ContentWithDate, ErrorMessage>;
332 ReadResult readFileContent(const QString &canonicalPath) const;
333
334 LoadResult load(const ContentWithDate &codeWithDate, const FileToLoad &file, DomType fType,
335 DomCreationOption creationOption = {});
336
337 // contains either Content to be parsed or LoadResult if loading / parsing is not needed
338 using PreloadResult = std::variant<ContentWithDate, LoadResult>;
339 PreloadResult preload(const DomItem &univ, const FileToLoad &file, DomType fType) const;
340
341 std::shared_ptr<QmlFile> parseQmlFile(const QString &code, const FileToLoad &file,
342 const QDateTime &contentDate,
343 DomCreationOption creationOption);
344 std::shared_ptr<JsFile> parseJsFile(const QString &code, const FileToLoad &file,
345 const QDateTime &contentDate);
346 std::shared_ptr<ExternalItemPairBase> getPathValueOrNull(DomType fType,
347 const QString &path) const;
348 std::optional<DomItem> getItemIfMostRecent(const DomItem &univ, DomType fType,
349 const QString &path) const;
350 std::optional<DomItem> getItemIfHasSameCode(const DomItem &univ, DomType fType,
351 const QString &canonicalPath,
352 const ContentWithDate &codeWithDate) const;
353 static bool valueHasMostRecentItem(const ExternalItemPairBase *value,
354 const QDateTime &lastModified);
355 static bool valueHasSameContent(const ExternalItemPairBase *value, const QString &content);
356
357 // TODO better name / consider proper public get/set
358 template <typename T>
359 QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &getMutableRefToMap()
360 {
361 Q_ASSERT(!mutex()->tryLock());
362 if constexpr (std::is_same_v<T, QmlDirectory>) {
363 return m_qmlDirectoryWithPath;
364 }
365 if constexpr (std::is_same_v<T, QmldirFile>) {
366 return m_qmldirFileWithPath;
367 }
368 if constexpr (std::is_same_v<T, QmlFile>) {
369 return m_qmlFileWithPath;
370 }
371 if constexpr (std::is_same_v<T, JsFile>) {
372 return m_jsFileWithPath;
373 }
374 if constexpr (std::is_same_v<T, QmltypesFile>) {
375 return m_qmltypesFileWithPath;
376 }
377 if constexpr (std::is_same_v<T, GlobalScope>) {
378 return m_globalScopeWithName;
379 }
380 Q_UNREACHABLE();
381 }
382
383 // Inserts or updates an entry reflecting ExternalItem in the corresponding map
384 // Returns a pair of:
385 // - current ExternalItemPair, current value in the map (might be empty, or equal to curValue)
386 // - new current ExternalItemPair, value in the map after after the execution of this function
387 template <typename T>
388 std::pair<std::shared_ptr<ExternalItemPair<T>>, std::shared_ptr<ExternalItemPair<T>>>
389 insertOrUpdateEntry(std::shared_ptr<T> newItem)
390 {
391 std::shared_ptr<ExternalItemPair<T>> curValue;
392 std::shared_ptr<ExternalItemPair<T>> newCurValue;
393 QString canonicalPath = newItem->canonicalFilePath();
394 QDateTime now = QDateTime::currentDateTimeUtc();
395 {
396 QMutexLocker l(mutex());
397 auto &map = getMutableRefToMap<T>();
398 auto it = map.find(canonicalPath);
399 if (it != map.cend() && (*it) && (*it)->current) {
400 curValue = *it;
401 if (valueHasSameContent(value: curValue.get(), content: newItem->code())) {
402 // value in the map has same content as newItem, a.k.a. most recent
403 newCurValue = curValue;
404 if (newCurValue->current->lastDataUpdateAt() < newItem->lastDataUpdateAt()) {
405 // update timestamp in the current, as if its content was refreshed by
406 // NewItem
407 newCurValue->current->refreshedDataAt(newItem->lastDataUpdateAt());
408 }
409 } else if (curValue->current->lastDataUpdateAt() > newItem->lastDataUpdateAt()) {
410 // value in the map is more recent than newItem, nothing to update
411 newCurValue = curValue;
412 } else {
413 // perform update with newItem
414 curValue->current = std::move(newItem);
415 curValue->currentExposedAt = now;
416 if (curValue->current->isValid()) {
417 curValue->valid = curValue->current;
418 curValue->validExposedAt = std::move(now);
419 }
420 newCurValue = curValue;
421 }
422 } else {
423 // not found / invalid, just insert
424 newCurValue = std::make_shared<ExternalItemPair<T>>(
425 (newItem->isValid() ? newItem : std::shared_ptr<T>()), newItem, now, now);
426 map.insert(canonicalPath, newCurValue);
427 }
428 }
429 return std::make_pair(curValue, newCurValue);
430 }
431
432 // Inserts or updates an entry reflecting ExternalItem in the corresponding map
433 // returns LoadResult reflecting the change made to the map
434 template <typename T>
435 LoadResult insertOrUpdateExternalItem(std::shared_ptr<T> extItem)
436 {
437 auto change = insertOrUpdateEntry<T>(std::move(extItem));
438 DomItem univ(shared_from_this());
439 return { univ.copy(change.first), univ.copy(change.second) };
440 }
441
442private:
443 QString m_name;
444 QMap<QString, std::shared_ptr<ExternalItemPair<GlobalScope>>> m_globalScopeWithName;
445 QMap<QString, std::shared_ptr<ExternalItemPair<QmlDirectory>>> m_qmlDirectoryWithPath;
446 QMap<QString, std::shared_ptr<ExternalItemPair<QmldirFile>>> m_qmldirFileWithPath;
447 QMap<QString, std::shared_ptr<ExternalItemPair<QmlFile>>> m_qmlFileWithPath;
448 QMap<QString, std::shared_ptr<ExternalItemPair<JsFile>>> m_jsFileWithPath;
449 QMap<QString, std::shared_ptr<ExternalItemPair<QmltypesFile>>> m_qmltypesFileWithPath;
450};
451
452class QMLDOM_EXPORT ExternalItemInfoBase: public OwningItem {
453 Q_DECLARE_TR_FUNCTIONS(ExternalItemInfoBase);
454public:
455 constexpr static DomType kindValue = DomType::ExternalItemInfo;
456 DomType kind() const final override { return kindValue; }
457 ExternalItemInfoBase(
458 const Path &canonicalPath,
459 const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC),
460 int derivedFrom = 0,
461 const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC))
462 : OwningItem(derivedFrom, lastDataUpdateAt),
463 m_canonicalPath(canonicalPath),
464 m_currentExposedAt(currentExposedAt)
465 {}
466 ExternalItemInfoBase(const ExternalItemInfoBase &o) = default;
467
468 virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
469 virtual DomItem currentItem(const DomItem &) const = 0;
470
471 QString canonicalFilePath(const DomItem &) const final override;
472 Path canonicalPath() const { return m_canonicalPath; }
473 Path canonicalPath(const DomItem &) const final override { return canonicalPath(); }
474 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override;
475 DomItem field(const DomItem &self, QStringView name) const final override
476 {
477 return OwningItem::field(self, name);
478 }
479
480 int currentRevision(const DomItem &self) const;
481 int lastRevision(const DomItem &self) const;
482 int lastValidRevision(const DomItem &self) const;
483
484 std::shared_ptr<ExternalItemInfoBase> makeCopy(const DomItem &self) const
485 {
486 return std::static_pointer_cast<ExternalItemInfoBase>(r: doCopy(self));
487 }
488
489 QDateTime lastDataUpdateAt() const final override
490 {
491 if (currentItem())
492 return currentItem()->lastDataUpdateAt();
493 return OwningItem::lastDataUpdateAt();
494 }
495
496 void refreshedDataAt(QDateTime tNew) final override
497 {
498 if (currentItem())
499 currentItem()->refreshedDataAt(tNew);
500 return OwningItem::refreshedDataAt(tNew);
501 }
502
503 void ensureLogicalFilePath(const QString &path) {
504 QMutexLocker l(mutex());
505 if (!m_logicalFilePaths.contains(str: path))
506 m_logicalFilePaths.append(t: path);
507 }
508
509 QDateTime currentExposedAt() const {
510 QMutexLocker l(mutex()); // should not be needed, as it should not change...
511 return m_currentExposedAt;
512 }
513
514 void setCurrentExposedAt(QDateTime d) {
515 QMutexLocker l(mutex()); // should not be needed, as it should not change...
516 m_currentExposedAt = d;
517 }
518
519
520 QStringList logicalFilePaths() const {
521 QMutexLocker l(mutex());
522 return m_logicalFilePaths;
523 }
524
525 private:
526 friend class DomEnvironment;
527 Path m_canonicalPath;
528 QDateTime m_currentExposedAt;
529 QStringList m_logicalFilePaths;
530};
531
532template<typename T>
533class ExternalItemInfo final : public ExternalItemInfoBase
534{
535protected:
536 std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
537 {
538 return std::make_shared<ExternalItemInfo>(*this);
539 }
540
541public:
542 constexpr static DomType kindValue = DomType::ExternalItemInfo;
543 ExternalItemInfo(
544 const std::shared_ptr<T> &current = std::shared_ptr<T>(),
545 const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC),
546 int derivedFrom = 0,
547 const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC))
548 : ExternalItemInfoBase(current->canonicalPath().dropTail(), currentExposedAt, derivedFrom,
549 lastDataUpdateAt),
550 current(current)
551 {}
552 ExternalItemInfo(const QString &canonicalPath) : current(new T(canonicalPath)) { }
553 ExternalItemInfo(const ExternalItemInfo &o):
554 ExternalItemInfoBase(o), current(o.current)
555 {
556 }
557
558 std::shared_ptr<ExternalItemInfo> makeCopy(const DomItem &self) const
559 {
560 return std::static_pointer_cast<ExternalItemInfo>(doCopy(self));
561 }
562
563 std::shared_ptr<ExternalOwningItem> currentItem() const override {
564 return current;
565 }
566 DomItem currentItem(const DomItem &self) const override { return self.copy(current); }
567
568 std::shared_ptr<T> current;
569};
570
571class Dependency
572{ // internal, should be cleaned, but nobody should use this...
573public:
574 bool operator==(Dependency const &o) const
575 {
576 return uri == o.uri && version.majorVersion == o.version.majorVersion
577 && version.minorVersion == o.version.minorVersion && filePath == o.filePath;
578 }
579 QString uri; // either dotted uri or file:, http: https: uri
580 Version version;
581 QString filePath; // for file deps
582 DomType fileType;
583};
584
585class QMLDOM_EXPORT LoadInfo final : public OwningItem
586{
587 Q_DECLARE_TR_FUNCTIONS(LoadInfo);
588
589protected:
590 std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
591
592public:
593 constexpr static DomType kindValue = DomType::LoadInfo;
594 DomType kind() const override { return kindValue; }
595
596 enum class Status {
597 NotStarted, // dependencies non checked yet
598 Starting, // adding deps
599 InProgress, // waiting for all deps to be loaded
600 CallingCallbacks, // calling callbacks
601 Done // fully loaded
602 };
603
604 LoadInfo(const Path &elPath = Path(), Status status = Status::NotStarted, int nLoaded = 0,
605 int derivedFrom = 0,
606 const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC))
607 : OwningItem(derivedFrom, lastDataUpdateAt),
608 m_elementCanonicalPath(elPath),
609 m_status(status),
610 m_nLoaded(nLoaded)
611 {
612 }
613 LoadInfo(const LoadInfo &o) : OwningItem(o), m_elementCanonicalPath(o.elementCanonicalPath())
614 {
615 {
616 QMutexLocker l(o.mutex());
617 m_status = o.m_status;
618 m_nLoaded = o.m_nLoaded;
619 m_toDo = o.m_toDo;
620 m_inProgress = o.m_inProgress;
621 m_endCallbacks = o.m_endCallbacks;
622 }
623 }
624
625 Path canonicalPath(const DomItem &self) const override;
626
627 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
628 std::shared_ptr<LoadInfo> makeCopy(const DomItem &self) const
629 {
630 return std::static_pointer_cast<LoadInfo>(r: doCopy(self));
631 }
632 void addError(const DomItem &self, ErrorMessage &&msg) override
633 {
634 self.path(p: elementCanonicalPath()).addError(msg: std::move(msg));
635 }
636
637 void addEndCallback(const DomItem &self, std::function<void(Path, const DomItem &, const DomItem &)> callback);
638
639 void advanceLoad(const DomItem &self);
640 void finishedLoadingDep(const DomItem &self, const Dependency &d);
641 void execEnd(const DomItem &self);
642
643 Status status() const
644 {
645 QMutexLocker l(mutex());
646 return m_status;
647 }
648
649 int nLoaded() const
650 {
651 QMutexLocker l(mutex());
652 return m_nLoaded;
653 }
654
655 Path elementCanonicalPath() const
656 {
657 QMutexLocker l(mutex()); // we should never change this, remove lock?
658 return m_elementCanonicalPath;
659 }
660
661 int nNotDone() const
662 {
663 QMutexLocker l(mutex());
664 return m_toDo.size() + m_inProgress.size();
665 }
666
667 QList<Dependency> inProgress() const
668 {
669 QMutexLocker l(mutex());
670 return m_inProgress;
671 }
672
673 QList<Dependency> toDo() const
674 {
675 QMutexLocker l(mutex());
676 return m_toDo;
677 }
678
679 int nCallbacks() const
680 {
681 QMutexLocker l(mutex());
682 return m_endCallbacks.size();
683 }
684
685private:
686 void doAddDependencies(const DomItem &self);
687 void addDependency(const DomItem &self, const Dependency &dep);
688
689 Path m_elementCanonicalPath;
690 Status m_status;
691 int m_nLoaded;
692 QQueue<Dependency> m_toDo;
693 QList<Dependency> m_inProgress;
694 QList<std::function<void(Path, const DomItem &, const DomItem &)>> m_endCallbacks;
695};
696
697enum class EnvLookup { Normal, NoBase, BaseOnly };
698
699enum class Changeable { ReadOnly, Writable };
700
701class QMLDOM_EXPORT RefCacheEntry
702{
703 Q_GADGET
704public:
705 enum class Cached { None, First, All };
706 Q_ENUM(Cached)
707
708 static RefCacheEntry forPath(const DomItem &el, const Path &canonicalPath);
709 static bool addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry,
710 AddOption addOption = AddOption::KeepExisting);
711
712 Cached cached = Cached::None;
713 QList<Path> canonicalPaths;
714};
715
716class QMLDOM_EXPORT DomEnvironment final : public DomTop,
717 public std::enable_shared_from_this<DomEnvironment>
718{
719 Q_GADGET
720 Q_DECLARE_TR_FUNCTIONS(DomEnvironment);
721protected:
722 std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
723
724private:
725 struct TypeReader
726 {
727 TypeReader(const std::weak_ptr<DomEnvironment> &env, const QStringList &importPaths)
728 : m_env(env), m_importPaths(importPaths)
729 {
730 }
731
732 std::weak_ptr<DomEnvironment> m_env;
733 QStringList m_importPaths;
734
735 QList<QQmlJS::DiagnosticMessage>
736 operator()(QQmlJSImporter *importer, const QString &filePath,
737 const QSharedPointer<QQmlJSScope> &scopeToPopulate);
738 };
739
740public:
741 enum class Option {
742 Default = 0x0,
743 KeepValid = 0x1, // if there is a previous valid version, use that instead of the latest
744 Exported = 0x2, // the current environment is accessible by multiple threads, one should only modify whole OwningItems, and in general load and do other operations in other (Child) environments
745 NoReload = 0x4, // never reload something that was already loaded by the parent environment
746 WeakLoad = 0x8, // load only the names of the available types, not the types (qml files) themselves
747 SingleThreaded = 0x10, // do all operations in a single thread
748 NoDependencies = 0x20 // will not load dependencies (useful when editing)
749 };
750 Q_ENUM(Option)
751 Q_DECLARE_FLAGS(Options, Option);
752
753 static ErrorGroups myErrors();
754 constexpr static DomType kindValue = DomType::DomEnvironment;
755 DomType kind() const override;
756
757 Path canonicalPath() const override;
758 using DomTop::canonicalPath;
759 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
760 DomItem field(const DomItem &self, QStringView name) const final override;
761
762 std::shared_ptr<DomEnvironment> makeCopy(const DomItem &self) const;
763
764 void loadFile(const FileToLoad &file, const Callback &callback,
765 std::optional<DomType> fileType = std::optional<DomType>(),
766 const ErrorHandler &h = nullptr /* used only in loadPendingDependencies*/);
767 void loadBuiltins(const Callback &callback = nullptr, const ErrorHandler &h = nullptr);
768 void loadModuleDependency(const QString &uri, Version v, const Callback &callback = nullptr,
769 const ErrorHandler & = nullptr);
770
771 void removePath(const QString &path);
772
773 std::shared_ptr<DomUniverse> universe() const;
774
775 QSet<QString> moduleIndexUris(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
776 QSet<int> moduleIndexMajorVersions(const DomItem &self, const QString &uri,
777 EnvLookup lookup = EnvLookup::Normal) const;
778 std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion,
779 EnvLookup lookup, Changeable changeable,
780 const ErrorHandler &errorHandler = nullptr);
781 std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion,
782 EnvLookup lookup = EnvLookup::Normal) const;
783 std::shared_ptr<ExternalItemInfo<QmlDirectory>>
784 qmlDirectoryWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
785 QSet<QString> qmlDirectoryPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
786 std::shared_ptr<ExternalItemInfo<QmldirFile>>
787 qmldirFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
788 QSet<QString> qmldirFilePaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
789 std::shared_ptr<ExternalItemInfoBase>
790 qmlDirWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
791 QSet<QString> qmlDirPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
792 std::shared_ptr<ExternalItemInfo<QmlFile>>
793 qmlFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
794 QSet<QString> qmlFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
795 std::shared_ptr<ExternalItemInfo<JsFile>>
796 jsFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
797 QSet<QString> jsFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
798 std::shared_ptr<ExternalItemInfo<QmltypesFile>>
799 qmltypesFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
800 QSet<QString> qmltypesFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
801 std::shared_ptr<ExternalItemInfo<GlobalScope>>
802 globalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal) const;
803 std::shared_ptr<ExternalItemInfo<GlobalScope>>
804 ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal);
805 QSet<QString> globalScopeNames(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
806
807 explicit DomEnvironment(const QStringList &loadPaths, Options options = Option::SingleThreaded,
808 DomCreationOption domCreationOption = Default,
809 const std::shared_ptr<DomUniverse> &universe = nullptr);
810 explicit DomEnvironment(const std::shared_ptr<DomEnvironment> &parent,
811 const QStringList &loadPaths, Options options = Option::SingleThreaded,
812 DomCreationOption domCreationOption = Default);
813 DomEnvironment(const DomEnvironment &o) = delete;
814 static std::shared_ptr<DomEnvironment>
815 create(const QStringList &loadPaths, Options options = Option::SingleThreaded,
816 DomCreationOption creationOption = DomCreationOption::Default,
817 const DomItem &universe = DomItem::empty);
818
819 // TODO AddOption can easily be removed later. KeepExisting option only used in one
820 // place which will be removed in https://codereview.qt-project.org/c/qt/qtdeclarative/+/523217
821 void addQmlFile(const std::shared_ptr<QmlFile> &file,
822 AddOption option = AddOption::KeepExisting);
823 void addQmlDirectory(const std::shared_ptr<QmlDirectory> &file,
824 AddOption option = AddOption::KeepExisting);
825 void addQmldirFile(const std::shared_ptr<QmldirFile> &file,
826 AddOption option = AddOption::KeepExisting);
827 void addQmltypesFile(const std::shared_ptr<QmltypesFile> &file,
828 AddOption option = AddOption::KeepExisting);
829 void addJsFile(const std::shared_ptr<JsFile> &file, AddOption option = AddOption::KeepExisting);
830 void addGlobalScope(const std::shared_ptr<GlobalScope> &file,
831 AddOption option = AddOption::KeepExisting);
832
833 bool commitToBase(
834 const DomItem &self, const std::shared_ptr<DomEnvironment> &validEnv = nullptr);
835
836 void addDependenciesToLoad(const Path &path);
837 void addLoadInfo(
838 const DomItem &self, const std::shared_ptr<LoadInfo> &loadInfo);
839 std::shared_ptr<LoadInfo> loadInfo(const Path &path) const;
840 QList<Path> loadInfoPaths() const;
841 QHash<Path, std::shared_ptr<LoadInfo>> loadInfos() const;
842 void loadPendingDependencies();
843 bool finishLoadingDependencies(int waitMSec = 30000);
844 void addWorkForLoadInfo(const Path &elementCanonicalPath);
845
846 Options options() const;
847
848 std::shared_ptr<DomEnvironment> base() const;
849
850 QStringList loadPaths() const;
851 QStringList qmldirFiles() const;
852
853 QString globalScopeName() const;
854
855 static QList<Import> defaultImplicitImports();
856 QList<Import> implicitImports() const;
857
858 void addAllLoadedCallback(const DomItem &self, Callback c);
859
860 void clearReferenceCache();
861 void setLoadPaths(const QStringList &v);
862
863 // Helper structure reflecting the change in the map once loading / fetching is completed
864 // formerItem - DomItem representing value (ExternalItemInfo) existing in the map before the
865 // loading && parsing. Might be empty (if didn't exist / failure) or equal to currentItem
866 // currentItem - DomItem representing current map value
867 struct LoadResult
868 {
869 DomItem formerItem;
870 DomItem currentItem;
871 };
872 // TODO(QTBUG-121171)
873 template <typename T>
874 LoadResult insertOrUpdateExternalItemInfo(const QString &path, std::shared_ptr<T> extItem)
875 {
876 // maybe in the next revision this all can be just substituted by the addExternalItem
877 DomItem env(shared_from_this());
878 // try to fetch from the current env.
879 if (auto curValue = lookup<T>(path, EnvLookup::NoBase)) {
880 // found in the "initial" env
881 return { env.copy(curValue), env.copy(curValue) };
882 }
883 std::shared_ptr<ExternalItemInfo<T>> newCurValue;
884 // try to fetch from the base env
885 auto valueInBase = lookup<T>(path, EnvLookup::BaseOnly);
886 if (!valueInBase) {
887 // Nothing found. Just create an externalItemInfo which will be inserted
888 newCurValue = std::make_shared<ExternalItemInfo<T>>(std::move(extItem),
889 QDateTime::currentDateTimeUtc());
890 } else {
891 // prepare updated value as a copy of the value from the Base to be inserted
892 newCurValue = valueInBase->makeCopy(env);
893 if (newCurValue->current != extItem) {
894 newCurValue->current = std::move(extItem);
895 newCurValue->setCurrentExposedAt(QDateTime::currentDateTimeUtc());
896 }
897 }
898 // Before inserting new or updated value, check one more time, if ItemInfo is already
899 // present
900 // lookup<> can't be used here because of the data-race
901 {
902 QMutexLocker l(mutex());
903 auto &map = getMutableRefToMap<T>();
904 const auto &it = map.find(path);
905 if (it != map.end())
906 return { env.copy(*it), env.copy(*it) };
907 // otherwise insert
908 map.insert(path, newCurValue);
909 }
910 return { env.copy(valueInBase), env.copy(newCurValue) };
911 }
912
913 template <typename T>
914 void addExternalItemInfo(const DomItem &newExtItem, const Callback &loadCallback,
915 const Callback &endCallback)
916 {
917 // get either Valid "file" from the ExternalItemPair or the current (wip) "file"
918 std::shared_ptr<T> newItemPtr;
919 if (options() & DomEnvironment::Option::KeepValid)
920 newItemPtr = newExtItem.field(name: Fields::validItem).ownerAs<T>();
921 if (!newItemPtr)
922 newItemPtr = newExtItem.field(name: Fields::currentItem).ownerAs<T>();
923 Q_ASSERT(newItemPtr && "envCallbackForFile reached without current file");
924
925 auto loadResult = insertOrUpdateExternalItemInfo(newExtItem.canonicalFilePath(),
926 std::move(newItemPtr));
927 Path p = loadResult.currentItem.canonicalPath();
928 {
929 auto depLoad = qScopeGuard([p, this, endCallback] {
930 addDependenciesToLoad(path: p);
931 // add EndCallback to the queue, which should be called once all dependencies are
932 // loaded
933 if (endCallback) {
934 DomItem env = DomItem(shared_from_this());
935 addAllLoadedCallback(
936 self: env, c: [p, endCallback](Path, const DomItem &, const DomItem &env) {
937 DomItem el = env.path(p);
938 endCallback(p, el, el);
939 });
940 }
941 });
942 // call loadCallback
943 if (loadCallback) {
944 loadCallback(p, loadResult.formerItem, loadResult.currentItem);
945 }
946 }
947 }
948 void populateFromQmlFile(MutableDomItem &&qmlFile);
949 DomCreationOption domCreationOption() const { return m_domCreationOption; }
950
951private:
952 friend class RefCacheEntry;
953
954 void loadFile(const FileToLoad &file, const Callback &loadCallback, const Callback &endCallback,
955 std::optional<DomType> fileType = std::optional<DomType>(),
956 const ErrorHandler &h = nullptr);
957
958 void loadModuleDependency(const DomItem &self, const QString &uri, Version v,
959 Callback loadCallback = nullptr, Callback endCallback = nullptr,
960 const ErrorHandler & = nullptr);
961
962 template <typename T>
963 QSet<QString> getStrings(function_ref<QSet<QString>()> getBase, const QMap<QString, T> &selfMap,
964 EnvLookup lookup) const;
965
966 template <typename T>
967 const QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getConstRefToMap() const
968 {
969 Q_ASSERT(!mutex()->tryLock());
970 if constexpr (std::is_same_v<T, GlobalScope>) {
971 return m_globalScopeWithName;
972 }
973 if constexpr (std::is_same_v<T, QmlDirectory>) {
974 return m_qmlDirectoryWithPath;
975 }
976 if constexpr (std::is_same_v<T, QmldirFile>) {
977 return m_qmldirFileWithPath;
978 }
979 if constexpr (std::is_same_v<T, QmlFile>) {
980 return m_qmlFileWithPath;
981 }
982 if constexpr (std::is_same_v<T, JsFile>) {
983 return m_jsFileWithPath;
984 }
985 if constexpr (std::is_same_v<T, QmltypesFile>) {
986 return m_qmltypesFileWithPath;
987 }
988 Q_UNREACHABLE();
989 }
990
991 template <typename T>
992 std::shared_ptr<ExternalItemInfo<T>> lookup(const QString &path, EnvLookup options) const
993 {
994 if (options != EnvLookup::BaseOnly) {
995 QMutexLocker l(mutex());
996 const auto &map = getConstRefToMap<T>();
997 const auto &it = map.find(path);
998 if (it != map.end())
999 return *it;
1000 }
1001 if (options != EnvLookup::NoBase && m_base)
1002 return m_base->lookup<T>(path, options);
1003 return {};
1004 }
1005
1006 template <typename T>
1007 QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getMutableRefToMap()
1008 {
1009 Q_ASSERT(!mutex()->tryLock());
1010 if constexpr (std::is_same_v<T, QmlDirectory>) {
1011 return m_qmlDirectoryWithPath;
1012 }
1013 if constexpr (std::is_same_v<T, QmldirFile>) {
1014 return m_qmldirFileWithPath;
1015 }
1016 if constexpr (std::is_same_v<T, QmlFile>) {
1017 return m_qmlFileWithPath;
1018 }
1019 if constexpr (std::is_same_v<T, JsFile>) {
1020 return m_jsFileWithPath;
1021 }
1022 if constexpr (std::is_same_v<T, QmltypesFile>) {
1023 return m_qmltypesFileWithPath;
1024 }
1025 if constexpr (std::is_same_v<T, GlobalScope>) {
1026 return m_globalScopeWithName;
1027 }
1028 Q_UNREACHABLE();
1029 }
1030
1031 template <typename T>
1032 void addExternalItem(std::shared_ptr<T> file, QString key, AddOption option)
1033 {
1034 if (!file)
1035 return;
1036
1037 auto eInfo = std::make_shared<ExternalItemInfo<T>>(file, QDateTime::currentDateTimeUtc());
1038 // Lookup helper can't be used here, because it introduces data-race otherwise
1039 // (other modifications might happen between the lookup and the insert)
1040 QMutexLocker l(mutex());
1041 auto &map = getMutableRefToMap<T>();
1042 const auto &it = map.find(key);
1043 if (it != map.end() && option == AddOption::KeepExisting)
1044 return;
1045 map.insert(key, eInfo);
1046 }
1047
1048 using FetchResult =
1049 std::pair<std::shared_ptr<ExternalItemInfoBase>, std::shared_ptr<ExternalItemInfoBase>>;
1050 // This function tries to get an Info object about the ExternalItem from the current env
1051 // and depending on the result and options tries to fetch it from the Parent env,
1052 // saving a copy with an updated timestamp
1053 template <typename T>
1054 FetchResult fetchFileFromEnvs(const FileToLoad &file)
1055 {
1056 const auto &path = file.canonicalPath();
1057 // lookup only in the current env
1058 if (auto value = lookup<T>(path, EnvLookup::NoBase)) {
1059 return std::make_pair(value, value);
1060 }
1061 // try to find the file in the base(parent) Env and insert if found
1062 if (options() & Option::NoReload) {
1063 if (auto baseV = lookup<T>(path, EnvLookup::BaseOnly)) {
1064 // Watch out! QTBUG-121171
1065 // It's possible between the lookup and creation of curVal, baseV && baseV->current
1066 // might have changed
1067 // Prepare a value to be inserted as copy of the value from Base
1068 auto curV = std::make_shared<ExternalItemInfo<T>>(
1069 baseV->current, QDateTime::currentDateTimeUtc(), baseV->revision(),
1070 baseV->lastDataUpdateAt());
1071 // Lookup one more time if the value was already inserted to the current env
1072 // Lookup can't be used here because of the data-race
1073 {
1074 QMutexLocker l(mutex());
1075 auto &map = getMutableRefToMap<T>();
1076 const auto &it = map.find(path);
1077 if (it != map.end())
1078 return std::make_pair(*it, *it);
1079 // otherwise insert
1080 map.insert(path, curV);
1081 }
1082 return std::make_pair(baseV, curV);
1083 }
1084 }
1085 return std::make_pair(x: nullptr, y: nullptr);
1086 }
1087
1088 Callback getLoadCallbackFor(DomType fileType, const Callback &loadCallback);
1089
1090 std::shared_ptr<ModuleIndex> lookupModuleInEnv(const QString &uri, int majorVersion) const;
1091 // ModuleLookupResult contains the ModuleIndex pointer, and an indicator whether it was found
1092 // in m_base or in m_moduleIndexWithUri
1093 struct ModuleLookupResult {
1094 enum Origin : bool {FromBase, FromGlobal};
1095 std::shared_ptr<ModuleIndex> module;
1096 Origin fromBase = FromGlobal;
1097 };
1098 // helper function used by the moduleIndexWithUri methods
1099 ModuleLookupResult moduleIndexWithUriHelper(const DomItem &self, const QString &uri, int majorVersion,
1100 EnvLookup lookup = EnvLookup::Normal) const;
1101
1102 const Options m_options;
1103 const std::shared_ptr<DomEnvironment> m_base;
1104 std::shared_ptr<DomEnvironment> m_lastValidBase;
1105 const std::shared_ptr<DomUniverse> m_universe;
1106 QStringList m_loadPaths; // paths for qml
1107 QString m_globalScopeName;
1108 QMap<QString, QMap<int, std::shared_ptr<ModuleIndex>>> m_moduleIndexWithUri;
1109 QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> m_globalScopeWithName;
1110 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlDirectory>>> m_qmlDirectoryWithPath;
1111 QMap<QString, std::shared_ptr<ExternalItemInfo<QmldirFile>>> m_qmldirFileWithPath;
1112 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlFile>>> m_qmlFileWithPath;
1113 QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> m_jsFileWithPath;
1114 QMap<QString, std::shared_ptr<ExternalItemInfo<QmltypesFile>>> m_qmltypesFileWithPath;
1115 QQueue<Path> m_loadsWithWork;
1116 QQueue<Path> m_inProgress;
1117 QHash<Path, std::shared_ptr<LoadInfo>> m_loadInfos;
1118 QList<Import> m_implicitImports;
1119 QList<Callback> m_allLoadedCallback;
1120 QHash<Path, RefCacheEntry> m_referenceCache;
1121 DomCreationOption m_domCreationOption;
1122
1123 struct SemanticAnalysis
1124 {
1125 SemanticAnalysis(const QStringList &loadPaths);
1126 void updateLoadPaths(const QStringList &loadPaths);
1127
1128 std::shared_ptr<QQmlJSResourceFileMapper> m_mapper;
1129 std::shared_ptr<QQmlJSImporter> m_importer;
1130 };
1131 std::optional<SemanticAnalysis> m_semanticAnalysis;
1132public:
1133 SemanticAnalysis semanticAnalysis();
1134};
1135Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options)
1136
1137} // end namespace Dom
1138} // end namespace QQmlJS
1139QT_END_NAMESPACE
1140#endif // DOMTOP_H
1141

source code of qtdeclarative/src/qmldom/qqmldomtop_p.h