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

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