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 QMLDOMITEM_H
5#define QMLDOMITEM_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 "qqmldom_global.h"
19#include "qqmldom_fwd_p.h"
20#include "qqmldom_utils_p.h"
21#include "qqmldomconstants_p.h"
22#include "qqmldomstringdumper_p.h"
23#include "qqmldompath_p.h"
24#include "qqmldomerrormessage_p.h"
25#include "qqmldomfunctionref_p.h"
26#include "qqmldomfilewriter_p.h"
27#include "qqmldomlinewriter_p.h"
28#include "qqmldomfieldfilter_p.h"
29
30#include <QtCore/QMap>
31#include <QtCore/QMultiMap>
32#include <QtCore/QSet>
33#include <QtCore/QString>
34#include <QtCore/QStringView>
35#include <QtCore/QDebug>
36#include <QtCore/QDateTime>
37#include <QtCore/QMutex>
38#include <QtCore/QCborValue>
39#include <QtCore/QTimeZone>
40#include <QtQml/private/qqmljssourcelocation_p.h>
41#include <QtQmlCompiler/private/qqmljsscope_p.h>
42
43#include <memory>
44#include <typeinfo>
45#include <utility>
46#include <type_traits>
47#include <variant>
48#include <optional>
49#include <cstddef>
50
51QT_BEGIN_NAMESPACE
52
53QT_DECLARE_EXPORTED_QT_LOGGING_CATEGORY(writeOutLog, QMLDOM_EXPORT);
54
55namespace QQmlJS {
56// we didn't have enough 'O's to properly name everything...
57namespace Dom {
58
59class Path;
60
61constexpr bool domTypeIsObjWrap(DomType k);
62constexpr bool domTypeIsValueWrap(DomType k);
63constexpr bool domTypeIsDomElement(DomType);
64constexpr bool domTypeIsOwningItem(DomType);
65constexpr bool domTypeIsUnattachedOwningItem(DomType);
66constexpr bool domTypeIsScriptElement(DomType);
67QMLDOM_EXPORT bool domTypeIsExternalItem(DomType k);
68QMLDOM_EXPORT bool domTypeIsTopItem(DomType k);
69QMLDOM_EXPORT bool domTypeIsContainer(DomType k);
70constexpr bool domTypeCanBeInline(DomType k)
71{
72 switch (k) {
73 case DomType::Empty:
74 case DomType::Map:
75 case DomType::List:
76 case DomType::ListP:
77 case DomType::ConstantData:
78 case DomType::SimpleObjectWrap:
79 case DomType::ScriptElementWrap:
80 case DomType::Reference:
81 return true;
82 default:
83 return false;
84 }
85}
86QMLDOM_EXPORT bool domTypeIsScope(DomType k);
87
88QMLDOM_EXPORT QMap<DomType,QString> domTypeToStringMap();
89QMLDOM_EXPORT QString domTypeToString(DomType k);
90QMLDOM_EXPORT QMap<DomKind, QString> domKindToStringMap();
91QMLDOM_EXPORT QString domKindToString(DomKind k);
92
93inline bool noFilter(const DomItem &, const PathEls::PathComponent &, const DomItem &)
94{
95 return true;
96}
97
98using DirectVisitor = function_ref<bool(const PathEls::PathComponent &, function_ref<DomItem()>)>;
99// using DirectVisitor = function_ref<bool(Path, const DomItem &)>;
100
101namespace {
102template<typename T>
103struct IsMultiMap : std::false_type
104{
105};
106
107template<typename Key, typename T>
108struct IsMultiMap<QMultiMap<Key, T>> : std::true_type
109{
110};
111
112template<typename T>
113struct IsMap : std::false_type
114{
115};
116
117template<typename Key, typename T>
118struct IsMap<QMap<Key, T>> : std::true_type
119{
120};
121
122template<typename... Ts>
123using void_t = void;
124
125template<typename T, typename = void>
126struct IsDomObject : std::false_type
127{
128};
129
130template<typename T>
131struct IsDomObject<T, void_t<decltype(T::kindValue)>> : std::true_type
132{
133};
134
135template<typename T, typename = void>
136struct IsInlineDom : std::false_type
137{
138};
139
140template<typename T>
141struct IsInlineDom<T, void_t<decltype(T::kindValue)>>
142 : std::integral_constant<bool, domTypeCanBeInline(T::kindValue)>
143{
144};
145
146template<typename T>
147struct IsInlineDom<T *, void_t<decltype(T::kindValue)>> : std::true_type
148{
149};
150
151template<typename T>
152struct IsInlineDom<std::shared_ptr<T>, void_t<decltype(T::kindValue)>> : std::true_type
153{
154};
155
156template<typename T>
157struct IsSharedPointerToDomObject : std::false_type
158{
159};
160
161template<typename T>
162struct IsSharedPointerToDomObject<std::shared_ptr<T>> : IsDomObject<T>
163{
164};
165
166template<typename T, typename = void>
167struct IsList : std::false_type
168{
169};
170
171template<typename T>
172struct IsList<T, void_t<typename T::value_type>> : std::true_type
173{
174};
175
176}
177
178template<typename T>
179union SubclassStorage {
180 int i;
181 T lp;
182
183 // TODO: these are extremely nasty. What is this int doing in here?
184 T *data() { return reinterpret_cast<T *>(this); }
185 const T *data() const { return reinterpret_cast<const T *>(this); }
186
187 SubclassStorage() { }
188 SubclassStorage(T &&el) { el.moveTo(data()); }
189 SubclassStorage(const T *el) { el->copyTo(data()); }
190 SubclassStorage(const SubclassStorage &o) : SubclassStorage(o.data()) { }
191 SubclassStorage(const SubclassStorage &&o) : SubclassStorage(o.data()) { }
192 SubclassStorage &operator=(const SubclassStorage &o)
193 {
194 data()->~T();
195 o.data()->copyTo(data());
196 return *this;
197 }
198 ~SubclassStorage() { data()->~T(); }
199};
200
201class QMLDOM_EXPORT DomBase
202{
203public:
204 using FilterT = function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>;
205
206 virtual ~DomBase() = default;
207
208 DomBase *domBase() { return this; }
209 const DomBase *domBase() const { return this; }
210
211 // minimal overload set:
212 virtual DomType kind() const = 0;
213 virtual DomKind domKind() const;
214 virtual Path pathFromOwner(const DomItem &self) const = 0;
215 virtual Path canonicalPath(const DomItem &self) const = 0;
216 virtual bool
217 iterateDirectSubpaths(const DomItem &self,
218 DirectVisitor visitor) const = 0; // iterates the *direct* subpaths, returns
219 // false if a quick end was requested
220
221 bool iterateDirectSubpathsConst(const DomItem &self, DirectVisitor)
222 const; // iterates the *direct* subpaths, returns false if a quick end was requested
223
224 virtual DomItem containingObject(
225 const DomItem &self) const; // the DomItem corresponding to the canonicalSource source
226 virtual void dump(const DomItem &, const Sink &sink, int indent, FilterT filter) const;
227 virtual quintptr id() const;
228 QString typeName() const;
229
230 virtual QList<QString> fields(const DomItem &self) const;
231 virtual DomItem field(const DomItem &self, QStringView name) const;
232
233 virtual index_type indexes(const DomItem &self) const;
234 virtual DomItem index(const DomItem &self, index_type index) const;
235
236 virtual QSet<QString> const keys(const DomItem &self) const;
237 virtual DomItem key(const DomItem &self, const QString &name) const;
238
239 virtual QString canonicalFilePath(const DomItem &self) const;
240
241 virtual void writeOut(const DomItem &self, OutWriter &lw) const;
242
243 virtual QCborValue value() const {
244 return QCborValue();
245 }
246};
247
248inline DomKind kind2domKind(DomType k)
249{
250 switch (k) {
251 case DomType::Empty:
252 return DomKind::Empty;
253 case DomType::List:
254 case DomType::ListP:
255 return DomKind::List;
256 case DomType::Map:
257 return DomKind::Map;
258 case DomType::ConstantData:
259 return DomKind::Value;
260 default:
261 return DomKind::Object;
262 }
263}
264
265class QMLDOM_EXPORT Empty final : public DomBase
266{
267public:
268 constexpr static DomType kindValue = DomType::Empty;
269 DomType kind() const override { return kindValue; }
270
271 Empty *operator->() { return this; }
272 const Empty *operator->() const { return this; }
273 Empty &operator*() { return *this; }
274 const Empty &operator*() const { return *this; }
275
276 Empty();
277 quintptr id() const override { return ~quintptr(0); }
278 Path pathFromOwner(const DomItem &self) const override;
279 Path canonicalPath(const DomItem &self) const override;
280 DomItem containingObject(const DomItem &self) const override;
281 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
282 void dump(const DomItem &, const Sink &s, int indent,
283 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter)
284 const override;
285};
286
287class QMLDOM_EXPORT DomElement: public DomBase {
288protected:
289 DomElement& operator=(const DomElement&) = default;
290public:
291 DomElement(const Path &pathFromOwner = Path());
292 DomElement(const DomElement &o) = default;
293 Path pathFromOwner(const DomItem &self) const override;
294 Path pathFromOwner() const { return m_pathFromOwner; }
295 Path canonicalPath(const DomItem &self) const override;
296 DomItem containingObject(const DomItem &self) const override;
297 virtual void updatePathFromOwner(const Path &newPath);
298
299private:
300 Path m_pathFromOwner;
301};
302
303class QMLDOM_EXPORT Map final : public DomElement
304{
305public:
306 constexpr static DomType kindValue = DomType::Map;
307 DomType kind() const override { return kindValue; }
308
309 Map *operator->() { return this; }
310 const Map *operator->() const { return this; }
311 Map &operator*() { return *this; }
312 const Map &operator*() const { return *this; }
313
314 using LookupFunction = std::function<DomItem(const DomItem &, QString)>;
315 using Keys = std::function<QSet<QString>(const DomItem &)>;
316 Map(const Path &pathFromOwner, const LookupFunction &lookup,
317 const Keys &keys, const QString &targetType);
318 quintptr id() const override;
319 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
320 QSet<QString> const keys(const DomItem &self) const override;
321 DomItem key(const DomItem &self, const QString &name) const override;
322
323 template<typename T>
324 static Map fromMultiMapRef(const Path &pathFromOwner, const QMultiMap<QString, T> &mmap);
325 template<typename T>
326 static Map fromMultiMap(const Path &pathFromOwner, const QMultiMap<QString, T> &mmap);
327 template<typename T>
328 static Map
329 fromMapRef(
330 const Path &pathFromOwner, const QMap<QString, T> &mmap,
331 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper);
332
333 template<typename T>
334 static Map fromFileRegionMap(
335 const Path &pathFromOwner, const QMap<FileLocationRegion, T> &map);
336 template<typename T>
337 static Map fromFileRegionListMap(
338 const Path &pathFromOwner, const QMap<FileLocationRegion, QList<T>> &map);
339
340private:
341 template<typename MapT>
342 static QSet<QString> fileRegionKeysFromMap(const MapT &map);
343 LookupFunction m_lookup;
344 Keys m_keys;
345 QString m_targetType;
346};
347
348class QMLDOM_EXPORT List final : public DomElement
349{
350public:
351 constexpr static DomType kindValue = DomType::List;
352 DomType kind() const override { return kindValue; }
353
354 List *operator->() { return this; }
355 const List *operator->() const { return this; }
356 List &operator*() { return *this; }
357 const List &operator*() const { return *this; }
358
359 using LookupFunction = std::function<DomItem(const DomItem &, index_type)>;
360 using Length = std::function<index_type(const DomItem &)>;
361 using IteratorFunction =
362 std::function<bool(const DomItem &, function_ref<bool(index_type, function_ref<DomItem()>)>)>;
363
364 List(const Path &pathFromOwner, const LookupFunction &lookup, const Length &length,
365 const IteratorFunction &iterator, const QString &elType);
366 quintptr id() const override;
367 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
368 void
369 dump(const DomItem &, const Sink &s, int indent,
370 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const override;
371 index_type indexes(const DomItem &self) const override;
372 DomItem index(const DomItem &self, index_type index) const override;
373
374 template<typename T>
375 static List
376 fromQList(const Path &pathFromOwner, const QList<T> &list,
377 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
378 ListOptions options = ListOptions::Normal);
379 template<typename T>
380 static List
381 fromQListRef(const Path &pathFromOwner, const QList<T> &list,
382 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
383 ListOptions options = ListOptions::Normal);
384 void writeOut(const DomItem &self, OutWriter &ow, bool compact) const;
385 void writeOut(const DomItem &self, OutWriter &ow) const override { writeOut(self, ow, compact: true); }
386
387private:
388 LookupFunction m_lookup;
389 Length m_length;
390 IteratorFunction m_iterator;
391 QString m_elType;
392};
393
394class QMLDOM_EXPORT ListPBase : public DomElement
395{
396public:
397 constexpr static DomType kindValue = DomType::ListP;
398 DomType kind() const override { return kindValue; }
399
400 ListPBase(const Path &pathFromOwner, const QList<const void *> &pList, const QString &elType)
401 : DomElement(pathFromOwner), m_pList(pList), m_elType(elType)
402 {
403 }
404 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const override;
405 virtual void copyTo(ListPBase *) const { Q_ASSERT(false); };
406 virtual void moveTo(ListPBase *) const { Q_ASSERT(false); };
407 quintptr id() const override { return quintptr(0); }
408 index_type indexes(const DomItem &) const override { return index_type(m_pList.size()); }
409 void writeOut(const DomItem &self, OutWriter &ow, bool compact) const;
410 void writeOut(const DomItem &self, OutWriter &ow) const override { writeOut(self, ow, compact: true); }
411
412protected:
413 QList<const void *> m_pList;
414 QString m_elType;
415};
416
417template<typename T>
418class ListPT final : public ListPBase
419{
420public:
421 constexpr static DomType kindValue = DomType::ListP;
422
423 ListPT(const Path &pathFromOwner, const QList<T *> &pList, const QString &elType = QString(),
424 ListOptions options = ListOptions::Normal)
425 : ListPBase(pathFromOwner, {},
426 (elType.isEmpty() ? QLatin1String(typeid(T).name()) : elType))
427 {
428 static_assert(sizeof(ListPBase) == sizeof(ListPT),
429 "ListPT does not have the same size as ListPBase");
430 static_assert(alignof(ListPBase) == alignof(ListPT),
431 "ListPT does not have the same size as ListPBase");
432 m_pList.reserve(size: pList.size());
433 if (options == ListOptions::Normal) {
434 for (const void *p : pList)
435 m_pList.append(p);
436 } else if (options == ListOptions::Reverse) {
437 for (qsizetype i = pList.size(); i-- != 0;)
438 // probably writing in reverse and reading sequentially would be better
439 m_pList.append(pList.at(i));
440 } else {
441 Q_ASSERT(false);
442 }
443 }
444 void copyTo(ListPBase *t) const override { new (t) ListPT(*this); }
445 void moveTo(ListPBase *t) const override { new (t) ListPT(std::move(*this)); }
446 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const override;
447
448 DomItem index(const DomItem &self, index_type index) const override;
449};
450
451class QMLDOM_EXPORT ListP
452{
453public:
454 constexpr static DomType kindValue = DomType::ListP;
455 template<typename T>
456 ListP(const Path &pathFromOwner, const QList<T *> &pList, const QString &elType = QString(),
457 ListOptions options = ListOptions::Normal)
458 : list(ListPT<T>(pathFromOwner, pList, elType, options))
459 {
460 }
461 ListP() = delete;
462
463 ListPBase *operator->() { return list.data(); }
464 const ListPBase *operator->() const { return list.data(); }
465 ListPBase &operator*() { return *list.data(); }
466 const ListPBase &operator*() const { return *list.data(); }
467
468private:
469 SubclassStorage<ListPBase> list;
470};
471
472class QMLDOM_EXPORT ConstantData final : public DomElement
473{
474public:
475 constexpr static DomType kindValue = DomType::ConstantData;
476 DomType kind() const override { return kindValue; }
477
478 enum class Options {
479 MapIsMap,
480 FirstMapIsFields
481 };
482
483 ConstantData *operator->() { return this; }
484 const ConstantData *operator->() const { return this; }
485 ConstantData &operator*() { return *this; }
486 const ConstantData &operator*() const { return *this; }
487
488 ConstantData(const Path &pathFromOwner, const QCborValue &value,
489 Options options = Options::MapIsMap);
490 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
491 quintptr id() const override;
492 DomKind domKind() const override;
493 QCborValue value() const override { return m_value; }
494 Options options() const { return m_options; }
495private:
496 QCborValue m_value;
497 Options m_options;
498};
499
500class QMLDOM_EXPORT SimpleObjectWrapBase : public DomElement
501{
502public:
503 constexpr static DomType kindValue = DomType::SimpleObjectWrap;
504 DomType kind() const final override { return m_kind; }
505
506 quintptr id() const final override { return m_id; }
507 DomKind domKind() const final override { return m_domKind; }
508
509 template <typename T>
510 T const *as() const
511 {
512 if (m_options & SimpleWrapOption::ValueType) {
513 if (m_value.metaType() == QMetaType::fromType<T>())
514 return static_cast<const T *>(m_value.constData());
515 return nullptr;
516 } else {
517 return m_value.value<const T *>();
518 }
519 }
520
521 SimpleObjectWrapBase() = delete;
522 virtual void copyTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); }
523 virtual void moveTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); }
524 bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override
525 {
526 Q_ASSERT(false);
527 return true;
528 }
529
530protected:
531 friend class TestDomItem;
532 SimpleObjectWrapBase(const Path &pathFromOwner, const QVariant &value, quintptr idValue,
533 DomType kind = kindValue,
534 SimpleWrapOptions options = SimpleWrapOption::None)
535 : DomElement(pathFromOwner),
536 m_kind(kind),
537 m_domKind(kind2domKind(k: kind)),
538 m_value(value),
539 m_id(idValue),
540 m_options(options)
541 {
542 }
543
544 DomType m_kind;
545 DomKind m_domKind;
546 QVariant m_value;
547 quintptr m_id;
548 SimpleWrapOptions m_options;
549};
550
551template<typename T>
552class SimpleObjectWrapT final : public SimpleObjectWrapBase
553{
554public:
555 constexpr static DomType kindValue = DomType::SimpleObjectWrap;
556
557 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
558 {
559 return asT()->iterateDirectSubpaths(self, visitor);
560 }
561
562 void writeOut(const DomItem &self, OutWriter &lw) const override;
563
564 const T *asT() const
565 {
566 if constexpr (domTypeIsValueWrap(T::kindValue)) {
567 if (m_value.metaType() == QMetaType::fromType<T>())
568 return static_cast<const T *>(m_value.constData());
569 return nullptr;
570 } else if constexpr (domTypeIsObjWrap(T::kindValue)) {
571 return m_value.value<const T *>();
572 } else {
573 // need dependent static assert to not unconditially trigger
574 static_assert(!std::is_same_v<T, T>, "wrapping of unexpected type");
575 return nullptr; // necessary to avoid warnings on INTEGRITY
576 }
577 }
578
579 void copyTo(SimpleObjectWrapBase *target) const override
580 {
581 static_assert(sizeof(SimpleObjectWrapBase) == sizeof(SimpleObjectWrapT),
582 "Size mismatch in SimpleObjectWrapT");
583 static_assert(alignof(SimpleObjectWrapBase) == alignof(SimpleObjectWrapT),
584 "Size mismatch in SimpleObjectWrapT");
585 new (target) SimpleObjectWrapT(*this);
586 }
587
588 void moveTo(SimpleObjectWrapBase *target) const override
589 {
590 static_assert(sizeof(SimpleObjectWrapBase) == sizeof(SimpleObjectWrapT),
591 "Size mismatch in SimpleObjectWrapT");
592 static_assert(alignof(SimpleObjectWrapBase) == alignof(SimpleObjectWrapT),
593 "Size mismatch in SimpleObjectWrapT");
594 new (target) SimpleObjectWrapT(std::move(*this));
595 }
596
597 SimpleObjectWrapT(const Path &pathFromOwner, const QVariant &v,
598 quintptr idValue, SimpleWrapOptions o)
599 : SimpleObjectWrapBase(pathFromOwner, v, idValue, T::kindValue, o)
600 {
601 Q_ASSERT(domTypeIsValueWrap(T::kindValue) == bool(o & SimpleWrapOption::ValueType));
602 }
603};
604
605class QMLDOM_EXPORT SimpleObjectWrap
606{
607public:
608 constexpr static DomType kindValue = DomType::SimpleObjectWrap;
609
610 SimpleObjectWrapBase *operator->() { return wrap.data(); }
611 const SimpleObjectWrapBase *operator->() const { return wrap.data(); }
612 SimpleObjectWrapBase &operator*() { return *wrap.data(); }
613 const SimpleObjectWrapBase &operator*() const { return *wrap.data(); }
614
615 template<typename T>
616 static SimpleObjectWrap fromObjectRef(const Path &pathFromOwner, T &value)
617 {
618 return SimpleObjectWrap(pathFromOwner, value);
619 }
620 SimpleObjectWrap() = delete;
621
622private:
623 template<typename T>
624 SimpleObjectWrap(const Path &pathFromOwner, T &value)
625 {
626 using BaseT = std::decay_t<T>;
627 if constexpr (domTypeIsObjWrap(BaseT::kindValue)) {
628 new (wrap.data()) SimpleObjectWrapT<BaseT>(pathFromOwner, QVariant::fromValue(&value),
629 quintptr(&value), SimpleWrapOption::None);
630 } else if constexpr (domTypeIsValueWrap(BaseT::kindValue)) {
631 new (wrap.data()) SimpleObjectWrapT<BaseT>(pathFromOwner, QVariant::fromValue(value),
632 quintptr(0), SimpleWrapOption::ValueType);
633 } else {
634 qCWarning(domLog) << "Unexpected object to wrap in SimpleObjectWrap: "
635 << domTypeToString(BaseT::kindValue);
636 Q_ASSERT_X(false, "SimpleObjectWrap",
637 "simple wrap of unexpected object"); // allow? (mocks for testing,...)
638 new (wrap.data())
639 SimpleObjectWrapT<BaseT>(pathFromOwner, nullptr, 0, SimpleWrapOption::None);
640 }
641 }
642 SubclassStorage<SimpleObjectWrapBase> wrap;
643};
644
645class QMLDOM_EXPORT Reference final : public DomElement
646{
647 Q_GADGET
648public:
649 constexpr static DomType kindValue = DomType::Reference;
650 DomType kind() const override { return kindValue; }
651
652 Reference *operator->() { return this; }
653 const Reference *operator->() const { return this; }
654 Reference &operator*() { return *this; }
655 const Reference &operator*() const { return *this; }
656
657 bool shouldCache() const;
658 Reference(const Path &referredObject = Path(), const Path &pathFromOwner = Path(),
659 const SourceLocation &loc = SourceLocation());
660 quintptr id() const override;
661 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
662 DomItem field(const DomItem &self, QStringView name) const override;
663 QList<QString> fields(const DomItem &self) const override;
664 index_type indexes(const DomItem &) const override { return 0; }
665 DomItem index(const DomItem &, index_type) const override;
666 QSet<QString> const keys(const DomItem &) const override { return {}; }
667 DomItem key(const DomItem &, const QString &) const override;
668
669 DomItem get(const DomItem &self, const ErrorHandler &h = nullptr,
670 QList<Path> *visitedRefs = nullptr) const;
671 QList<DomItem> getAll(const DomItem &self, const ErrorHandler &h = nullptr,
672 QList<Path> *visitedRefs = nullptr) const;
673
674 Path referredObjectPath;
675};
676
677namespace FileLocations {
678struct Info;
679}
680
681/*!
682 \internal
683 \brief A common base class for all the script elements.
684
685 This marker class allows to use all the script elements as a ScriptElement*, using virtual
686 dispatch. For now, it does not add any extra functionality, compared to a DomElement, but allows
687 to forbid DomElement* at the places where only script elements are required.
688 */
689// TODO: do we need another marker struct like this one to differentiate expressions from
690// statements? This would allow to avoid mismatchs between script expressions and script statements,
691// using type-safety.
692struct ScriptElement : public DomElement
693{
694 template<typename T>
695 using PointerType = std::shared_ptr<T>;
696
697 using DomElement::DomElement;
698 virtual void
699 createFileLocations(const std::shared_ptr<FileLocations::Node> &fileLocationOfOwner) = 0;
700
701 QQmlJSScope::ConstPtr semanticScope();
702 void setSemanticScope(const QQmlJSScope::ConstPtr &scope);
703
704private:
705 QQmlJSScope::ConstPtr m_scope;
706};
707
708/*!
709 \internal
710 \brief Use this to contain any script element.
711 */
712class ScriptElementVariant
713{
714private:
715 template<typename... T>
716 using VariantOfPointer = std::variant<ScriptElement::PointerType<T>...>;
717
718 template<typename T, typename Variant>
719 struct TypeIsInVariant;
720
721 template<typename T, typename... Ts>
722 struct TypeIsInVariant<T, std::variant<Ts...>> : public std::disjunction<std::is_same<T, Ts>...>
723 {
724 };
725
726public:
727 using ScriptElementT =
728 VariantOfPointer<ScriptElements::BlockStatement, ScriptElements::IdentifierExpression,
729 ScriptElements::ForStatement, ScriptElements::BinaryExpression,
730 ScriptElements::VariableDeclarationEntry, ScriptElements::Literal,
731 ScriptElements::IfStatement, ScriptElements::GenericScriptElement,
732 ScriptElements::VariableDeclaration, ScriptElements::ReturnStatement>;
733
734 template<typename T>
735 static ScriptElementVariant fromElement(const T &element)
736 {
737 static_assert(TypeIsInVariant<T, ScriptElementT>::value,
738 "Cannot construct ScriptElementVariant from T, as it is missing from the "
739 "ScriptElementT.");
740 ScriptElementVariant p;
741 p.m_data = element;
742 return p;
743 }
744
745 ScriptElement::PointerType<ScriptElement> base() const;
746
747 operator bool() const { return m_data.has_value(); }
748
749 template<typename F>
750 void visitConst(F &&visitor) const
751 {
752 if (m_data)
753 std::visit(std::forward<F>(visitor), *m_data);
754 }
755
756 template<typename F>
757 void visit(F &&visitor)
758 {
759 if (m_data)
760 std::visit(std::forward<F>(visitor), *m_data);
761 }
762 std::optional<ScriptElementT> data() { return m_data; }
763 void setData(const ScriptElementT &data) { m_data = data; }
764
765private:
766 std::optional<ScriptElementT> m_data;
767};
768
769/*!
770 \internal
771
772 To avoid cluttering the already unwieldy \l ElementT type below with all the types that the
773 different script elements can have, wrap them in an extra class. It will behave like an internal
774 Dom structure (e.g. like a List or a Map) and contain a pointer the the script element.
775 */
776class ScriptElementDomWrapper
777{
778public:
779 ScriptElementDomWrapper(const ScriptElementVariant &element) : m_element(element) { }
780
781 static constexpr DomType kindValue = DomType::ScriptElementWrap;
782
783 DomBase *operator->() { return m_element.base().get(); }
784 const DomBase *operator->() const { return m_element.base().get(); }
785 DomBase &operator*() { return *m_element.base(); }
786 const DomBase &operator*() const { return *m_element.base(); }
787
788 ScriptElementVariant element() const { return m_element; }
789
790private:
791 ScriptElementVariant m_element;
792};
793
794// TODO: create more "groups" to simplify this variant? Maybe into Internal, ScriptExpression, ???
795using ElementT =
796 std::variant<ConstantData, Empty, List, ListP, Map, Reference, ScriptElementDomWrapper,
797 SimpleObjectWrap, const AstComments *, const FileLocations::Node *,
798 const DomEnvironment *, const DomUniverse *, const EnumDecl *,
799 const ExternalItemInfoBase *, const ExternalItemPairBase *,
800 const GlobalComponent *, const GlobalScope *, const JsFile *,
801 const JsResource *, const LoadInfo *, const MockObject *, const MockOwner *,
802 const ModuleIndex *, const ModuleScope *, const QmlComponent *,
803 const QmlDirectory *, const QmlFile *, const QmlObject *, const QmldirFile *,
804 const QmltypesComponent *, const QmltypesFile *, const ScriptExpression *>;
805
806using TopT = std::variant<
807 std::monostate,
808 std::shared_ptr<DomEnvironment>,
809 std::shared_ptr<DomUniverse>>;
810
811using OwnerT =
812 std::variant<std::monostate, std::shared_ptr<ModuleIndex>, std::shared_ptr<MockOwner>,
813 std::shared_ptr<ExternalItemInfoBase>, std::shared_ptr<ExternalItemPairBase>,
814 std::shared_ptr<QmlDirectory>, std::shared_ptr<QmldirFile>,
815 std::shared_ptr<JsFile>, std::shared_ptr<QmlFile>,
816 std::shared_ptr<QmltypesFile>, std::shared_ptr<GlobalScope>,
817 std::shared_ptr<ScriptExpression>, std::shared_ptr<AstComments>,
818 std::shared_ptr<LoadInfo>, std::shared_ptr<FileLocations::Node>,
819 std::shared_ptr<DomEnvironment>, std::shared_ptr<DomUniverse>>;
820
821inline bool emptyChildrenVisitor(Path, const DomItem &, bool)
822{
823 return true;
824}
825
826class MutableDomItem;
827
828class FileToLoad
829{
830public:
831 struct InMemoryContents
832 {
833 QString data;
834 QDateTime date = QDateTime::currentDateTimeUtc();
835 };
836
837 FileToLoad(const std::weak_ptr<DomEnvironment> &environment, const QString &canonicalPath,
838 const QString &logicalPath, const std::optional<InMemoryContents> &content);
839 FileToLoad() = default;
840
841 static FileToLoad fromMemory(const std::weak_ptr<DomEnvironment> &environment,
842 const QString &path, const QString &data);
843 static FileToLoad fromFileSystem(const std::weak_ptr<DomEnvironment> &environment,
844 const QString &canonicalPath);
845
846 std::weak_ptr<DomEnvironment> environment() const { return m_environment; }
847 QString canonicalPath() const { return m_canonicalPath; }
848 QString logicalPath() const { return m_logicalPath; }
849 void setCanonicalPath(const QString &canonicalPath) { m_canonicalPath = canonicalPath; }
850 void setLogicalPath(const QString &logicalPath) { m_logicalPath = logicalPath; }
851 std::optional<InMemoryContents> content() const { return m_content; }
852
853private:
854 std::weak_ptr<DomEnvironment> m_environment;
855 QString m_canonicalPath;
856 QString m_logicalPath;
857 std::optional<InMemoryContents> m_content;
858};
859
860class QMLDOM_EXPORT DomItem {
861 Q_DECLARE_TR_FUNCTIONS(DomItem);
862public:
863 using Callback = function<void(const Path &, const DomItem &, const DomItem &)>;
864
865 using InternalKind = DomType;
866 using Visitor = function_ref<bool(const Path &, const DomItem &)>;
867 using ChildrenVisitor = function_ref<bool(const Path &, const DomItem &, bool)>;
868
869 static ErrorGroup domErrorGroup;
870 static ErrorGroups myErrors();
871 static ErrorGroups myResolveErrors();
872 static DomItem empty;
873
874 enum class CopyOption { EnvConnected, EnvDisconnected };
875
876 template<typename F>
877 auto visitEl(F f) const
878 {
879 return std::visit(f, this->m_element);
880 }
881
882 explicit operator bool() const { return m_kind != DomType::Empty; }
883 InternalKind internalKind() const {
884 return m_kind;
885 }
886 QString internalKindStr() const { return domTypeToString(k: internalKind()); }
887 DomKind domKind() const
888 {
889 if (m_kind == DomType::ConstantData)
890 return std::get<ConstantData>(v: m_element).domKind();
891 else
892 return kind2domKind(k: m_kind);
893 }
894
895 Path canonicalPath() const;
896
897 DomItem filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const;
898 DomItem containingObject() const;
899 DomItem container() const;
900 DomItem owner() const;
901 DomItem top() const;
902 DomItem environment() const;
903 DomItem universe() const;
904 DomItem containingFile() const;
905 DomItem containingScriptExpression() const;
906 DomItem goToFile(const QString &filePath) const;
907 DomItem goUp(int) const;
908 DomItem directParent() const;
909
910 DomItem qmlObject(GoTo option = GoTo::Strict,
911 FilterUpOptions options = FilterUpOptions::ReturnOuter) const;
912 DomItem fileObject(GoTo option = GoTo::Strict) const;
913 DomItem rootQmlObject(GoTo option = GoTo::Strict) const;
914 DomItem globalScope() const;
915 DomItem component(GoTo option = GoTo::Strict) const;
916 DomItem scope(FilterUpOptions options = FilterUpOptions::ReturnOuter) const;
917 QQmlJSScope::ConstPtr nearestSemanticScope() const;
918 QQmlJSScope::ConstPtr semanticScope() const;
919
920 // convenience getters
921 DomItem get(const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
922 QList<DomItem> getAll(const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
923 bool isOwningItem() const { return domTypeIsOwningItem(internalKind()); }
924 bool isExternalItem() const { return domTypeIsExternalItem(k: internalKind()); }
925 bool isTopItem() const { return domTypeIsTopItem(k: internalKind()); }
926 bool isContainer() const { return domTypeIsContainer(k: internalKind()); }
927 bool isScope() const { return domTypeIsScope(k: internalKind()); }
928 bool isCanonicalChild(const DomItem &child) const;
929 bool hasAnnotations() const;
930 QString name() const { return field(name: Fields::name).value().toString(); }
931 DomItem pragmas() const { return field(name: Fields::pragmas); }
932 DomItem ids() const { return field(name: Fields::ids); }
933 QString idStr() const { return field(name: Fields::idStr).value().toString(); }
934 DomItem propertyInfos() const { return field(name: Fields::propertyInfos); }
935 PropertyInfo propertyInfoWithName(const QString &name) const;
936 QSet<QString> propertyInfoNames() const;
937 DomItem propertyDefs() const { return field(name: Fields::propertyDefs); }
938 DomItem bindings() const { return field(name: Fields::bindings); }
939 DomItem methods() const { return field(name: Fields::methods); }
940 DomItem enumerations() const { return field(name: Fields::enumerations); }
941 DomItem children() const { return field(name: Fields::children); }
942 DomItem child(index_type i) const { return field(name: Fields::children).index(i); }
943 DomItem annotations() const
944 {
945 if (hasAnnotations())
946 return field(name: Fields::annotations);
947 else
948 return DomItem();
949 }
950
951 bool resolve(const Path &path, Visitor visitor, const ErrorHandler &errorHandler,
952 ResolveOptions options = ResolveOption::None, const Path &fullPath = Path(),
953 QList<Path> *visitedRefs = nullptr) const;
954
955 DomItem operator[](const Path &path) const;
956 DomItem operator[](QStringView component) const;
957 DomItem operator[](const QString &component) const;
958 DomItem operator[](const char16_t *component) const
959 {
960 return (*this)[QStringView(component)];
961 } // to avoid clash with stupid builtin ptrdiff_t[DomItem&], coming from C
962 DomItem operator[](index_type i) const { return index(i); }
963 DomItem operator[](int i) const { return index(i); }
964 index_type size() const { return indexes() + keys().size(); }
965 index_type length() const { return size(); }
966
967 DomItem path(const Path &p, const ErrorHandler &h = &defaultErrorHandler) const;
968 DomItem path(const QString &p, const ErrorHandler &h = &defaultErrorHandler) const;
969 DomItem path(QStringView p, const ErrorHandler &h = &defaultErrorHandler) const;
970
971 QList<QString> fields() const;
972 DomItem field(QStringView name) const;
973
974 index_type indexes() const;
975 DomItem index(index_type) const;
976 bool visitIndexes(function_ref<bool(const DomItem &)> visitor) const;
977
978 QSet<QString> keys() const;
979 QStringList sortedKeys() const;
980 DomItem key(const QString &name) const;
981 DomItem key(QStringView name) const { return key(name: name.toString()); }
982 bool visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const;
983
984 QList<DomItem> values() const;
985 void writeOutPre(OutWriter &lw) const;
986 void writeOut(OutWriter &lw) const;
987 void writeOutPost(OutWriter &lw) const;
988 bool writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const;
989 bool writeOut(const QString &path, int nBackups = 2,
990 const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr,
991 WriteOutChecks extraChecks = WriteOutCheck::Default) const;
992
993 bool visitTree(const Path &basePath, ChildrenVisitor visitor,
994 VisitOptions options = VisitOption::Default,
995 ChildrenVisitor openingVisitor = emptyChildrenVisitor,
996 ChildrenVisitor closingVisitor = emptyChildrenVisitor,
997 const FieldFilter &filter = FieldFilter::noFilter()) const;
998 bool visitPrototypeChain(function_ref<bool(const DomItem &)> visitor,
999 VisitPrototypesOptions options = VisitPrototypesOption::Normal,
1000 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1001 QList<Path> *visitedRefs = nullptr) const;
1002 bool visitDirectAccessibleScopes(function_ref<bool(const DomItem &)> visitor,
1003 VisitPrototypesOptions options = VisitPrototypesOption::Normal,
1004 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1005 QList<Path> *visitedRefs = nullptr) const;
1006 bool
1007 visitStaticTypePrototypeChains(function_ref<bool(const DomItem &)> visitor,
1008 VisitPrototypesOptions options = VisitPrototypesOption::Normal,
1009 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1010 QList<Path> *visitedRefs = nullptr) const;
1011
1012 bool visitUp(function_ref<bool(const DomItem &)> visitor) const;
1013 bool visitScopeChain(
1014 function_ref<bool(const DomItem &)> visitor, LookupOptions = LookupOption::Normal,
1015 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1016 QList<Path> *visitedRefs = nullptr) const;
1017 bool visitLocalSymbolsNamed(
1018 const QString &name, function_ref<bool(const DomItem &)> visitor) const;
1019 bool visitLookup1(
1020 const QString &symbolName, function_ref<bool(const DomItem &)> visitor,
1021 LookupOptions = LookupOption::Normal, const ErrorHandler &h = nullptr,
1022 QSet<quintptr> *visited = nullptr, QList<Path> *visitedRefs = nullptr) const;
1023 bool visitLookup(
1024 const QString &symbolName, function_ref<bool(const DomItem &)> visitor,
1025 LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal,
1026 const ErrorHandler &errorHandler = nullptr, QSet<quintptr> *visited = nullptr,
1027 QList<Path> *visitedRefs = nullptr) const;
1028 bool visitSubSymbolsNamed(
1029 const QString &name, function_ref<bool(const DomItem &)> visitor) const;
1030 DomItem proceedToScope(
1031 const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
1032 QList<DomItem> lookup(
1033 const QString &symbolName, LookupType type = LookupType::Symbol,
1034 LookupOptions = LookupOption::Normal, const ErrorHandler &errorHandler = nullptr) const;
1035 DomItem lookupFirst(
1036 const QString &symbolName, LookupType type = LookupType::Symbol,
1037 LookupOptions = LookupOption::Normal, const ErrorHandler &errorHandler = nullptr) const;
1038
1039 quintptr id() const;
1040 Path pathFromOwner() const;
1041 QString canonicalFilePath() const;
1042 MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected) const;
1043 bool commitToBase(const std::shared_ptr<DomEnvironment> &validPtr = nullptr) const;
1044 DomItem refreshed() const { return top().path(p: canonicalPath()); }
1045 QCborValue value() const;
1046
1047 void dumpPtr(const Sink &sink) const;
1048 void dump(const Sink &, int indent = 0,
1049 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter =
1050 noFilter) const;
1051 FileWriter::Status
1052 dump(const QString &path,
1053 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter,
1054 int nBackups = 2, int indent = 0, FileWriter *fw = nullptr) const;
1055 QString toString() const;
1056
1057 // OwnigItem elements
1058 int derivedFrom() const;
1059 int revision() const;
1060 QDateTime createdAt() const;
1061 QDateTime frozenAt() const;
1062 QDateTime lastDataUpdateAt() const;
1063
1064 void addError(ErrorMessage &&msg) const;
1065 ErrorHandler errorHandler() const;
1066 void clearErrors(const ErrorGroups &groups = ErrorGroups({}), bool iterate = true) const;
1067 // return false if a quick exit was requested
1068 bool iterateErrors(
1069 function_ref<bool (const DomItem &, const ErrorMessage &)> visitor, bool iterate,
1070 Path inPath = Path()) const;
1071
1072 bool iterateSubOwners(function_ref<bool(const DomItem &owner)> visitor) const;
1073 bool iterateDirectSubpaths(DirectVisitor v) const;
1074
1075 template<typename T>
1076 DomItem subDataItem(const PathEls::PathComponent &c, const T &value,
1077 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1078 template<typename T>
1079 DomItem subDataItemField(QStringView f, const T &value,
1080 ConstantData::Options options = ConstantData::Options::MapIsMap) const
1081 {
1082 return subDataItem(PathEls::Field(f), value, options);
1083 }
1084 template<typename T>
1085 DomItem subValueItem(const PathEls::PathComponent &c, const T &value,
1086 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1087 template<typename T>
1088 bool dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, const T &value,
1089 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1090 template<typename T>
1091 bool dvValueField(DirectVisitor visitor, QStringView f, const T &value,
1092 ConstantData::Options options = ConstantData::Options::MapIsMap) const
1093 {
1094 return this->dvValue<T>(std::move(visitor), PathEls::Field(f), value, options);
1095 }
1096 template<typename F>
1097 bool dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
1098 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1099 template<typename F>
1100 bool dvValueLazyField(DirectVisitor visitor, QStringView f, F valueF,
1101 ConstantData::Options options = ConstantData::Options::MapIsMap) const
1102 {
1103 return this->dvValueLazy(std::move(visitor), PathEls::Field(f), valueF, options);
1104 }
1105 DomItem subLocationItem(const PathEls::PathComponent &c, SourceLocation loc) const
1106 {
1107 return this->subDataItem(c, value: sourceLocationToQCborValue(loc));
1108 }
1109 // bool dvSubReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path
1110 // referencedObject);
1111 DomItem subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const;
1112 DomItem subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const;
1113 bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, const Path &referencedObject) const
1114 {
1115 return dvItem(visitor: std::move(visitor), c, it: [c, this, referencedObject]() {
1116 return this->subReferenceItem(c, referencedObject);
1117 });
1118 }
1119 bool dvReferences(
1120 DirectVisitor visitor, const PathEls::PathComponent &c, const QList<Path> &paths) const
1121 {
1122 return dvItem(visitor: std::move(visitor), c, it: [c, this, paths]() {
1123 return this->subReferencesItem(c, paths);
1124 });
1125 }
1126 bool dvReferenceField(DirectVisitor visitor, QStringView f, const Path &referencedObject) const
1127 {
1128 return dvReference(visitor: std::move(visitor), c: PathEls::Field(f), referencedObject);
1129 }
1130 bool dvReferencesField(DirectVisitor visitor, QStringView f, const QList<Path> &paths) const
1131 {
1132 return dvReferences(visitor: std::move(visitor), c: PathEls::Field(f), paths);
1133 }
1134 bool dvItem(DirectVisitor visitor, const PathEls::PathComponent &c, function_ref<DomItem()> it) const
1135 {
1136 return visitor(c, it);
1137 }
1138 bool dvItemField(DirectVisitor visitor, QStringView f, function_ref<DomItem()> it) const
1139 {
1140 return dvItem(visitor: std::move(visitor), c: PathEls::Field(f), it);
1141 }
1142 DomItem subListItem(const List &list) const;
1143 DomItem subMapItem(const Map &map) const;
1144 DomItem subObjectWrapItem(SimpleObjectWrap obj) const
1145 {
1146 return DomItem(m_top, m_owner, m_ownerPath, obj);
1147 }
1148
1149 DomItem subScriptElementWrapperItem(const ScriptElementVariant &obj) const
1150 {
1151 Q_ASSERT(obj);
1152 return DomItem(m_top, m_owner, m_ownerPath, ScriptElementDomWrapper(obj));
1153 }
1154
1155 template<typename Owner>
1156 DomItem subOwnerItem(const PathEls::PathComponent &c, Owner o) const
1157 {
1158 if constexpr (domTypeIsUnattachedOwningItem(Owner::element_type::kindValue))
1159 return DomItem(m_top, o, canonicalPath().withComponent(c), o.get());
1160 else
1161 return DomItem(m_top, o, Path(), o.get());
1162 }
1163 template<typename T>
1164 DomItem wrap(const PathEls::PathComponent &c, const T &obj) const;
1165 template<typename T>
1166 DomItem wrapField(QStringView f, const T &obj) const
1167 {
1168 return wrap<T>(PathEls::Field(f), obj);
1169 }
1170 template<typename T>
1171 bool dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj) const;
1172 template<typename T>
1173 bool dvWrapField(DirectVisitor visitor, QStringView f, T &obj) const
1174 {
1175 return dvWrap<T>(std::move(visitor), PathEls::Field(f), obj);
1176 }
1177
1178 DomItem() = default;
1179 DomItem(const std::shared_ptr<DomEnvironment> &);
1180 DomItem(const std::shared_ptr<DomUniverse> &);
1181
1182 // TODO move to DomEnvironment?
1183 static DomItem fromCode(const QString &code, DomType fileType = DomType::QmlFile);
1184
1185 // --- start of potentially dangerous stuff, make private? ---
1186
1187 std::shared_ptr<DomTop> topPtr() const;
1188 std::shared_ptr<OwningItem> owningItemPtr() const;
1189
1190 // keep the DomItem around to ensure that it doesn't get deleted
1191 template<typename T, typename std::enable_if<std::is_base_of_v<DomBase, T>, bool>::type = true>
1192 T const *as() const
1193 {
1194 if (m_kind == T::kindValue) {
1195 if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
1196 return std::get<SimpleObjectWrap>(v: m_element)->as<T>();
1197 else
1198 return static_cast<T const *>(base());
1199 }
1200 return nullptr;
1201 }
1202
1203 template<typename T, typename std::enable_if<!std::is_base_of_v<DomBase, T>, bool>::type = true>
1204 T const *as() const
1205 {
1206 if (m_kind == T::kindValue) {
1207 Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind));
1208 return std::get<SimpleObjectWrap>(v: m_element)->as<T>();
1209 }
1210 return nullptr;
1211 }
1212
1213 template<typename T>
1214 std::shared_ptr<T> ownerAs() const;
1215
1216 template<typename Owner, typename T>
1217 DomItem copy(const Owner &owner, const Path &ownerPath, const T &base) const
1218 {
1219 Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
1220 static_assert(IsInlineDom<std::decay_t<T>>::value, "Expected an inline item or pointer");
1221 return DomItem(m_top, owner, ownerPath, base);
1222 }
1223
1224 template<typename Owner>
1225 DomItem copy(const Owner &owner, const Path &ownerPath) const
1226 {
1227 Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
1228 return DomItem(m_top, owner, ownerPath, owner.get());
1229 }
1230
1231 template<typename T>
1232 DomItem copy(const T &base) const
1233 {
1234 Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
1235 using BaseT = std::decay_t<T>;
1236 static_assert(!std::is_same_v<BaseT, ElementT>,
1237 "variant not supported, pass in the stored types");
1238 static_assert(IsInlineDom<BaseT>::value || std::is_same_v<BaseT, std::monostate>,
1239 "expected either a pointer or an inline item");
1240
1241 if constexpr (IsSharedPointerToDomObject<BaseT>::value)
1242 return DomItem(m_top, base, Path(), base.get());
1243 else if constexpr (IsInlineDom<BaseT>::value)
1244 return DomItem(m_top, m_owner, m_ownerPath, base);
1245
1246 Q_UNREACHABLE_RETURN(DomItem(m_top, m_owner, m_ownerPath, nullptr));
1247 }
1248
1249private:
1250 enum class WriteOutCheckResult { Success, Failed };
1251 WriteOutCheckResult performWriteOutChecks(const DomItem &, OutWriter &, WriteOutChecks) const;
1252 const DomBase *base() const;
1253
1254 template<typename Env, typename Owner>
1255 DomItem(Env, Owner, Path, std::nullptr_t) : DomItem()
1256 {
1257 }
1258
1259 template<typename Env, typename Owner, typename T,
1260 typename = std::enable_if_t<IsInlineDom<std::decay_t<T>>::value>>
1261 DomItem(Env env, Owner owner, const Path &ownerPath, const T &el)
1262 : m_top(env), m_owner(owner), m_ownerPath(ownerPath), m_element(el)
1263 {
1264 using BaseT = std::decay_t<T>;
1265 if constexpr (std::is_pointer_v<BaseT>) {
1266 if (!el || el->kind() == DomType::Empty) { // avoid null ptr, and allow only a
1267 // single kind of Empty
1268 m_kind = DomType::Empty;
1269 m_top = std::monostate();
1270 m_owner = std::monostate();
1271 m_ownerPath = Path();
1272 m_element = Empty();
1273 } else {
1274 using DomT = std::remove_pointer_t<BaseT>;
1275 m_element = el;
1276 m_kind = DomT::kindValue;
1277 }
1278 } else {
1279 static_assert(!std::is_same_v<BaseT, ElementT>,
1280 "variant not supported, pass in the internal type");
1281 m_kind = el->kind();
1282 }
1283 }
1284 friend class DomBase;
1285 friend class DomElement;
1286 friend class Map;
1287 friend class List;
1288 friend class QmlObject;
1289 friend class DomUniverse;
1290 friend class DomEnvironment;
1291 friend class ExternalItemInfoBase;
1292 friend class ConstantData;
1293 friend class MutableDomItem;
1294 friend class ScriptExpression;
1295 friend class AstComments;
1296 friend class FileLocations::Node;
1297 friend class TestDomItem;
1298 friend QMLDOM_EXPORT bool operator==(const DomItem &, const DomItem &);
1299 DomType m_kind = DomType::Empty;
1300 TopT m_top;
1301 OwnerT m_owner;
1302 Path m_ownerPath;
1303 ElementT m_element = Empty();
1304};
1305
1306QMLDOM_EXPORT bool operator==(const DomItem &o1, const DomItem &o2);
1307
1308inline bool operator!=(const DomItem &o1, const DomItem &o2)
1309{
1310 return !(o1 == o2);
1311}
1312
1313template<typename T>
1314static DomItem keyMultiMapHelper(const DomItem &self, const QString &key,
1315 const QMultiMap<QString, T> &mmap)
1316{
1317 auto it = mmap.find(key);
1318 auto end = mmap.cend();
1319 if (it == end)
1320 return DomItem();
1321 else {
1322 // special case single element (++it == end || it.key() != key)?
1323 QList<const T *> values;
1324 while (it != end && it.key() == key)
1325 values.append(&(*it++));
1326 ListP ll(self.pathFromOwner().withComponent(c: PathEls::Key(key)), values, QString(),
1327 ListOptions::Reverse);
1328 return self.copy(base: ll);
1329 }
1330}
1331
1332template<typename T>
1333Map Map::fromMultiMapRef(const Path &pathFromOwner, const QMultiMap<QString, T> &mmap)
1334{
1335 return Map(
1336 pathFromOwner,
1337 [&mmap](const DomItem &self, const QString &key) {
1338 return keyMultiMapHelper(self, key, mmap);
1339 },
1340 [&mmap](const DomItem &) { return QSet<QString>(mmap.keyBegin(), mmap.keyEnd()); },
1341 QLatin1String(typeid(T).name()));
1342}
1343
1344template<typename T>
1345Map Map::fromMapRef(
1346 const Path &pathFromOwner, const QMap<QString, T> &map,
1347 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper)
1348{
1349 return Map(
1350 pathFromOwner,
1351 [&map, elWrapper](const DomItem &self, const QString &key) {
1352 const auto it = map.constFind(key);
1353 if (it == map.constEnd())
1354 return DomItem();
1355 return elWrapper(self, PathEls::Key(key), it.value());
1356 },
1357 [&map](const DomItem &) { return QSet<QString>(map.keyBegin(), map.keyEnd()); },
1358 QLatin1String(typeid(T).name()));
1359}
1360
1361template<typename MapT>
1362QSet<QString> Map::fileRegionKeysFromMap(const MapT &map)
1363{
1364 QSet<QString> keys;
1365 std::transform(map.keyBegin(), map.keyEnd(), std::inserter(x&: keys, i: keys.begin()), fileLocationRegionName);
1366 return keys;
1367}
1368
1369template<typename T>
1370Map Map::fromFileRegionMap(const Path &pathFromOwner, const QMap<FileLocationRegion, T> &map)
1371{
1372 auto result = Map(
1373 pathFromOwner,
1374 [&map](const DomItem &mapItem, const QString &key) -> DomItem {
1375 auto it = map.constFind(fileLocationRegionValue(region: key));
1376 if (it == map.constEnd())
1377 return {};
1378
1379 return mapItem.wrap(PathEls::Key(key), *it);
1380 },
1381 [&map](const DomItem &) { return fileRegionKeysFromMap(map); },
1382 QString::fromLatin1(ba: typeid(T).name()));
1383 return result;
1384}
1385
1386template<typename T>
1387Map Map::fromFileRegionListMap(const Path &pathFromOwner,
1388 const QMap<FileLocationRegion, QList<T>> &map)
1389{
1390 using namespace Qt::StringLiterals;
1391 auto result = Map(
1392 pathFromOwner,
1393 [&map](const DomItem &mapItem, const QString &key) -> DomItem {
1394 const QList<SourceLocation> locations = map.value(fileLocationRegionValue(region: key));
1395 if (locations.empty())
1396 return {};
1397
1398 auto list = List::fromQList<SourceLocation>(
1399 mapItem.pathFromOwner(), locations,
1400 [](const DomItem &self, const PathEls::PathComponent &path,
1401 const SourceLocation &location) {
1402 return self.subLocationItem(c: path, loc: location);
1403 });
1404 return mapItem.subListItem(list);
1405 },
1406 [&map](const DomItem &) { return fileRegionKeysFromMap(map); },
1407 u"QList<%1>"_s.arg(a: QString::fromLatin1(ba: typeid(T).name())));
1408 return result;
1409}
1410
1411template<typename T>
1412List List::fromQList(
1413 const Path &pathFromOwner, const QList<T> &list,
1414 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
1415 ListOptions options)
1416{
1417 index_type len = list.size();
1418 if (options == ListOptions::Reverse) {
1419 return List(
1420 pathFromOwner,
1421 [list, elWrapper](const DomItem &self, index_type i) mutable {
1422 if (i < 0 || i >= list.size())
1423 return DomItem();
1424 return elWrapper(self, PathEls::Index(i), list[list.size() - i - 1]);
1425 },
1426 [len](const DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
1427 } else {
1428 return List(
1429 pathFromOwner,
1430 [list, elWrapper](const DomItem &self, index_type i) mutable {
1431 if (i < 0 || i >= list.size())
1432 return DomItem();
1433 return elWrapper(self, PathEls::Index(i), list[i]);
1434 },
1435 [len](const DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
1436 }
1437}
1438
1439template<typename T>
1440List List::fromQListRef(
1441 const Path &pathFromOwner, const QList<T> &list,
1442 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
1443 ListOptions options)
1444{
1445 if (options == ListOptions::Reverse) {
1446 return List(
1447 pathFromOwner,
1448 [&list, elWrapper](const DomItem &self, index_type i) {
1449 if (i < 0 || i >= list.size())
1450 return DomItem();
1451 return elWrapper(self, PathEls::Index(i), list[list.size() - i - 1]);
1452 },
1453 [&list](const DomItem &) { return list.size(); }, nullptr,
1454 QLatin1String(typeid(T).name()));
1455 } else {
1456 return List(
1457 pathFromOwner,
1458 [&list, elWrapper](const DomItem &self, index_type i) {
1459 if (i < 0 || i >= list.size())
1460 return DomItem();
1461 return elWrapper(self, PathEls::Index(i), list[i]);
1462 },
1463 [&list](const DomItem &) { return list.size(); }, nullptr,
1464 QLatin1String(typeid(T).name()));
1465 }
1466}
1467
1468class QMLDOM_EXPORT OwningItem: public DomBase {
1469protected:
1470 virtual std::shared_ptr<OwningItem> doCopy(const DomItem &self) const = 0;
1471
1472public:
1473 OwningItem(const OwningItem &o);
1474 OwningItem(int derivedFrom=0);
1475 OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt);
1476 OwningItem(const OwningItem &&) = delete;
1477 OwningItem &operator=(const OwningItem &&) = delete;
1478 static int nextRevision();
1479
1480 Path canonicalPath(const DomItem &self) const override = 0;
1481
1482 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
1483 std::shared_ptr<OwningItem> makeCopy(const DomItem &self) const { return doCopy(self); }
1484 Path pathFromOwner() const { return Path(); }
1485 Path pathFromOwner(const DomItem &) const override final { return Path(); }
1486 DomItem containingObject(const DomItem &self) const override;
1487 int derivedFrom() const;
1488 virtual int revision() const;
1489
1490 QDateTime createdAt() const;
1491 virtual QDateTime lastDataUpdateAt() const;
1492 virtual void refreshedDataAt(QDateTime tNew);
1493
1494 // explicit freeze handling needed?
1495 virtual bool frozen() const;
1496 virtual bool freeze();
1497 QDateTime frozenAt() const;
1498
1499 virtual void addError(const DomItem &self, ErrorMessage &&msg);
1500 void addErrorLocal(ErrorMessage &&msg);
1501 void clearErrors(const ErrorGroups &groups = ErrorGroups({}));
1502 // return false if a quick exit was requested
1503 bool iterateErrors(
1504 const DomItem &self,
1505 function_ref<bool(const DomItem &source, const ErrorMessage &msg)> visitor,
1506 const Path &inPath = Path());
1507 QMultiMap<Path, ErrorMessage> localErrors() const {
1508 QMutexLocker l(mutex());
1509 return m_errors;
1510 }
1511
1512 virtual bool iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor);
1513
1514 QBasicMutex *mutex() const { return &m_mutex; }
1515private:
1516 mutable QBasicMutex m_mutex;
1517 int m_derivedFrom;
1518 int m_revision;
1519 QDateTime m_createdAt;
1520 QDateTime m_lastDataUpdateAt;
1521 QDateTime m_frozenAt;
1522 QMultiMap<Path, ErrorMessage> m_errors;
1523 QMap<ErrorMessage, quint32> m_errorsCounts;
1524};
1525
1526template<typename T>
1527std::shared_ptr<T> DomItem::ownerAs() const
1528{
1529 if constexpr (domTypeIsOwningItem(T::kindValue)) {
1530 if (!std::holds_alternative<std::monostate>(v: m_owner)) {
1531 if constexpr (T::kindValue == DomType::FileLocationsNode) {
1532 if (std::holds_alternative<std::shared_ptr<FileLocations::Node>>(v: m_owner))
1533 return std::static_pointer_cast<T>(
1534 std::get<std::shared_ptr<FileLocations::Node>>(v: m_owner));
1535 } else if constexpr (T::kindValue == DomType::ExternalItemInfo) {
1536 if (std::holds_alternative<std::shared_ptr<ExternalItemInfoBase>>(v: m_owner))
1537 return std::static_pointer_cast<T>(
1538 std::get<std::shared_ptr<ExternalItemInfoBase>>(v: m_owner));
1539 } else if constexpr (T::kindValue == DomType::ExternalItemPair) {
1540 if (std::holds_alternative<std::shared_ptr<ExternalItemPairBase>>(v: m_owner))
1541 return std::static_pointer_cast<T>(
1542 std::get<std::shared_ptr<ExternalItemPairBase>>(v: m_owner));
1543 } else {
1544 if (std::holds_alternative<std::shared_ptr<T>>(m_owner)) {
1545 return std::get<std::shared_ptr<T>>(m_owner);
1546 }
1547 }
1548 }
1549 } else {
1550 Q_ASSERT_X(false, "DomItem::ownerAs", "unexpected non owning value in ownerAs");
1551 }
1552 return std::shared_ptr<T> {};
1553}
1554
1555template<int I>
1556struct rank : rank<I - 1>
1557{
1558 static_assert(I > 0, "");
1559};
1560template<>
1561struct rank<0>
1562{
1563};
1564
1565template<typename T>
1566auto writeOutWrap(const T &t, const DomItem &self, OutWriter &lw, rank<1>)
1567 -> decltype(t.writeOut(self, lw))
1568{
1569 t.writeOut(self, lw);
1570}
1571
1572template<typename T>
1573auto writeOutWrap(const T &, const DomItem &, OutWriter &, rank<0>) -> void
1574{
1575 qCWarning(writeOutLog) << "Ignoring writeout to wrapped object not supporting it ("
1576 << typeid(T).name();
1577}
1578template<typename T>
1579auto writeOutWrap(const T &t, const DomItem &self, OutWriter &lw) -> void
1580{
1581 writeOutWrap(t, self, lw, rank<1>());
1582}
1583
1584template<typename T>
1585void SimpleObjectWrapT<T>::writeOut(const DomItem &self, OutWriter &lw) const
1586{
1587 writeOutWrap<T>(*asT(), self, lw);
1588}
1589
1590QMLDOM_EXPORT QDebug operator<<(QDebug debug, const DomItem &c);
1591
1592class QMLDOM_EXPORT MutableDomItem {
1593public:
1594 using CopyOption = DomItem::CopyOption;
1595
1596 explicit operator bool() const
1597 {
1598 return bool(m_owner);
1599 } // this is weaker than item(), but normally correct
1600 DomType internalKind() { return item().internalKind(); }
1601 QString internalKindStr() { return domTypeToString(k: internalKind()); }
1602 DomKind domKind() { return kind2domKind(k: internalKind()); }
1603
1604 Path canonicalPath() const { return m_owner.canonicalPath().withPath(toAdd: m_pathFromOwner); }
1605 MutableDomItem containingObject()
1606 {
1607 if (m_pathFromOwner)
1608 return MutableDomItem(m_owner, m_pathFromOwner.split().pathToSource);
1609 else {
1610 DomItem cObj = m_owner.containingObject();
1611 return MutableDomItem(cObj.owner(), (domTypeIsOwningItem(cObj.internalKind()) ? Path() :cObj.pathFromOwner()));
1612 }
1613 }
1614
1615 MutableDomItem container()
1616 {
1617 if (m_pathFromOwner)
1618 return MutableDomItem(m_owner, m_pathFromOwner.dropTail());
1619 else {
1620 return MutableDomItem(item().container());
1621 }
1622 }
1623
1624 MutableDomItem qmlObject(GoTo option = GoTo::Strict,
1625 FilterUpOptions fOptions = FilterUpOptions::ReturnOuter)
1626 {
1627 return MutableDomItem(item().qmlObject(option, options: fOptions));
1628 }
1629 MutableDomItem fileObject(GoTo option = GoTo::Strict)
1630 {
1631 return MutableDomItem(item().fileObject(option));
1632 }
1633 MutableDomItem rootQmlObject(GoTo option = GoTo::Strict)
1634 {
1635 return MutableDomItem(item().rootQmlObject(option));
1636 }
1637 MutableDomItem globalScope() { return MutableDomItem(item().globalScope()); }
1638 MutableDomItem scope() { return MutableDomItem(item().scope()); }
1639
1640 MutableDomItem component(GoTo option = GoTo::Strict)
1641 {
1642 return MutableDomItem { item().component(option) };
1643 }
1644 MutableDomItem owner() { return MutableDomItem(m_owner); }
1645 MutableDomItem top() { return MutableDomItem(item().top()); }
1646 MutableDomItem environment() { return MutableDomItem(item().environment()); }
1647 MutableDomItem universe() { return MutableDomItem(item().universe()); }
1648 Path pathFromOwner() { return m_pathFromOwner; }
1649 MutableDomItem operator[](const Path &path) { return MutableDomItem(item()[path]); }
1650 MutableDomItem operator[](QStringView component) { return MutableDomItem(item()[component]); }
1651 MutableDomItem operator[](const QString &component)
1652 {
1653 return MutableDomItem(item()[component]);
1654 }
1655 MutableDomItem operator[](const char16_t *component)
1656 {
1657 // to avoid clash with stupid builtin ptrdiff_t[MutableDomItem&], coming from C
1658 return MutableDomItem(item()[QStringView(component)]);
1659 }
1660 MutableDomItem operator[](index_type i) { return MutableDomItem(item().index(i)); }
1661
1662 MutableDomItem path(const Path &p) { return MutableDomItem(item().path(p)); }
1663 MutableDomItem path(const QString &p) { return path(p: Path::fromString(s: p)); }
1664 MutableDomItem path(QStringView p) { return path(p: Path::fromString(s: p)); }
1665
1666 QList<QString> const fields() { return item().fields(); }
1667 MutableDomItem field(QStringView name) { return MutableDomItem(item().field(name)); }
1668 index_type indexes() { return item().indexes(); }
1669 MutableDomItem index(index_type i) { return MutableDomItem(item().index(i)); }
1670
1671 QSet<QString> const keys() { return item().keys(); }
1672 MutableDomItem key(const QString &name) { return MutableDomItem(item().key(name)); }
1673 MutableDomItem key(QStringView name) { return key(name: name.toString()); }
1674
1675 void
1676 dump(const Sink &s, int indent = 0,
1677 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter)
1678 {
1679 item().dump(s, indent, filter);
1680 }
1681 FileWriter::Status
1682 dump(const QString &path,
1683 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter,
1684 int nBackups = 2, int indent = 0, FileWriter *fw = nullptr)
1685 {
1686 return item().dump(path, filter, nBackups, indent, fw);
1687 }
1688 void writeOut(OutWriter &lw) { return item().writeOut(lw); }
1689 bool writeOut(const QString &path, int nBackups = 2,
1690 const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr)
1691 {
1692 return item().writeOut(path, nBackups, opt, fw);
1693 }
1694
1695 MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected)
1696 {
1697 return item().makeCopy(option);
1698 }
1699 bool commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr = nullptr)
1700 {
1701 return item().commitToBase(validPtr: validEnvPtr);
1702 }
1703 QString canonicalFilePath() const { return item().canonicalFilePath(); }
1704
1705 MutableDomItem refreshed() { return MutableDomItem(item().refreshed()); }
1706
1707 QCborValue value() { return item().value(); }
1708
1709 QString toString() { return item().toString(); }
1710
1711 // convenience getters
1712 QString name() { return item().name(); }
1713 MutableDomItem pragmas() { return item().pragmas(); }
1714 MutableDomItem ids() { return MutableDomItem::item().ids(); }
1715 QString idStr() { return item().idStr(); }
1716 MutableDomItem propertyDefs() { return MutableDomItem(item().propertyDefs()); }
1717 MutableDomItem bindings() { return MutableDomItem(item().bindings()); }
1718 MutableDomItem methods() { return MutableDomItem(item().methods()); }
1719 MutableDomItem children() { return MutableDomItem(item().children()); }
1720 MutableDomItem child(index_type i) { return MutableDomItem(item().child(i)); }
1721 MutableDomItem annotations() { return MutableDomItem(item().annotations()); }
1722
1723 // // OwnigItem elements
1724 int derivedFrom() { return m_owner.derivedFrom(); }
1725 int revision() { return m_owner.revision(); }
1726 QDateTime createdAt() { return m_owner.createdAt(); }
1727 QDateTime frozenAt() { return m_owner.frozenAt(); }
1728 QDateTime lastDataUpdateAt() { return m_owner.lastDataUpdateAt(); }
1729
1730 void addError(ErrorMessage &&msg) { item().addError(msg: std::move(msg)); }
1731 ErrorHandler errorHandler();
1732
1733 // convenience setters
1734 MutableDomItem addPrototypePath(const Path &prototypePath);
1735 MutableDomItem setNextScopePath(const Path &nextScopePath);
1736 MutableDomItem setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs);
1737 MutableDomItem setBindings(QMultiMap<QString, Binding> bindings);
1738 MutableDomItem setMethods(QMultiMap<QString, MethodInfo> functionDefs);
1739 MutableDomItem setChildren(const QList<QmlObject> &children);
1740 MutableDomItem setAnnotations(const QList<QmlObject> &annotations);
1741 MutableDomItem setScript(const std::shared_ptr<ScriptExpression> &exp);
1742 MutableDomItem setCode(const QString &code);
1743 MutableDomItem addPropertyDef(const PropertyDefinition &propertyDef,
1744 AddOption option = AddOption::Overwrite);
1745 MutableDomItem addBinding(Binding binding, AddOption option = AddOption::Overwrite);
1746 MutableDomItem addMethod(
1747 const MethodInfo &functionDef, AddOption option = AddOption::Overwrite);
1748 MutableDomItem addChild(QmlObject child);
1749 MutableDomItem addAnnotation(QmlObject child);
1750 MutableDomItem addPreComment(const Comment &comment, FileLocationRegion region);
1751 MutableDomItem addPostComment(const Comment &comment, FileLocationRegion region);
1752 QQmlJSScope::ConstPtr semanticScope();
1753 void setSemanticScope(const QQmlJSScope::ConstPtr &scope);
1754
1755 MutableDomItem() = default;
1756 MutableDomItem(const DomItem &owner, const Path &pathFromOwner):
1757 m_owner(owner), m_pathFromOwner(pathFromOwner)
1758 {}
1759 MutableDomItem(const DomItem &item):
1760 m_owner(item.owner()), m_pathFromOwner(item.pathFromOwner())
1761 {}
1762
1763 std::shared_ptr<DomTop> topPtr() { return m_owner.topPtr(); }
1764 std::shared_ptr<OwningItem> owningItemPtr() { return m_owner.owningItemPtr(); }
1765
1766 template<typename T>
1767 T const *as()
1768 {
1769 return item().as<T>();
1770 }
1771
1772 template <typename T>
1773 T *mutableAs() {
1774 Q_ASSERT(!m_owner || !m_owner.owningItemPtr()->frozen());
1775
1776 DomItem self = item();
1777 if (self.m_kind != T::kindValue)
1778 return nullptr;
1779
1780 const T *t = nullptr;
1781 if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
1782 t = static_cast<const SimpleObjectWrapBase *>(self.base())->as<T>();
1783 else if constexpr (std::is_base_of<DomBase, T>::value)
1784 t = static_cast<const T *>(self.base());
1785 else
1786 Q_UNREACHABLE_RETURN(nullptr);
1787
1788 // Nasty. But since ElementT has to store the const pointers, we allow it in this one place.
1789 return const_cast<T *>(t);
1790 }
1791
1792 template<typename T>
1793 std::shared_ptr<T> ownerAs() const
1794 {
1795 return m_owner.ownerAs<T>();
1796 }
1797 // it is dangerous to assume it stays valid when updates are preformed...
1798 DomItem item() const { return m_owner.path(p: m_pathFromOwner); }
1799
1800 friend bool operator==(const MutableDomItem &o1, const MutableDomItem &o2)
1801 {
1802 return o1.m_owner == o2.m_owner && o1.m_pathFromOwner == o2.m_pathFromOwner;
1803 }
1804 friend bool operator!=(const MutableDomItem &o1, const MutableDomItem &o2)
1805 {
1806 return !(o1 == o2);
1807 }
1808
1809private:
1810 DomItem m_owner;
1811 Path m_pathFromOwner;
1812};
1813
1814QMLDOM_EXPORT QDebug operator<<(QDebug debug, const MutableDomItem &c);
1815
1816template<typename K, typename T>
1817Path insertUpdatableElementInMultiMap(const Path &mapPathFromOwner, QMultiMap<K, T> &mmap, K key,
1818 const T &value, AddOption option = AddOption::KeepExisting,
1819 T **valuePtr = nullptr)
1820{
1821 if (option == AddOption::Overwrite) {
1822 auto it = mmap.find(key);
1823 if (it != mmap.end()) {
1824 T &v = *it;
1825 v = value;
1826 if (++it != mmap.end() && it.key() == key) {
1827 qWarning() << " requested overwrite of " << key
1828 << " that contains aleready multiple entries in" << mapPathFromOwner;
1829 }
1830 Path newPath = mapPathFromOwner.withKey(key).withIndex(0);
1831 v.updatePathFromOwner(newPath);
1832 if (valuePtr)
1833 *valuePtr = &v;
1834 return newPath;
1835 }
1836 }
1837 mmap.insert(key, value);
1838 auto it = mmap.find(key);
1839 auto it2 = it;
1840 int nVal = 0;
1841 while (it2 != mmap.end() && it2.key() == key) {
1842 ++nVal;
1843 ++it2;
1844 }
1845 Path newPath = mapPathFromOwner.withKey(key).withIndex(nVal-1);
1846 T &v = *it;
1847 v.updatePathFromOwner(newPath);
1848 if (valuePtr)
1849 *valuePtr = &v;
1850 return newPath;
1851}
1852
1853template<typename T>
1854Path appendUpdatableElementInQList(const Path &listPathFromOwner, QList<T> &list, const T &value,
1855 T **vPtr = nullptr)
1856{
1857 int idx = list.size();
1858 list.append(value);
1859 Path newPath = listPathFromOwner.withIndex(i: idx);
1860 T &targetV = list[idx];
1861 targetV.updatePathFromOwner(newPath);
1862 if (vPtr)
1863 *vPtr = &targetV;
1864 return newPath;
1865}
1866
1867template <typename T, typename K = QString>
1868void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, const Path &newPath)
1869{
1870 auto it = mmap.begin();
1871 auto end = mmap.end();
1872 index_type i = 0;
1873 K name;
1874 QList<T*> els;
1875 while (it != end) {
1876 if (i > 0 && name != it.key()) {
1877 Path pName = newPath.withKey(name: QString(name));
1878 for (T *el : els)
1879 el->updatePathFromOwner(pName.withIndex(i: --i));
1880 els.clear();
1881 els.append(&(*it));
1882 name = it.key();
1883 i = 1;
1884 } else {
1885 els.append(&(*it));
1886 name = it.key();
1887 ++i;
1888 }
1889 ++it;
1890 }
1891 Path pName = newPath.withKey(name);
1892 for (T *el : els)
1893 el->updatePathFromOwner(pName.withIndex(i: --i));
1894}
1895
1896template <typename T>
1897void updatePathFromOwnerQList(QList<T> &list, const Path &newPath)
1898{
1899 auto it = list.begin();
1900 auto end = list.end();
1901 index_type i = 0;
1902 while (it != end)
1903 (it++)->updatePathFromOwner(newPath.withIndex(i: i++));
1904}
1905
1906constexpr bool domTypeIsObjWrap(DomType k)
1907{
1908 switch (k) {
1909 case DomType::Binding:
1910 case DomType::EnumItem:
1911 case DomType::ErrorMessage:
1912 case DomType::Export:
1913 case DomType::Id:
1914 case DomType::Import:
1915 case DomType::ImportScope:
1916 case DomType::MethodInfo:
1917 case DomType::MethodParameter:
1918 case DomType::ModuleAutoExport:
1919 case DomType::Pragma:
1920 case DomType::PropertyDefinition:
1921 case DomType::Version:
1922 case DomType::Comment:
1923 case DomType::CommentedElement:
1924 case DomType::RegionComments:
1925 case DomType::FileLocationsInfo:
1926 return true;
1927 default:
1928 return false;
1929 }
1930}
1931
1932constexpr bool domTypeIsValueWrap(DomType k)
1933{
1934 switch (k) {
1935 case DomType::PropertyInfo:
1936 return true;
1937 default:
1938 return false;
1939 }
1940}
1941
1942constexpr bool domTypeIsDomElement(DomType k)
1943{
1944 switch (k) {
1945 case DomType::ModuleScope:
1946 case DomType::QmlObject:
1947 case DomType::ConstantData:
1948 case DomType::SimpleObjectWrap:
1949 case DomType::Reference:
1950 case DomType::Map:
1951 case DomType::List:
1952 case DomType::ListP:
1953 case DomType::EnumDecl:
1954 case DomType::JsResource:
1955 case DomType::QmltypesComponent:
1956 case DomType::QmlComponent:
1957 case DomType::GlobalComponent:
1958 case DomType::MockObject:
1959 return true;
1960 default:
1961 return false;
1962 }
1963}
1964
1965constexpr bool domTypeIsOwningItem(DomType k)
1966{
1967 switch (k) {
1968 case DomType::ModuleIndex:
1969
1970 case DomType::MockOwner:
1971
1972 case DomType::ExternalItemInfo:
1973 case DomType::ExternalItemPair:
1974
1975 case DomType::QmlDirectory:
1976 case DomType::QmldirFile:
1977 case DomType::JsFile:
1978 case DomType::QmlFile:
1979 case DomType::QmltypesFile:
1980 case DomType::GlobalScope:
1981
1982 case DomType::ScriptExpression:
1983 case DomType::AstComments:
1984
1985 case DomType::LoadInfo:
1986 case DomType::FileLocationsNode:
1987
1988 case DomType::DomEnvironment:
1989 case DomType::DomUniverse:
1990 return true;
1991 default:
1992 return false;
1993 }
1994}
1995
1996constexpr bool domTypeIsUnattachedOwningItem(DomType k)
1997{
1998 switch (k) {
1999 case DomType::ScriptExpression:
2000 case DomType::AstComments:
2001 case DomType::FileLocationsNode:
2002 return true;
2003 default:
2004 return false;
2005 }
2006}
2007
2008constexpr bool domTypeIsScriptElement(DomType k)
2009{
2010 return DomType::ScriptElementStart <= k && k <= DomType::ScriptElementStop;
2011}
2012
2013template<typename T>
2014DomItem DomItem::subValueItem(const PathEls::PathComponent &c, const T &value,
2015 ConstantData::Options options) const
2016{
2017 using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
2018 if constexpr (
2019 std::is_base_of_v<
2020 QCborValue,
2021 BaseT> || std::is_base_of_v<QCborArray, BaseT> || std::is_base_of_v<QCborMap, BaseT>) {
2022 return DomItem(m_top, m_owner, m_ownerPath,
2023 ConstantData(pathFromOwner().withComponent(c), value, options));
2024 } else if constexpr (std::is_same_v<DomItem, BaseT>) {
2025 Q_UNUSED(options);
2026 return value;
2027 } else if constexpr (IsList<T>::value && !std::is_convertible_v<BaseT, QStringView>) {
2028 return subListItem(list: List::fromQList<typename BaseT::value_type>(
2029 pathFromOwner().withComponent(c), value,
2030 [options](const DomItem &list, const PathEls::PathComponent &p,
2031 const typename T::value_type &v) { return list.subValueItem(p, v, options); }));
2032 } else if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
2033 Q_UNUSED(options);
2034 return subOwnerItem(c, value);
2035 } else {
2036 return subDataItem(c, value, options);
2037 }
2038}
2039
2040template<typename T>
2041DomItem DomItem::subDataItem(const PathEls::PathComponent &c, const T &value,
2042 ConstantData::Options options) const
2043{
2044 using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
2045 if constexpr (std::is_same_v<BaseT, ConstantData>) {
2046 return this->copy(value);
2047 } else if constexpr (std::is_base_of_v<QCborValue, BaseT>) {
2048 return DomItem(m_top, m_owner, m_ownerPath,
2049 ConstantData(pathFromOwner().withComponent(c), value, options));
2050 } else {
2051 return DomItem(
2052 m_top, m_owner, m_ownerPath,
2053 ConstantData(pathFromOwner().withComponent(c), QCborValue(value), options));
2054 }
2055}
2056
2057template<typename T>
2058bool DomItem::dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, const T &value,
2059 ConstantData::Options options) const
2060{
2061 auto lazyWrap = [this, &c, &value, options]() {
2062 return this->subValueItem<T>(c, value, options);
2063 };
2064 return visitor(c, lazyWrap);
2065}
2066
2067template<typename F>
2068bool DomItem::dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
2069 ConstantData::Options options) const
2070{
2071 auto lazyWrap = [this, &c, &valueF, options]() {
2072 return this->subValueItem<decltype(valueF())>(c, valueF(), options);
2073 };
2074 return visitor(c, lazyWrap);
2075}
2076
2077template<typename T>
2078DomItem DomItem::wrap(const PathEls::PathComponent &c, const T &obj) const
2079{
2080 using BaseT = std::decay_t<T>;
2081 if constexpr (std::is_same_v<QString, BaseT> || std::is_arithmetic_v<BaseT>) {
2082 return this->subDataItem(c, value: QCborValue(obj));
2083 } else if constexpr (std::is_same_v<SourceLocation, BaseT>) {
2084 return this->subLocationItem(c, loc: obj);
2085 } else if constexpr (std::is_same_v<BaseT, Reference>) {
2086 Q_ASSERT_X(false, "DomItem::wrap",
2087 "wrapping a reference object, probably an error (wrap the target path instead)");
2088 return this->copy(obj);
2089 } else if constexpr (std::is_same_v<BaseT, ConstantData>) {
2090 return this->subDataItem(c, obj);
2091 } else if constexpr (std::is_same_v<BaseT, Map>) {
2092 return this->subMapItem(map: obj);
2093 } else if constexpr (std::is_same_v<BaseT, List>) {
2094 return this->subListItem(list: obj);
2095 } else if constexpr (std::is_base_of_v<ListPBase, BaseT>) {
2096 return this->subListItem(list: obj);
2097 } else if constexpr (std::is_same_v<BaseT, SimpleObjectWrap>) {
2098 return this->subObjectWrapItem(obj);
2099 } else if constexpr (IsDomObject<BaseT>::value) {
2100 if constexpr (domTypeIsObjWrap(BaseT::kindValue) || domTypeIsValueWrap(BaseT::kindValue)) {
2101 return this->subObjectWrapItem(
2102 obj: SimpleObjectWrap::fromObjectRef(this->pathFromOwner().withComponent(c), obj));
2103 } else if constexpr (domTypeIsDomElement(BaseT::kindValue)) {
2104 return this->copy(&obj);
2105 } else {
2106 qCWarning(domLog) << "Unhandled object of type " << domTypeToString(BaseT::kindValue)
2107 << " in DomItem::wrap, not using a shared_ptr for an "
2108 << "OwningItem, or unexpected wrapped object?";
2109 return DomItem();
2110 }
2111 } else if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
2112 if constexpr (domTypeIsOwningItem(BaseT::element_type::kindValue)) {
2113 return this->subOwnerItem(c, obj);
2114 } else {
2115 Q_ASSERT_X(false, "DomItem::wrap", "shared_ptr with non owning item");
2116 return DomItem();
2117 }
2118 } else if constexpr (IsMultiMap<BaseT>::value) {
2119 if constexpr (std::is_same_v<typename BaseT::key_type, QString>) {
2120 return subMapItem(map: Map::fromMultiMapRef<typename BaseT::mapped_type>(
2121 pathFromOwner().withComponent(c), obj));
2122 } else {
2123 Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)");
2124 }
2125 } else if constexpr (IsMap<BaseT>::value) {
2126 if constexpr (std::is_same_v<typename BaseT::key_type, QString>) {
2127 return subMapItem(map: Map::fromMapRef<typename BaseT::mapped_type>(
2128 pathFromOwner().withComponent(c), obj,
2129 [](const DomItem &map, const PathEls::PathComponent &p,
2130 const typename BaseT::mapped_type &el) { return map.wrap(p, el); }));
2131 } else {
2132 Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)");
2133 }
2134 } else if constexpr (IsList<BaseT>::value) {
2135 if constexpr (IsDomObject<typename BaseT::value_type>::value) {
2136 return subListItem(list: List::fromQListRef<typename BaseT::value_type>(
2137 pathFromOwner().withComponent(c), obj,
2138 [](const DomItem &list, const PathEls::PathComponent &p,
2139 const typename BaseT::value_type &el) { return list.wrap(p, el); }));
2140 } else {
2141 Q_ASSERT_X(false, "DomItem::wrap", "Unsupported list type T");
2142 return DomItem();
2143 }
2144 } else {
2145 qCWarning(domLog) << "Cannot wrap " << typeid(BaseT).name();
2146 Q_ASSERT_X(false, "DomItem::wrap", "Do not know how to wrap type T");
2147 return DomItem();
2148 }
2149}
2150
2151template<typename T>
2152bool DomItem::dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj) const
2153{
2154 auto lazyWrap = [this, &c, &obj]() { return this->wrap<T>(c, obj); };
2155 return visitor(c, lazyWrap);
2156}
2157
2158template<typename T>
2159bool ListPT<T>::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const
2160{
2161 index_type len = index_type(m_pList.size());
2162 for (index_type i = 0; i < len; ++i) {
2163 if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, index: i); }))
2164 return false;
2165 }
2166 return true;
2167}
2168
2169template<typename T>
2170DomItem ListPT<T>::index(const DomItem &self, index_type index) const
2171{
2172 if (index >= 0 && index < m_pList.size())
2173 return self.wrap(PathEls::Index(index), *static_cast<const T *>(m_pList.value(index)));
2174 return DomItem();
2175}
2176
2177// allow inlining of DomBase
2178inline DomKind DomBase::domKind() const
2179{
2180 return kind2domKind(k: kind());
2181}
2182
2183inline bool DomBase::iterateDirectSubpathsConst(const DomItem &self, DirectVisitor visitor) const
2184{
2185 Q_ASSERT(self.base() == this);
2186 return self.iterateDirectSubpaths(v: std::move(visitor));
2187}
2188
2189inline DomItem DomBase::containingObject(const DomItem &self) const
2190{
2191 Path path = pathFromOwner(self);
2192 DomItem base = self.owner();
2193 if (!path) {
2194 path = canonicalPath(self);
2195 base = self;
2196 }
2197 Source source = path.split();
2198 return base.path(p: source.pathToSource);
2199}
2200
2201inline quintptr DomBase::id() const
2202{
2203 return quintptr(this);
2204}
2205
2206inline QString DomBase::typeName() const
2207{
2208 return domTypeToString(k: kind());
2209}
2210
2211inline QList<QString> DomBase::fields(const DomItem &self) const
2212{
2213 QList<QString> res;
2214 self.iterateDirectSubpaths(v: [&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
2215 if (c.kind() == Path::Kind::Field)
2216 res.append(t: c.name());
2217 return true;
2218 });
2219 return res;
2220}
2221
2222inline DomItem DomBase::field(const DomItem &self, QStringView name) const
2223{
2224 DomItem res;
2225 self.iterateDirectSubpaths(
2226 v: [&res, name](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
2227 if (c.kind() == Path::Kind::Field && c.checkName(s: name)) {
2228 res = obj();
2229 return false;
2230 }
2231 return true;
2232 });
2233 return res;
2234}
2235
2236inline index_type DomBase::indexes(const DomItem &self) const
2237{
2238 index_type res = 0;
2239 self.iterateDirectSubpaths(v: [&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
2240 if (c.kind() == Path::Kind::Index) {
2241 index_type i = c.index() + 1;
2242 if (res < i)
2243 res = i;
2244 }
2245 return true;
2246 });
2247 return res;
2248}
2249
2250inline DomItem DomBase::index(const DomItem &self, qint64 index) const
2251{
2252 DomItem res;
2253 self.iterateDirectSubpaths(
2254 v: [&res, index](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
2255 if (c.kind() == Path::Kind::Index && c.index() == index) {
2256 res = obj();
2257 return false;
2258 }
2259 return true;
2260 });
2261 return res;
2262}
2263
2264inline QSet<QString> const DomBase::keys(const DomItem &self) const
2265{
2266 QSet<QString> res;
2267 self.iterateDirectSubpaths(v: [&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
2268 if (c.kind() == Path::Kind::Key)
2269 res.insert(value: c.name());
2270 return true;
2271 });
2272 return res;
2273}
2274
2275inline DomItem DomBase::key(const DomItem &self, const QString &name) const
2276{
2277 DomItem res;
2278 self.iterateDirectSubpaths(
2279 v: [&res, name](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
2280 if (c.kind() == Path::Kind::Key && c.checkName(s: name)) {
2281 res = obj();
2282 return false;
2283 }
2284 return true;
2285 });
2286 return res;
2287}
2288
2289inline DomItem DomItem::subListItem(const List &list) const
2290{
2291 return DomItem(m_top, m_owner, m_ownerPath, list);
2292}
2293
2294inline DomItem DomItem::subMapItem(const Map &map) const
2295{
2296 return DomItem(m_top, m_owner, m_ownerPath, map);
2297}
2298
2299} // end namespace Dom
2300} // end namespace QQmlJS
2301
2302QT_END_NAMESPACE
2303#endif // QMLDOMITEM_H
2304

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