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 | |
32 | QT_BEGIN_NAMESPACE |
33 | |
34 | using namespace Qt::Literals::StringLiterals; |
35 | |
36 | namespace QQmlJS { |
37 | namespace Dom { |
38 | |
39 | class QMLDOM_EXPORT ExternalItemPairBase: public OwningItem { // all access should have the lock of the DomUniverse containing this |
40 | Q_DECLARE_TR_FUNCTIONS(ExternalItemPairBase); |
41 | public: |
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 ¤tExposedAt = 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 | |
96 | template<class T> |
97 | class QMLDOM_EXPORT ExternalItemPair final : public ExternalItemPairBase |
98 | { // all access should have the lock of the DomUniverse containing this |
99 | protected: |
100 | std::shared_ptr<OwningItem> doCopy(const DomItem &) const override |
101 | { |
102 | return std::make_shared<ExternalItemPair>(*this); |
103 | } |
104 | |
105 | public: |
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> ¤t = {}, |
110 | const QDateTime &validExposedAt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC), |
111 | const QDateTime ¤tExposedAt = 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 | |
135 | class QMLDOM_EXPORT DomTop: public OwningItem { |
136 | public: |
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 | |
169 | private: |
170 | QMap<QString, OwnerT> m_extraOwningItems; |
171 | }; |
172 | |
173 | class QMLDOM_EXPORT DomUniverse final : public DomTop, |
174 | public std::enable_shared_from_this<DomUniverse> |
175 | { |
176 | Q_GADGET |
177 | Q_DECLARE_TR_FUNCTIONS(DomUniverse); |
178 | protected: |
179 | std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override; |
180 | |
181 | public: |
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 | |
324 | private: |
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 | |
442 | private: |
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 | |
452 | class QMLDOM_EXPORT ExternalItemInfoBase: public OwningItem { |
453 | Q_DECLARE_TR_FUNCTIONS(ExternalItemInfoBase); |
454 | public: |
455 | constexpr static DomType kindValue = DomType::ExternalItemInfo; |
456 | DomType kind() const final override { return kindValue; } |
457 | ExternalItemInfoBase( |
458 | const Path &canonicalPath, |
459 | const QDateTime ¤tExposedAt = 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 | |
532 | template<typename T> |
533 | class ExternalItemInfo final : public ExternalItemInfoBase |
534 | { |
535 | protected: |
536 | std::shared_ptr<OwningItem> doCopy(const DomItem &) const override |
537 | { |
538 | return std::make_shared<ExternalItemInfo>(*this); |
539 | } |
540 | |
541 | public: |
542 | constexpr static DomType kindValue = DomType::ExternalItemInfo; |
543 | ExternalItemInfo( |
544 | const std::shared_ptr<T> ¤t = std::shared_ptr<T>(), |
545 | const QDateTime ¤tExposedAt = 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 | |
571 | class Dependency |
572 | { // internal, should be cleaned, but nobody should use this... |
573 | public: |
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 | |
585 | class QMLDOM_EXPORT LoadInfo final : public OwningItem |
586 | { |
587 | Q_DECLARE_TR_FUNCTIONS(LoadInfo); |
588 | |
589 | protected: |
590 | std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override; |
591 | |
592 | public: |
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 | |
685 | private: |
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 | |
697 | enum class EnvLookup { Normal, NoBase, BaseOnly }; |
698 | |
699 | enum class Changeable { ReadOnly, Writable }; |
700 | |
701 | class QMLDOM_EXPORT RefCacheEntry |
702 | { |
703 | Q_GADGET |
704 | public: |
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 | |
716 | class QMLDOM_EXPORT DomEnvironment final : public DomTop, |
717 | public std::enable_shared_from_this<DomEnvironment> |
718 | { |
719 | Q_GADGET |
720 | Q_DECLARE_TR_FUNCTIONS(DomEnvironment); |
721 | protected: |
722 | std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override; |
723 | |
724 | private: |
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 | }; |
733 | public: |
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 | |
944 | private: |
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; |
1125 | public: |
1126 | SemanticAnalysis semanticAnalysis(); |
1127 | }; |
1128 | Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options) |
1129 | |
1130 | } // end namespace Dom |
1131 | } // end namespace QQmlJS |
1132 | QT_END_NAMESPACE |
1133 | #endif // DOMTOP_H |
1134 |
Definitions
- ExternalItemPairBase
- kindValue
- kind
- ExternalItemPairBase
- ExternalItemPairBase
- field
- makeCopy
- lastDataUpdateAt
- refreshedDataAt
- ExternalItemPair
- doCopy
- kindValue
- ExternalItemPair
- ExternalItemPair
- validItem
- validItem
- currentItem
- currentItem
- makeCopy
- DomTop
- DomTop
- DomTop
- setExtraOwningItem
- DomUniverse
- kindValue
- kind
- DomUniverse
- makeCopy
- LoadResult
- globalScopeWithName
- ensureGlobalScopeWithName
- globalScopeNames
- qmlDirectoryWithPath
- qmlDirectoryPaths
- qmldirFileWithPath
- qmldirFilePaths
- qmlFileWithPath
- qmlFilePaths
- jsFileWithPath
- jsFilePaths
- qmltypesFileWithPath
- qmltypesFilePaths
- name
- ContentWithDate
- getMutableRefToMap
- insertOrUpdateEntry
- insertOrUpdateExternalItem
- ExternalItemInfoBase
- kindValue
- kind
- ExternalItemInfoBase
- ExternalItemInfoBase
- canonicalPath
- canonicalPath
- field
- makeCopy
- lastDataUpdateAt
- refreshedDataAt
- ensureLogicalFilePath
- currentExposedAt
- setCurrentExposedAt
- logicalFilePaths
- ExternalItemInfo
- doCopy
- kindValue
- ExternalItemInfo
- ExternalItemInfo
- ExternalItemInfo
- makeCopy
- currentItem
- currentItem
- Dependency
- operator==
- LoadInfo
- kindValue
- kind
- Status
- LoadInfo
- LoadInfo
- makeCopy
- addError
- status
- nLoaded
- elementCanonicalPath
- nNotDone
- inProgress
- toDo
- nCallbacks
- EnvLookup
- Changeable
- RefCacheEntry
- Cached
- DomEnvironment
- TypeReader
- Option
- kindValue
- DomEnvironment
- LoadResult
- insertOrUpdateExternalItemInfo
- addExternalItemInfo
- domCreationOptions
- getConstRefToMap
- lookup
- getMutableRefToMap
- addExternalItem
- fetchFileFromEnvs
- ModuleLookupResult
- Origin
Learn Advanced QML with KDAB
Find out more