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 DomCreationOptions creationOptions = {});
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 DomCreationOptions creationOptions = {});
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 DomCreationOptions creationOptions);
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 QPair<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 qMakePair(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 std::weak_ptr<DomEnvironment> m_env;
728
729 QList<QQmlJS::DiagnosticMessage>
730 operator()(QQmlJSImporter *importer, const QString &filePath,
731 const QSharedPointer<QQmlJSScope> &scopeToPopulate);
732 };
733public:
734 enum class Option {
735 Default = 0x0,
736 KeepValid = 0x1, // if there is a previous valid version, use that instead of the latest
737 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
738 NoReload = 0x4, // never reload something that was already loaded by the parent environment
739 WeakLoad = 0x8, // load only the names of the available types, not the types (qml files) themselves
740 SingleThreaded = 0x10, // do all operations in a single thread
741 NoDependencies = 0x20 // will not load dependencies (useful when editing)
742 };
743 Q_ENUM(Option)
744 Q_DECLARE_FLAGS(Options, Option);
745
746 static ErrorGroups myErrors();
747 constexpr static DomType kindValue = DomType::DomEnvironment;
748 DomType kind() const override;
749
750 Path canonicalPath() const override;
751 using DomTop::canonicalPath;
752 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
753 DomItem field(const DomItem &self, QStringView name) const final override;
754
755 std::shared_ptr<DomEnvironment> makeCopy(const DomItem &self) const;
756
757 void loadFile(const FileToLoad &file, const Callback &callback,
758 std::optional<DomType> fileType = std::optional<DomType>(),
759 const ErrorHandler &h = nullptr /* used only in loadPendingDependencies*/);
760 void loadBuiltins(const Callback &callback = nullptr, const ErrorHandler &h = nullptr);
761 void loadModuleDependency(const QString &uri, Version v, const Callback &callback = nullptr,
762 const ErrorHandler & = nullptr);
763
764 void removePath(const QString &path);
765
766 std::shared_ptr<DomUniverse> universe() const;
767
768 QSet<QString> moduleIndexUris(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
769 QSet<int> moduleIndexMajorVersions(const DomItem &self, const QString &uri,
770 EnvLookup lookup = EnvLookup::Normal) const;
771 std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion,
772 EnvLookup lookup, Changeable changeable,
773 const ErrorHandler &errorHandler = nullptr);
774 std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion,
775 EnvLookup lookup = EnvLookup::Normal) const;
776 std::shared_ptr<ExternalItemInfo<QmlDirectory>>
777 qmlDirectoryWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
778 QSet<QString> qmlDirectoryPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
779 std::shared_ptr<ExternalItemInfo<QmldirFile>>
780 qmldirFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
781 QSet<QString> qmldirFilePaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
782 std::shared_ptr<ExternalItemInfoBase>
783 qmlDirWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
784 QSet<QString> qmlDirPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
785 std::shared_ptr<ExternalItemInfo<QmlFile>>
786 qmlFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
787 QSet<QString> qmlFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
788 std::shared_ptr<ExternalItemInfo<JsFile>>
789 jsFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
790 QSet<QString> jsFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
791 std::shared_ptr<ExternalItemInfo<QmltypesFile>>
792 qmltypesFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
793 QSet<QString> qmltypesFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
794 std::shared_ptr<ExternalItemInfo<GlobalScope>>
795 globalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal) const;
796 std::shared_ptr<ExternalItemInfo<GlobalScope>>
797 ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal);
798 QSet<QString> globalScopeNames(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
799
800 explicit DomEnvironment(const QStringList &loadPaths, Options options = Option::SingleThreaded,
801 DomCreationOptions domCreationOptions = None,
802 const std::shared_ptr<DomUniverse> &universe = nullptr);
803 explicit DomEnvironment(const std::shared_ptr<DomEnvironment> &parent,
804 const QStringList &loadPaths, Options options = Option::SingleThreaded,
805 DomCreationOptions domCreationOptions = None);
806 DomEnvironment(const DomEnvironment &o) = delete;
807 static std::shared_ptr<DomEnvironment>
808 create(const QStringList &loadPaths, Options options = Option::SingleThreaded,
809 DomCreationOptions creationOptions = DomCreationOption::None,
810 const DomItem &universe = DomItem::empty);
811
812 // TODO AddOption can easily be removed later. KeepExisting option only used in one
813 // place which will be removed in https://codereview.qt-project.org/c/qt/qtdeclarative/+/523217
814 void addQmlFile(const std::shared_ptr<QmlFile> &file,
815 AddOption option = AddOption::KeepExisting);
816 void addQmlDirectory(const std::shared_ptr<QmlDirectory> &file,
817 AddOption option = AddOption::KeepExisting);
818 void addQmldirFile(const std::shared_ptr<QmldirFile> &file,
819 AddOption option = AddOption::KeepExisting);
820 void addQmltypesFile(const std::shared_ptr<QmltypesFile> &file,
821 AddOption option = AddOption::KeepExisting);
822 void addJsFile(const std::shared_ptr<JsFile> &file, AddOption option = AddOption::KeepExisting);
823 void addGlobalScope(const std::shared_ptr<GlobalScope> &file,
824 AddOption option = AddOption::KeepExisting);
825
826 bool commitToBase(
827 const DomItem &self, const std::shared_ptr<DomEnvironment> &validEnv = nullptr);
828
829 void addDependenciesToLoad(const Path &path);
830 void addLoadInfo(
831 const DomItem &self, const std::shared_ptr<LoadInfo> &loadInfo);
832 std::shared_ptr<LoadInfo> loadInfo(const Path &path) const;
833 QList<Path> loadInfoPaths() const;
834 QHash<Path, std::shared_ptr<LoadInfo>> loadInfos() const;
835 void loadPendingDependencies();
836 bool finishLoadingDependencies(int waitMSec = 30000);
837 void addWorkForLoadInfo(const Path &elementCanonicalPath);
838
839 Options options() const;
840
841 std::shared_ptr<DomEnvironment> base() const;
842
843 QStringList loadPaths() const;
844 QStringList qmldirFiles() const;
845
846 QString globalScopeName() const;
847
848 static QList<Import> defaultImplicitImports();
849 QList<Import> implicitImports() const;
850
851 void addAllLoadedCallback(const DomItem &self, Callback c);
852
853 void clearReferenceCache();
854 void setLoadPaths(const QStringList &v);
855
856 // Helper structure reflecting the change in the map once loading / fetching is completed
857 // formerItem - DomItem representing value (ExternalItemInfo) existing in the map before the
858 // loading && parsing. Might be empty (if didn't exist / failure) or equal to currentItem
859 // currentItem - DomItem representing current map value
860 struct LoadResult
861 {
862 DomItem formerItem;
863 DomItem currentItem;
864 };
865 // TODO(QTBUG-121171)
866 template <typename T>
867 LoadResult insertOrUpdateExternalItemInfo(const QString &path, std::shared_ptr<T> extItem)
868 {
869 // maybe in the next revision this all can be just substituted by the addExternalItem
870 DomItem env(shared_from_this());
871 // try to fetch from the current env.
872 if (auto curValue = lookup<T>(path, EnvLookup::NoBase)) {
873 // found in the "initial" env
874 return { env.copy(curValue), env.copy(curValue) };
875 }
876 std::shared_ptr<ExternalItemInfo<T>> newCurValue;
877 // try to fetch from the base env
878 auto valueInBase = lookup<T>(path, EnvLookup::BaseOnly);
879 if (!valueInBase) {
880 // Nothing found. Just create an externalItemInfo which will be inserted
881 newCurValue = std::make_shared<ExternalItemInfo<T>>(std::move(extItem),
882 QDateTime::currentDateTimeUtc());
883 } else {
884 // prepare updated value as a copy of the value from the Base to be inserted
885 newCurValue = valueInBase->makeCopy(env);
886 if (newCurValue->current != extItem) {
887 newCurValue->current = std::move(extItem);
888 newCurValue->setCurrentExposedAt(QDateTime::currentDateTimeUtc());
889 }
890 }
891 // Before inserting new or updated value, check one more time, if ItemInfo is already
892 // present
893 // lookup<> can't be used here because of the data-race
894 {
895 QMutexLocker l(mutex());
896 auto &map = getMutableRefToMap<T>();
897 const auto &it = map.find(path);
898 if (it != map.end())
899 return { env.copy(*it), env.copy(*it) };
900 // otherwise insert
901 map.insert(path, newCurValue);
902 }
903 return { env.copy(valueInBase), env.copy(newCurValue) };
904 }
905
906 template <typename T>
907 void addExternalItemInfo(const DomItem &newExtItem, const Callback &loadCallback,
908 const Callback &endCallback)
909 {
910 // get either Valid "file" from the ExternalItemPair or the current (wip) "file"
911 std::shared_ptr<T> newItemPtr;
912 if (options() & DomEnvironment::Option::KeepValid)
913 newItemPtr = newExtItem.field(name: Fields::validItem).ownerAs<T>();
914 if (!newItemPtr)
915 newItemPtr = newExtItem.field(name: Fields::currentItem).ownerAs<T>();
916 Q_ASSERT(newItemPtr && "envCallbackForFile reached without current file");
917
918 auto loadResult = insertOrUpdateExternalItemInfo(newExtItem.canonicalFilePath(),
919 std::move(newItemPtr));
920 Path p = loadResult.currentItem.canonicalPath();
921 {
922 auto depLoad = qScopeGuard([p, this, endCallback] {
923 addDependenciesToLoad(path: p);
924 // add EndCallback to the queue, which should be called once all dependencies are
925 // loaded
926 if (endCallback) {
927 DomItem env = DomItem(shared_from_this());
928 addAllLoadedCallback(
929 self: env, c: [p, endCallback](Path, const DomItem &, const DomItem &env) {
930 DomItem el = env.path(p);
931 endCallback(p, el, el);
932 });
933 }
934 });
935 // call loadCallback
936 if (loadCallback) {
937 loadCallback(p, loadResult.formerItem, loadResult.currentItem);
938 }
939 }
940 }
941 void populateFromQmlFile(MutableDomItem &&qmlFile);
942 DomCreationOptions domCreationOptions() const { return m_domCreationOptions; }
943
944private:
945 friend class RefCacheEntry;
946
947 void loadFile(const FileToLoad &file, const Callback &loadCallback, const Callback &endCallback,
948 std::optional<DomType> fileType = std::optional<DomType>(),
949 const ErrorHandler &h = nullptr);
950
951 void loadModuleDependency(const DomItem &self, const QString &uri, Version v,
952 Callback loadCallback = nullptr, Callback endCallback = nullptr,
953 const ErrorHandler & = nullptr);
954
955 template <typename T>
956 QSet<QString> getStrings(function_ref<QSet<QString>()> getBase, const QMap<QString, T> &selfMap,
957 EnvLookup lookup) const;
958
959 template <typename T>
960 const QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getConstRefToMap() const
961 {
962 Q_ASSERT(!mutex()->tryLock());
963 if constexpr (std::is_same_v<T, GlobalScope>) {
964 return m_globalScopeWithName;
965 }
966 if constexpr (std::is_same_v<T, QmlDirectory>) {
967 return m_qmlDirectoryWithPath;
968 }
969 if constexpr (std::is_same_v<T, QmldirFile>) {
970 return m_qmldirFileWithPath;
971 }
972 if constexpr (std::is_same_v<T, QmlFile>) {
973 return m_qmlFileWithPath;
974 }
975 if constexpr (std::is_same_v<T, JsFile>) {
976 return m_jsFileWithPath;
977 }
978 if constexpr (std::is_same_v<T, QmltypesFile>) {
979 return m_qmltypesFileWithPath;
980 }
981 Q_UNREACHABLE();
982 }
983
984 template <typename T>
985 std::shared_ptr<ExternalItemInfo<T>> lookup(const QString &path, EnvLookup options) const
986 {
987 if (options != EnvLookup::BaseOnly) {
988 QMutexLocker l(mutex());
989 const auto &map = getConstRefToMap<T>();
990 const auto &it = map.find(path);
991 if (it != map.end())
992 return *it;
993 }
994 if (options != EnvLookup::NoBase && m_base)
995 return m_base->lookup<T>(path, options);
996 return {};
997 }
998
999 template <typename T>
1000 QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getMutableRefToMap()
1001 {
1002 Q_ASSERT(!mutex()->tryLock());
1003 if constexpr (std::is_same_v<T, QmlDirectory>) {
1004 return m_qmlDirectoryWithPath;
1005 }
1006 if constexpr (std::is_same_v<T, QmldirFile>) {
1007 return m_qmldirFileWithPath;
1008 }
1009 if constexpr (std::is_same_v<T, QmlFile>) {
1010 return m_qmlFileWithPath;
1011 }
1012 if constexpr (std::is_same_v<T, JsFile>) {
1013 return m_jsFileWithPath;
1014 }
1015 if constexpr (std::is_same_v<T, QmltypesFile>) {
1016 return m_qmltypesFileWithPath;
1017 }
1018 if constexpr (std::is_same_v<T, GlobalScope>) {
1019 return m_globalScopeWithName;
1020 }
1021 Q_UNREACHABLE();
1022 }
1023
1024 template <typename T>
1025 void addExternalItem(std::shared_ptr<T> file, QString key, AddOption option)
1026 {
1027 if (!file)
1028 return;
1029
1030 auto eInfo = std::make_shared<ExternalItemInfo<T>>(file, QDateTime::currentDateTimeUtc());
1031 // Lookup helper can't be used here, because it introduces data-race otherwise
1032 // (other modifications might happen between the lookup and the insert)
1033 QMutexLocker l(mutex());
1034 auto &map = getMutableRefToMap<T>();
1035 const auto &it = map.find(key);
1036 if (it != map.end() && option == AddOption::KeepExisting)
1037 return;
1038 map.insert(key, eInfo);
1039 }
1040
1041 using FetchResult =
1042 QPair<std::shared_ptr<ExternalItemInfoBase>, std::shared_ptr<ExternalItemInfoBase>>;
1043 // This function tries to get an Info object about the ExternalItem from the current env
1044 // and depending on the result and options tries to fetch it from the Parent env,
1045 // saving a copy with an updated timestamp
1046 template <typename T>
1047 FetchResult fetchFileFromEnvs(const FileToLoad &file)
1048 {
1049 const auto &path = file.canonicalPath();
1050 // lookup only in the current env
1051 if (auto value = lookup<T>(path, EnvLookup::NoBase)) {
1052 return qMakePair(value, value);
1053 }
1054 // try to find the file in the base(parent) Env and insert if found
1055 if (options() & Option::NoReload) {
1056 if (auto baseV = lookup<T>(path, EnvLookup::BaseOnly)) {
1057 // Watch out! QTBUG-121171
1058 // It's possible between the lookup and creation of curVal, baseV && baseV->current
1059 // might have changed
1060 // Prepare a value to be inserted as copy of the value from Base
1061 auto curV = std::make_shared<ExternalItemInfo<T>>(
1062 baseV->current, QDateTime::currentDateTimeUtc(), baseV->revision(),
1063 baseV->lastDataUpdateAt());
1064 // Lookup one more time if the value was already inserted to the current env
1065 // Lookup can't be used here because of the data-race
1066 {
1067 QMutexLocker l(mutex());
1068 auto &map = getMutableRefToMap<T>();
1069 const auto &it = map.find(path);
1070 if (it != map.end())
1071 return qMakePair(*it, *it);
1072 // otherwise insert
1073 map.insert(path, curV);
1074 }
1075 return qMakePair(baseV, curV);
1076 }
1077 }
1078 return qMakePair(value1: nullptr, value2: nullptr);
1079 }
1080
1081 Callback getLoadCallbackFor(DomType fileType, const Callback &loadCallback);
1082
1083 std::shared_ptr<ModuleIndex> lookupModuleInEnv(const QString &uri, int majorVersion) const;
1084 // ModuleLookupResult contains the ModuleIndex pointer, and an indicator whether it was found
1085 // in m_base or in m_moduleIndexWithUri
1086 struct ModuleLookupResult {
1087 enum Origin : bool {FromBase, FromGlobal};
1088 std::shared_ptr<ModuleIndex> module;
1089 Origin fromBase = FromGlobal;
1090 };
1091 // helper function used by the moduleIndexWithUri methods
1092 ModuleLookupResult moduleIndexWithUriHelper(const DomItem &self, const QString &uri, int majorVersion,
1093 EnvLookup lookup = EnvLookup::Normal) const;
1094
1095 const Options m_options;
1096 const std::shared_ptr<DomEnvironment> m_base;
1097 std::shared_ptr<DomEnvironment> m_lastValidBase;
1098 const std::shared_ptr<DomUniverse> m_universe;
1099 QStringList m_loadPaths; // paths for qml
1100 QString m_globalScopeName;
1101 QMap<QString, QMap<int, std::shared_ptr<ModuleIndex>>> m_moduleIndexWithUri;
1102 QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> m_globalScopeWithName;
1103 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlDirectory>>> m_qmlDirectoryWithPath;
1104 QMap<QString, std::shared_ptr<ExternalItemInfo<QmldirFile>>> m_qmldirFileWithPath;
1105 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlFile>>> m_qmlFileWithPath;
1106 QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> m_jsFileWithPath;
1107 QMap<QString, std::shared_ptr<ExternalItemInfo<QmltypesFile>>> m_qmltypesFileWithPath;
1108 QQueue<Path> m_loadsWithWork;
1109 QQueue<Path> m_inProgress;
1110 QHash<Path, std::shared_ptr<LoadInfo>> m_loadInfos;
1111 QList<Import> m_implicitImports;
1112 QList<Callback> m_allLoadedCallback;
1113 QHash<Path, RefCacheEntry> m_referenceCache;
1114 DomCreationOptions m_domCreationOptions;
1115
1116 struct SemanticAnalysis
1117 {
1118 SemanticAnalysis(const QStringList &loadPaths);
1119 void updateLoadPaths(const QStringList &loadPaths);
1120
1121 std::shared_ptr<QQmlJSResourceFileMapper> m_mapper;
1122 std::shared_ptr<QQmlJSImporter> m_importer;
1123 };
1124 std::optional<SemanticAnalysis> m_semanticAnalysis;
1125public:
1126 SemanticAnalysis semanticAnalysis();
1127};
1128Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options)
1129
1130} // end namespace Dom
1131} // end namespace QQmlJS
1132QT_END_NAMESPACE
1133#endif // DOMTOP_H
1134

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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