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
53Q_DECLARE_LOGGING_CATEGORY(writeOutLog);
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
677template<typename Info>
678class AttachedInfoT;
679class FileLocations;
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 createFileLocations(
699 const std::shared_ptr<AttachedInfoT<FileLocations>> &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<
797 ConstantData,
798 Empty,
799 List,
800 ListP,
801 Map,
802 Reference,
803 ScriptElementDomWrapper,
804 SimpleObjectWrap,
805 const AstComments *,
806 const AttachedInfo *,
807 const DomEnvironment *,
808 const DomUniverse *,
809 const EnumDecl *,
810 const ExternalItemInfoBase *,
811 const ExternalItemPairBase *,
812 const GlobalComponent *,
813 const GlobalScope *,
814 const JsFile *,
815 const JsResource *,
816 const LoadInfo *,
817 const MockObject *,
818 const MockOwner *,
819 const ModuleIndex *,
820 const ModuleScope *,
821 const QmlComponent *,
822 const QmlDirectory *,
823 const QmlFile *,
824 const QmlObject *,
825 const QmldirFile *,
826 const QmltypesComponent *,
827 const QmltypesFile *,
828 const ScriptExpression *
829 >;
830
831using TopT = std::variant<
832 std::monostate,
833 std::shared_ptr<DomEnvironment>,
834 std::shared_ptr<DomUniverse>>;
835
836using OwnerT = std::variant<
837 std::monostate,
838 std::shared_ptr<ModuleIndex>,
839 std::shared_ptr<MockOwner>,
840 std::shared_ptr<ExternalItemInfoBase>,
841 std::shared_ptr<ExternalItemPairBase>,
842 std::shared_ptr<QmlDirectory>,
843 std::shared_ptr<QmldirFile>,
844 std::shared_ptr<JsFile>,
845 std::shared_ptr<QmlFile>,
846 std::shared_ptr<QmltypesFile>,
847 std::shared_ptr<GlobalScope>,
848 std::shared_ptr<ScriptExpression>,
849 std::shared_ptr<AstComments>,
850 std::shared_ptr<LoadInfo>,
851 std::shared_ptr<AttachedInfo>,
852 std::shared_ptr<DomEnvironment>,
853 std::shared_ptr<DomUniverse>>;
854
855inline bool emptyChildrenVisitor(Path, const DomItem &, bool)
856{
857 return true;
858}
859
860class MutableDomItem;
861
862class FileToLoad
863{
864public:
865 struct InMemoryContents
866 {
867 QString data;
868 QDateTime date = QDateTime::currentDateTimeUtc();
869 };
870
871 FileToLoad(const std::weak_ptr<DomEnvironment> &environment, const QString &canonicalPath,
872 const QString &logicalPath, const std::optional<InMemoryContents> &content);
873 FileToLoad() = default;
874
875 static FileToLoad fromMemory(const std::weak_ptr<DomEnvironment> &environment,
876 const QString &path, const QString &data);
877 static FileToLoad fromFileSystem(const std::weak_ptr<DomEnvironment> &environment,
878 const QString &canonicalPath);
879
880 std::weak_ptr<DomEnvironment> environment() const { return m_environment; }
881 QString canonicalPath() const { return m_canonicalPath; }
882 QString logicalPath() const { return m_logicalPath; }
883 void setCanonicalPath(const QString &canonicalPath) { m_canonicalPath = canonicalPath; }
884 void setLogicalPath(const QString &logicalPath) { m_logicalPath = logicalPath; }
885 std::optional<InMemoryContents> content() const { return m_content; }
886
887private:
888 std::weak_ptr<DomEnvironment> m_environment;
889 QString m_canonicalPath;
890 QString m_logicalPath;
891 std::optional<InMemoryContents> m_content;
892};
893
894class QMLDOM_EXPORT DomItem {
895 Q_DECLARE_TR_FUNCTIONS(DomItem);
896public:
897 using Callback = function<void(const Path &, const DomItem &, const DomItem &)>;
898
899 using InternalKind = DomType;
900 using Visitor = function_ref<bool(const Path &, const DomItem &)>;
901 using ChildrenVisitor = function_ref<bool(const Path &, const DomItem &, bool)>;
902
903 static ErrorGroup domErrorGroup;
904 static ErrorGroups myErrors();
905 static ErrorGroups myResolveErrors();
906 static DomItem empty;
907
908 enum class CopyOption { EnvConnected, EnvDisconnected };
909
910 template<typename F>
911 auto visitEl(F f) const
912 {
913 return std::visit(f, this->m_element);
914 }
915
916 explicit operator bool() const { return m_kind != DomType::Empty; }
917 InternalKind internalKind() const {
918 return m_kind;
919 }
920 QString internalKindStr() const { return domTypeToString(k: internalKind()); }
921 DomKind domKind() const
922 {
923 if (m_kind == DomType::ConstantData)
924 return std::get<ConstantData>(v: m_element).domKind();
925 else
926 return kind2domKind(k: m_kind);
927 }
928
929 Path canonicalPath() const;
930
931 DomItem filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const;
932 DomItem containingObject() const;
933 DomItem container() const;
934 DomItem owner() const;
935 DomItem top() const;
936 DomItem environment() const;
937 DomItem universe() const;
938 DomItem containingFile() const;
939 DomItem containingScriptExpression() const;
940 DomItem goToFile(const QString &filePath) const;
941 DomItem goUp(int) const;
942 DomItem directParent() const;
943
944 DomItem qmlObject(GoTo option = GoTo::Strict,
945 FilterUpOptions options = FilterUpOptions::ReturnOuter) const;
946 DomItem fileObject(GoTo option = GoTo::Strict) const;
947 DomItem rootQmlObject(GoTo option = GoTo::Strict) const;
948 DomItem globalScope() const;
949 DomItem component(GoTo option = GoTo::Strict) const;
950 DomItem scope(FilterUpOptions options = FilterUpOptions::ReturnOuter) const;
951 QQmlJSScope::ConstPtr nearestSemanticScope() const;
952 QQmlJSScope::ConstPtr semanticScope() const;
953
954 // convenience getters
955 DomItem get(const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
956 QList<DomItem> getAll(const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
957 bool isOwningItem() const { return domTypeIsOwningItem(internalKind()); }
958 bool isExternalItem() const { return domTypeIsExternalItem(k: internalKind()); }
959 bool isTopItem() const { return domTypeIsTopItem(k: internalKind()); }
960 bool isContainer() const { return domTypeIsContainer(k: internalKind()); }
961 bool isScope() const { return domTypeIsScope(k: internalKind()); }
962 bool isCanonicalChild(const DomItem &child) const;
963 bool hasAnnotations() const;
964 QString name() const { return field(name: Fields::name).value().toString(); }
965 DomItem pragmas() const { return field(name: Fields::pragmas); }
966 DomItem ids() const { return field(name: Fields::ids); }
967 QString idStr() const { return field(name: Fields::idStr).value().toString(); }
968 DomItem propertyInfos() const { return field(name: Fields::propertyInfos); }
969 PropertyInfo propertyInfoWithName(const QString &name) const;
970 QSet<QString> propertyInfoNames() const;
971 DomItem propertyDefs() const { return field(name: Fields::propertyDefs); }
972 DomItem bindings() const { return field(name: Fields::bindings); }
973 DomItem methods() const { return field(name: Fields::methods); }
974 DomItem enumerations() const { return field(name: Fields::enumerations); }
975 DomItem children() const { return field(name: Fields::children); }
976 DomItem child(index_type i) const { return field(name: Fields::children).index(i); }
977 DomItem annotations() const
978 {
979 if (hasAnnotations())
980 return field(name: Fields::annotations);
981 else
982 return DomItem();
983 }
984
985 bool resolve(const Path &path, Visitor visitor, const ErrorHandler &errorHandler,
986 ResolveOptions options = ResolveOption::None, const Path &fullPath = Path(),
987 QList<Path> *visitedRefs = nullptr) const;
988
989 DomItem operator[](const Path &path) const;
990 DomItem operator[](QStringView component) const;
991 DomItem operator[](const QString &component) const;
992 DomItem operator[](const char16_t *component) const
993 {
994 return (*this)[QStringView(component)];
995 } // to avoid clash with stupid builtin ptrdiff_t[DomItem&], coming from C
996 DomItem operator[](index_type i) const { return index(i); }
997 DomItem operator[](int i) const { return index(i); }
998 index_type size() const { return indexes() + keys().size(); }
999 index_type length() const { return size(); }
1000
1001 DomItem path(const Path &p, const ErrorHandler &h = &defaultErrorHandler) const;
1002 DomItem path(const QString &p, const ErrorHandler &h = &defaultErrorHandler) const;
1003 DomItem path(QStringView p, const ErrorHandler &h = &defaultErrorHandler) const;
1004
1005 QList<QString> fields() const;
1006 DomItem field(QStringView name) const;
1007
1008 index_type indexes() const;
1009 DomItem index(index_type) const;
1010 bool visitIndexes(function_ref<bool(const DomItem &)> visitor) const;
1011
1012 QSet<QString> keys() const;
1013 QStringList sortedKeys() const;
1014 DomItem key(const QString &name) const;
1015 DomItem key(QStringView name) const { return key(name: name.toString()); }
1016 bool visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const;
1017
1018 QList<DomItem> values() const;
1019 void writeOutPre(OutWriter &lw) const;
1020 void writeOut(OutWriter &lw) const;
1021 void writeOutPost(OutWriter &lw) const;
1022 bool writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const;
1023 bool writeOut(const QString &path, int nBackups = 2,
1024 const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr,
1025 WriteOutChecks extraChecks = WriteOutCheck::Default) const;
1026
1027 bool visitTree(const Path &basePath, ChildrenVisitor visitor,
1028 VisitOptions options = VisitOption::Default,
1029 ChildrenVisitor openingVisitor = emptyChildrenVisitor,
1030 ChildrenVisitor closingVisitor = emptyChildrenVisitor,
1031 const FieldFilter &filter = FieldFilter::noFilter()) const;
1032 bool visitPrototypeChain(function_ref<bool(const DomItem &)> visitor,
1033 VisitPrototypesOptions options = VisitPrototypesOption::Normal,
1034 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1035 QList<Path> *visitedRefs = nullptr) const;
1036 bool visitDirectAccessibleScopes(function_ref<bool(const DomItem &)> visitor,
1037 VisitPrototypesOptions options = VisitPrototypesOption::Normal,
1038 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1039 QList<Path> *visitedRefs = nullptr) const;
1040 bool
1041 visitStaticTypePrototypeChains(function_ref<bool(const DomItem &)> visitor,
1042 VisitPrototypesOptions options = VisitPrototypesOption::Normal,
1043 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1044 QList<Path> *visitedRefs = nullptr) const;
1045
1046 bool visitUp(function_ref<bool(const DomItem &)> visitor) const;
1047 bool visitScopeChain(
1048 function_ref<bool(const DomItem &)> visitor, LookupOptions = LookupOption::Normal,
1049 const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
1050 QList<Path> *visitedRefs = nullptr) const;
1051 bool visitLocalSymbolsNamed(
1052 const QString &name, function_ref<bool(const DomItem &)> visitor) const;
1053 bool visitLookup1(
1054 const QString &symbolName, function_ref<bool(const DomItem &)> visitor,
1055 LookupOptions = LookupOption::Normal, const ErrorHandler &h = nullptr,
1056 QSet<quintptr> *visited = nullptr, QList<Path> *visitedRefs = nullptr) const;
1057 bool visitLookup(
1058 const QString &symbolName, function_ref<bool(const DomItem &)> visitor,
1059 LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal,
1060 const ErrorHandler &errorHandler = nullptr, QSet<quintptr> *visited = nullptr,
1061 QList<Path> *visitedRefs = nullptr) const;
1062 bool visitSubSymbolsNamed(
1063 const QString &name, function_ref<bool(const DomItem &)> visitor) const;
1064 DomItem proceedToScope(
1065 const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
1066 QList<DomItem> lookup(
1067 const QString &symbolName, LookupType type = LookupType::Symbol,
1068 LookupOptions = LookupOption::Normal, const ErrorHandler &errorHandler = nullptr) const;
1069 DomItem lookupFirst(
1070 const QString &symbolName, LookupType type = LookupType::Symbol,
1071 LookupOptions = LookupOption::Normal, const ErrorHandler &errorHandler = nullptr) const;
1072
1073 quintptr id() const;
1074 Path pathFromOwner() const;
1075 QString canonicalFilePath() const;
1076 DomItem fileLocationsTree() const;
1077 DomItem fileLocations() const;
1078 MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected) const;
1079 bool commitToBase(const std::shared_ptr<DomEnvironment> &validPtr = nullptr) const;
1080 DomItem refreshed() const { return top().path(p: canonicalPath()); }
1081 QCborValue value() const;
1082
1083 void dumpPtr(const Sink &sink) const;
1084 void dump(const Sink &, int indent = 0,
1085 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter =
1086 noFilter) const;
1087 FileWriter::Status
1088 dump(const QString &path,
1089 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter,
1090 int nBackups = 2, int indent = 0, FileWriter *fw = nullptr) const;
1091 QString toString() const;
1092
1093 // OwnigItem elements
1094 int derivedFrom() const;
1095 int revision() const;
1096 QDateTime createdAt() const;
1097 QDateTime frozenAt() const;
1098 QDateTime lastDataUpdateAt() const;
1099
1100 void addError(ErrorMessage &&msg) const;
1101 ErrorHandler errorHandler() const;
1102 void clearErrors(const ErrorGroups &groups = ErrorGroups({}), bool iterate = true) const;
1103 // return false if a quick exit was requested
1104 bool iterateErrors(
1105 function_ref<bool (const DomItem &, const ErrorMessage &)> visitor, bool iterate,
1106 Path inPath = Path()) const;
1107
1108 bool iterateSubOwners(function_ref<bool(const DomItem &owner)> visitor) const;
1109 bool iterateDirectSubpaths(DirectVisitor v) const;
1110
1111 template<typename T>
1112 DomItem subDataItem(const PathEls::PathComponent &c, const T &value,
1113 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1114 template<typename T>
1115 DomItem subDataItemField(QStringView f, const T &value,
1116 ConstantData::Options options = ConstantData::Options::MapIsMap) const
1117 {
1118 return subDataItem(PathEls::Field(f), value, options);
1119 }
1120 template<typename T>
1121 DomItem subValueItem(const PathEls::PathComponent &c, const T &value,
1122 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1123 template<typename T>
1124 bool dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, const T &value,
1125 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1126 template<typename T>
1127 bool dvValueField(DirectVisitor visitor, QStringView f, const T &value,
1128 ConstantData::Options options = ConstantData::Options::MapIsMap) const
1129 {
1130 return this->dvValue<T>(std::move(visitor), PathEls::Field(f), value, options);
1131 }
1132 template<typename F>
1133 bool dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
1134 ConstantData::Options options = ConstantData::Options::MapIsMap) const;
1135 template<typename F>
1136 bool dvValueLazyField(DirectVisitor visitor, QStringView f, F valueF,
1137 ConstantData::Options options = ConstantData::Options::MapIsMap) const
1138 {
1139 return this->dvValueLazy(std::move(visitor), PathEls::Field(f), valueF, options);
1140 }
1141 DomItem subLocationItem(const PathEls::PathComponent &c, SourceLocation loc) const
1142 {
1143 return this->subDataItem(c, value: sourceLocationToQCborValue(loc));
1144 }
1145 // bool dvSubReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path
1146 // referencedObject);
1147 DomItem subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const;
1148 DomItem subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const;
1149 bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, const Path &referencedObject) const
1150 {
1151 return dvItem(visitor: std::move(visitor), c, it: [c, this, referencedObject]() {
1152 return this->subReferenceItem(c, referencedObject);
1153 });
1154 }
1155 bool dvReferences(
1156 DirectVisitor visitor, const PathEls::PathComponent &c, const QList<Path> &paths) const
1157 {
1158 return dvItem(visitor: std::move(visitor), c, it: [c, this, paths]() {
1159 return this->subReferencesItem(c, paths);
1160 });
1161 }
1162 bool dvReferenceField(DirectVisitor visitor, QStringView f, const Path &referencedObject) const
1163 {
1164 return dvReference(visitor: std::move(visitor), c: PathEls::Field(f), referencedObject);
1165 }
1166 bool dvReferencesField(DirectVisitor visitor, QStringView f, const QList<Path> &paths) const
1167 {
1168 return dvReferences(visitor: std::move(visitor), c: PathEls::Field(f), paths);
1169 }
1170 bool dvItem(DirectVisitor visitor, const PathEls::PathComponent &c, function_ref<DomItem()> it) const
1171 {
1172 return visitor(c, it);
1173 }
1174 bool dvItemField(DirectVisitor visitor, QStringView f, function_ref<DomItem()> it) const
1175 {
1176 return dvItem(visitor: std::move(visitor), c: PathEls::Field(f), it);
1177 }
1178 DomItem subListItem(const List &list) const;
1179 DomItem subMapItem(const Map &map) const;
1180 DomItem subObjectWrapItem(SimpleObjectWrap obj) const
1181 {
1182 return DomItem(m_top, m_owner, m_ownerPath, obj);
1183 }
1184
1185 DomItem subScriptElementWrapperItem(const ScriptElementVariant &obj) const
1186 {
1187 Q_ASSERT(obj);
1188 return DomItem(m_top, m_owner, m_ownerPath, ScriptElementDomWrapper(obj));
1189 }
1190
1191 template<typename Owner>
1192 DomItem subOwnerItem(const PathEls::PathComponent &c, Owner o) const
1193 {
1194 if constexpr (domTypeIsUnattachedOwningItem(Owner::element_type::kindValue))
1195 return DomItem(m_top, o, canonicalPath().appendComponent(c), o.get());
1196 else
1197 return DomItem(m_top, o, Path(), o.get());
1198 }
1199 template<typename T>
1200 DomItem wrap(const PathEls::PathComponent &c, const T &obj) const;
1201 template<typename T>
1202 DomItem wrapField(QStringView f, const T &obj) const
1203 {
1204 return wrap<T>(PathEls::Field(f), obj);
1205 }
1206 template<typename T>
1207 bool dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj) const;
1208 template<typename T>
1209 bool dvWrapField(DirectVisitor visitor, QStringView f, T &obj) const
1210 {
1211 return dvWrap<T>(std::move(visitor), PathEls::Field(f), obj);
1212 }
1213
1214 DomItem() = default;
1215 DomItem(const std::shared_ptr<DomEnvironment> &);
1216 DomItem(const std::shared_ptr<DomUniverse> &);
1217
1218 // TODO move to DomEnvironment?
1219 static DomItem fromCode(const QString &code, DomType fileType = DomType::QmlFile);
1220
1221 // --- start of potentially dangerous stuff, make private? ---
1222
1223 std::shared_ptr<DomTop> topPtr() const;
1224 std::shared_ptr<OwningItem> owningItemPtr() const;
1225
1226 // keep the DomItem around to ensure that it doesn't get deleted
1227 template<typename T, typename std::enable_if<std::is_base_of_v<DomBase, T>, bool>::type = true>
1228 T const *as() const
1229 {
1230 if (m_kind == T::kindValue) {
1231 if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
1232 return std::get<SimpleObjectWrap>(v: m_element)->as<T>();
1233 else
1234 return static_cast<T const *>(base());
1235 }
1236 return nullptr;
1237 }
1238
1239 template<typename T, typename std::enable_if<!std::is_base_of_v<DomBase, T>, bool>::type = true>
1240 T const *as() const
1241 {
1242 if (m_kind == T::kindValue) {
1243 Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind));
1244 return std::get<SimpleObjectWrap>(v: m_element)->as<T>();
1245 }
1246 return nullptr;
1247 }
1248
1249 template<typename T>
1250 std::shared_ptr<T> ownerAs() const;
1251
1252 template<typename Owner, typename T>
1253 DomItem copy(const Owner &owner, const Path &ownerPath, const T &base) const
1254 {
1255 Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
1256 static_assert(IsInlineDom<std::decay_t<T>>::value, "Expected an inline item or pointer");
1257 return DomItem(m_top, owner, ownerPath, base);
1258 }
1259
1260 template<typename Owner>
1261 DomItem copy(const Owner &owner, const Path &ownerPath) const
1262 {
1263 Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
1264 return DomItem(m_top, owner, ownerPath, owner.get());
1265 }
1266
1267 template<typename T>
1268 DomItem copy(const T &base) const
1269 {
1270 Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
1271 using BaseT = std::decay_t<T>;
1272 static_assert(!std::is_same_v<BaseT, ElementT>,
1273 "variant not supported, pass in the stored types");
1274 static_assert(IsInlineDom<BaseT>::value || std::is_same_v<BaseT, std::monostate>,
1275 "expected either a pointer or an inline item");
1276
1277 if constexpr (IsSharedPointerToDomObject<BaseT>::value)
1278 return DomItem(m_top, base, Path(), base.get());
1279 else if constexpr (IsInlineDom<BaseT>::value)
1280 return DomItem(m_top, m_owner, m_ownerPath, base);
1281
1282 Q_UNREACHABLE_RETURN(DomItem(m_top, m_owner, m_ownerPath, nullptr));
1283 }
1284
1285private:
1286 enum class WriteOutCheckResult { Success, Failed };
1287 WriteOutCheckResult performWriteOutChecks(const DomItem &, const DomItem &, OutWriter &, WriteOutChecks) const;
1288 const DomBase *base() const;
1289
1290 template<typename Env, typename Owner>
1291 DomItem(Env, Owner, Path, std::nullptr_t) : DomItem()
1292 {
1293 }
1294
1295 template<typename Env, typename Owner, typename T,
1296 typename = std::enable_if_t<IsInlineDom<std::decay_t<T>>::value>>
1297 DomItem(Env env, Owner owner, const Path &ownerPath, const T &el)
1298 : m_top(env), m_owner(owner), m_ownerPath(ownerPath), m_element(el)
1299 {
1300 using BaseT = std::decay_t<T>;
1301 if constexpr (std::is_pointer_v<BaseT>) {
1302 if (!el || el->kind() == DomType::Empty) { // avoid null ptr, and allow only a
1303 // single kind of Empty
1304 m_kind = DomType::Empty;
1305 m_top = std::monostate();
1306 m_owner = std::monostate();
1307 m_ownerPath = Path();
1308 m_element = Empty();
1309 } else {
1310 using DomT = std::remove_pointer_t<BaseT>;
1311 m_element = el;
1312 m_kind = DomT::kindValue;
1313 }
1314 } else {
1315 static_assert(!std::is_same_v<BaseT, ElementT>,
1316 "variant not supported, pass in the internal type");
1317 m_kind = el->kind();
1318 }
1319 }
1320 friend class DomBase;
1321 friend class DomElement;
1322 friend class Map;
1323 friend class List;
1324 friend class QmlObject;
1325 friend class DomUniverse;
1326 friend class DomEnvironment;
1327 friend class ExternalItemInfoBase;
1328 friend class ConstantData;
1329 friend class MutableDomItem;
1330 friend class ScriptExpression;
1331 friend class AstComments;
1332 friend class AttachedInfo;
1333 friend class TestDomItem;
1334 friend QMLDOM_EXPORT bool operator==(const DomItem &, const DomItem &);
1335 DomType m_kind = DomType::Empty;
1336 TopT m_top;
1337 OwnerT m_owner;
1338 Path m_ownerPath;
1339 ElementT m_element = Empty();
1340};
1341
1342QMLDOM_EXPORT bool operator==(const DomItem &o1, const DomItem &o2);
1343
1344inline bool operator!=(const DomItem &o1, const DomItem &o2)
1345{
1346 return !(o1 == o2);
1347}
1348
1349template<typename T>
1350static DomItem keyMultiMapHelper(const DomItem &self, const QString &key,
1351 const QMultiMap<QString, T> &mmap)
1352{
1353 auto it = mmap.find(key);
1354 auto end = mmap.cend();
1355 if (it == end)
1356 return DomItem();
1357 else {
1358 // special case single element (++it == end || it.key() != key)?
1359 QList<const T *> values;
1360 while (it != end && it.key() == key)
1361 values.append(&(*it++));
1362 ListP ll(self.pathFromOwner().appendComponent(c: PathEls::Key(key)), values, QString(),
1363 ListOptions::Reverse);
1364 return self.copy(base: ll);
1365 }
1366}
1367
1368template<typename T>
1369Map Map::fromMultiMapRef(const Path &pathFromOwner, const QMultiMap<QString, T> &mmap)
1370{
1371 return Map(
1372 pathFromOwner,
1373 [&mmap](const DomItem &self, const QString &key) {
1374 return keyMultiMapHelper(self, key, mmap);
1375 },
1376 [&mmap](const DomItem &) { return QSet<QString>(mmap.keyBegin(), mmap.keyEnd()); },
1377 QLatin1String(typeid(T).name()));
1378}
1379
1380template<typename T>
1381Map Map::fromMapRef(
1382 const Path &pathFromOwner, const QMap<QString, T> &map,
1383 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper)
1384{
1385 return Map(
1386 pathFromOwner,
1387 [&map, elWrapper](const DomItem &self, const QString &key) {
1388 const auto it = map.constFind(key);
1389 if (it == map.constEnd())
1390 return DomItem();
1391 return elWrapper(self, PathEls::Key(key), it.value());
1392 },
1393 [&map](const DomItem &) { return QSet<QString>(map.keyBegin(), map.keyEnd()); },
1394 QLatin1String(typeid(T).name()));
1395}
1396
1397template<typename MapT>
1398QSet<QString> Map::fileRegionKeysFromMap(const MapT &map)
1399{
1400 QSet<QString> keys;
1401 std::transform(map.keyBegin(), map.keyEnd(), std::inserter(x&: keys, i: keys.begin()), fileLocationRegionName);
1402 return keys;
1403}
1404
1405template<typename T>
1406Map Map::fromFileRegionMap(const Path &pathFromOwner, const QMap<FileLocationRegion, T> &map)
1407{
1408 auto result = Map(
1409 pathFromOwner,
1410 [&map](const DomItem &mapItem, const QString &key) -> DomItem {
1411 auto it = map.constFind(fileLocationRegionValue(region: key));
1412 if (it == map.constEnd())
1413 return {};
1414
1415 return mapItem.wrap(PathEls::Key(key), *it);
1416 },
1417 [&map](const DomItem &) { return fileRegionKeysFromMap(map); },
1418 QString::fromLatin1(ba: typeid(T).name()));
1419 return result;
1420}
1421
1422template<typename T>
1423Map Map::fromFileRegionListMap(const Path &pathFromOwner,
1424 const QMap<FileLocationRegion, QList<T>> &map)
1425{
1426 using namespace Qt::StringLiterals;
1427 auto result = Map(
1428 pathFromOwner,
1429 [&map](const DomItem &mapItem, const QString &key) -> DomItem {
1430 const QList<SourceLocation> locations = map.value(fileLocationRegionValue(region: key));
1431 if (locations.empty())
1432 return {};
1433
1434 auto list = List::fromQList<SourceLocation>(
1435 mapItem.pathFromOwner(), locations,
1436 [](const DomItem &self, const PathEls::PathComponent &path,
1437 const SourceLocation &location) {
1438 return self.subLocationItem(c: path, loc: location);
1439 });
1440 return mapItem.subListItem(list);
1441 },
1442 [&map](const DomItem &) { return fileRegionKeysFromMap(map); },
1443 u"QList<%1>"_s.arg(a: QString::fromLatin1(ba: typeid(T).name())));
1444 return result;
1445}
1446
1447template<typename T>
1448List List::fromQList(
1449 const Path &pathFromOwner, const QList<T> &list,
1450 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
1451 ListOptions options)
1452{
1453 index_type len = list.size();
1454 if (options == ListOptions::Reverse) {
1455 return List(
1456 pathFromOwner,
1457 [list, elWrapper](const DomItem &self, index_type i) mutable {
1458 if (i < 0 || i >= list.size())
1459 return DomItem();
1460 return elWrapper(self, PathEls::Index(i), list[list.size() - i - 1]);
1461 },
1462 [len](const DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
1463 } else {
1464 return List(
1465 pathFromOwner,
1466 [list, elWrapper](const DomItem &self, index_type i) mutable {
1467 if (i < 0 || i >= list.size())
1468 return DomItem();
1469 return elWrapper(self, PathEls::Index(i), list[i]);
1470 },
1471 [len](const DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
1472 }
1473}
1474
1475template<typename T>
1476List List::fromQListRef(
1477 const Path &pathFromOwner, const QList<T> &list,
1478 const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
1479 ListOptions options)
1480{
1481 if (options == ListOptions::Reverse) {
1482 return List(
1483 pathFromOwner,
1484 [&list, elWrapper](const DomItem &self, index_type i) {
1485 if (i < 0 || i >= list.size())
1486 return DomItem();
1487 return elWrapper(self, PathEls::Index(i), list[list.size() - i - 1]);
1488 },
1489 [&list](const DomItem &) { return list.size(); }, nullptr,
1490 QLatin1String(typeid(T).name()));
1491 } else {
1492 return List(
1493 pathFromOwner,
1494 [&list, elWrapper](const DomItem &self, index_type i) {
1495 if (i < 0 || i >= list.size())
1496 return DomItem();
1497 return elWrapper(self, PathEls::Index(i), list[i]);
1498 },
1499 [&list](const DomItem &) { return list.size(); }, nullptr,
1500 QLatin1String(typeid(T).name()));
1501 }
1502}
1503
1504class QMLDOM_EXPORT OwningItem: public DomBase {
1505protected:
1506 virtual std::shared_ptr<OwningItem> doCopy(const DomItem &self) const = 0;
1507
1508public:
1509 OwningItem(const OwningItem &o);
1510 OwningItem(int derivedFrom=0);
1511 OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt);
1512 OwningItem(const OwningItem &&) = delete;
1513 OwningItem &operator=(const OwningItem &&) = delete;
1514 static int nextRevision();
1515
1516 Path canonicalPath(const DomItem &self) const override = 0;
1517
1518 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
1519 std::shared_ptr<OwningItem> makeCopy(const DomItem &self) const { return doCopy(self); }
1520 Path pathFromOwner() const { return Path(); }
1521 Path pathFromOwner(const DomItem &) const override final { return Path(); }
1522 DomItem containingObject(const DomItem &self) const override;
1523 int derivedFrom() const;
1524 virtual int revision() const;
1525
1526 QDateTime createdAt() const;
1527 virtual QDateTime lastDataUpdateAt() const;
1528 virtual void refreshedDataAt(QDateTime tNew);
1529
1530 // explicit freeze handling needed?
1531 virtual bool frozen() const;
1532 virtual bool freeze();
1533 QDateTime frozenAt() const;
1534
1535 virtual void addError(const DomItem &self, ErrorMessage &&msg);
1536 void addErrorLocal(ErrorMessage &&msg);
1537 void clearErrors(const ErrorGroups &groups = ErrorGroups({}));
1538 // return false if a quick exit was requested
1539 bool iterateErrors(
1540 const DomItem &self,
1541 function_ref<bool(const DomItem &source, const ErrorMessage &msg)> visitor,
1542 const Path &inPath = Path());
1543 QMultiMap<Path, ErrorMessage> localErrors() const {
1544 QMutexLocker l(mutex());
1545 return m_errors;
1546 }
1547
1548 virtual bool iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor);
1549
1550 QBasicMutex *mutex() const { return &m_mutex; }
1551private:
1552 mutable QBasicMutex m_mutex;
1553 int m_derivedFrom;
1554 int m_revision;
1555 QDateTime m_createdAt;
1556 QDateTime m_lastDataUpdateAt;
1557 QDateTime m_frozenAt;
1558 QMultiMap<Path, ErrorMessage> m_errors;
1559 QMap<ErrorMessage, quint32> m_errorsCounts;
1560};
1561
1562template<typename T>
1563std::shared_ptr<T> DomItem::ownerAs() const
1564{
1565 if constexpr (domTypeIsOwningItem(T::kindValue)) {
1566 if (!std::holds_alternative<std::monostate>(v: m_owner)) {
1567 if constexpr (T::kindValue == DomType::AttachedInfo) {
1568 if (std::holds_alternative<std::shared_ptr<AttachedInfo>>(v: m_owner))
1569 return std::static_pointer_cast<T>(
1570 std::get<std::shared_ptr<AttachedInfo>>(v: m_owner));
1571 } else if constexpr (T::kindValue == DomType::ExternalItemInfo) {
1572 if (std::holds_alternative<std::shared_ptr<ExternalItemInfoBase>>(v: m_owner))
1573 return std::static_pointer_cast<T>(
1574 std::get<std::shared_ptr<ExternalItemInfoBase>>(v: m_owner));
1575 } else if constexpr (T::kindValue == DomType::ExternalItemPair) {
1576 if (std::holds_alternative<std::shared_ptr<ExternalItemPairBase>>(v: m_owner))
1577 return std::static_pointer_cast<T>(
1578 std::get<std::shared_ptr<ExternalItemPairBase>>(v: m_owner));
1579 } else {
1580 if (std::holds_alternative<std::shared_ptr<T>>(m_owner)) {
1581 return std::get<std::shared_ptr<T>>(m_owner);
1582 }
1583 }
1584 }
1585 } else {
1586 Q_ASSERT_X(false, "DomItem::ownerAs", "unexpected non owning value in ownerAs");
1587 }
1588 return std::shared_ptr<T> {};
1589}
1590
1591template<int I>
1592struct rank : rank<I - 1>
1593{
1594 static_assert(I > 0, "");
1595};
1596template<>
1597struct rank<0>
1598{
1599};
1600
1601template<typename T>
1602auto writeOutWrap(const T &t, const DomItem &self, OutWriter &lw, rank<1>)
1603 -> decltype(t.writeOut(self, lw))
1604{
1605 t.writeOut(self, lw);
1606}
1607
1608template<typename T>
1609auto writeOutWrap(const T &, const DomItem &, OutWriter &, rank<0>) -> void
1610{
1611 qCWarning(writeOutLog) << "Ignoring writeout to wrapped object not supporting it ("
1612 << typeid(T).name();
1613}
1614template<typename T>
1615auto writeOutWrap(const T &t, const DomItem &self, OutWriter &lw) -> void
1616{
1617 writeOutWrap(t, self, lw, rank<1>());
1618}
1619
1620template<typename T>
1621void SimpleObjectWrapT<T>::writeOut(const DomItem &self, OutWriter &lw) const
1622{
1623 writeOutWrap<T>(*asT(), self, lw);
1624}
1625
1626QMLDOM_EXPORT QDebug operator<<(QDebug debug, const DomItem &c);
1627
1628class QMLDOM_EXPORT MutableDomItem {
1629public:
1630 using CopyOption = DomItem::CopyOption;
1631
1632 explicit operator bool() const
1633 {
1634 return bool(m_owner);
1635 } // this is weaker than item(), but normally correct
1636 DomType internalKind() { return item().internalKind(); }
1637 QString internalKindStr() { return domTypeToString(k: internalKind()); }
1638 DomKind domKind() { return kind2domKind(k: internalKind()); }
1639
1640 Path canonicalPath() const { return m_owner.canonicalPath().path(toAdd: m_pathFromOwner); }
1641 MutableDomItem containingObject()
1642 {
1643 if (m_pathFromOwner)
1644 return MutableDomItem(m_owner, m_pathFromOwner.split().pathToSource);
1645 else {
1646 DomItem cObj = m_owner.containingObject();
1647 return MutableDomItem(cObj.owner(), (domTypeIsOwningItem(cObj.internalKind()) ? Path() :cObj.pathFromOwner()));
1648 }
1649 }
1650
1651 MutableDomItem container()
1652 {
1653 if (m_pathFromOwner)
1654 return MutableDomItem(m_owner, m_pathFromOwner.dropTail());
1655 else {
1656 return MutableDomItem(item().container());
1657 }
1658 }
1659
1660 MutableDomItem qmlObject(GoTo option = GoTo::Strict,
1661 FilterUpOptions fOptions = FilterUpOptions::ReturnOuter)
1662 {
1663 return MutableDomItem(item().qmlObject(option, options: fOptions));
1664 }
1665 MutableDomItem fileObject(GoTo option = GoTo::Strict)
1666 {
1667 return MutableDomItem(item().fileObject(option));
1668 }
1669 MutableDomItem rootQmlObject(GoTo option = GoTo::Strict)
1670 {
1671 return MutableDomItem(item().rootQmlObject(option));
1672 }
1673 MutableDomItem globalScope() { return MutableDomItem(item().globalScope()); }
1674 MutableDomItem scope() { return MutableDomItem(item().scope()); }
1675
1676 MutableDomItem component(GoTo option = GoTo::Strict)
1677 {
1678 return MutableDomItem { item().component(option) };
1679 }
1680 MutableDomItem owner() { return MutableDomItem(m_owner); }
1681 MutableDomItem top() { return MutableDomItem(item().top()); }
1682 MutableDomItem environment() { return MutableDomItem(item().environment()); }
1683 MutableDomItem universe() { return MutableDomItem(item().universe()); }
1684 Path pathFromOwner() { return m_pathFromOwner; }
1685 MutableDomItem operator[](const Path &path) { return MutableDomItem(item()[path]); }
1686 MutableDomItem operator[](QStringView component) { return MutableDomItem(item()[component]); }
1687 MutableDomItem operator[](const QString &component)
1688 {
1689 return MutableDomItem(item()[component]);
1690 }
1691 MutableDomItem operator[](const char16_t *component)
1692 {
1693 // to avoid clash with stupid builtin ptrdiff_t[MutableDomItem&], coming from C
1694 return MutableDomItem(item()[QStringView(component)]);
1695 }
1696 MutableDomItem operator[](index_type i) { return MutableDomItem(item().index(i)); }
1697
1698 MutableDomItem path(const Path &p) { return MutableDomItem(item().path(p)); }
1699 MutableDomItem path(const QString &p) { return path(p: Path::fromString(s: p)); }
1700 MutableDomItem path(QStringView p) { return path(p: Path::fromString(s: p)); }
1701
1702 QList<QString> const fields() { return item().fields(); }
1703 MutableDomItem field(QStringView name) { return MutableDomItem(item().field(name)); }
1704 index_type indexes() { return item().indexes(); }
1705 MutableDomItem index(index_type i) { return MutableDomItem(item().index(i)); }
1706
1707 QSet<QString> const keys() { return item().keys(); }
1708 MutableDomItem key(const QString &name) { return MutableDomItem(item().key(name)); }
1709 MutableDomItem key(QStringView name) { return key(name: name.toString()); }
1710
1711 void
1712 dump(const Sink &s, int indent = 0,
1713 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter)
1714 {
1715 item().dump(s, indent, filter);
1716 }
1717 FileWriter::Status
1718 dump(const QString &path,
1719 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter,
1720 int nBackups = 2, int indent = 0, FileWriter *fw = nullptr)
1721 {
1722 return item().dump(path, filter, nBackups, indent, fw);
1723 }
1724 void writeOut(OutWriter &lw) { return item().writeOut(lw); }
1725 bool writeOut(const QString &path, int nBackups = 2,
1726 const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr)
1727 {
1728 return item().writeOut(path, nBackups, opt, fw);
1729 }
1730
1731 MutableDomItem fileLocations() { return MutableDomItem(item().fileLocations()); }
1732 MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected)
1733 {
1734 return item().makeCopy(option);
1735 }
1736 bool commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr = nullptr)
1737 {
1738 return item().commitToBase(validPtr: validEnvPtr);
1739 }
1740 QString canonicalFilePath() const { return item().canonicalFilePath(); }
1741
1742 MutableDomItem refreshed() { return MutableDomItem(item().refreshed()); }
1743
1744 QCborValue value() { return item().value(); }
1745
1746 QString toString() { return item().toString(); }
1747
1748 // convenience getters
1749 QString name() { return item().name(); }
1750 MutableDomItem pragmas() { return item().pragmas(); }
1751 MutableDomItem ids() { return MutableDomItem::item().ids(); }
1752 QString idStr() { return item().idStr(); }
1753 MutableDomItem propertyDefs() { return MutableDomItem(item().propertyDefs()); }
1754 MutableDomItem bindings() { return MutableDomItem(item().bindings()); }
1755 MutableDomItem methods() { return MutableDomItem(item().methods()); }
1756 MutableDomItem children() { return MutableDomItem(item().children()); }
1757 MutableDomItem child(index_type i) { return MutableDomItem(item().child(i)); }
1758 MutableDomItem annotations() { return MutableDomItem(item().annotations()); }
1759
1760 // // OwnigItem elements
1761 int derivedFrom() { return m_owner.derivedFrom(); }
1762 int revision() { return m_owner.revision(); }
1763 QDateTime createdAt() { return m_owner.createdAt(); }
1764 QDateTime frozenAt() { return m_owner.frozenAt(); }
1765 QDateTime lastDataUpdateAt() { return m_owner.lastDataUpdateAt(); }
1766
1767 void addError(ErrorMessage &&msg) { item().addError(msg: std::move(msg)); }
1768 ErrorHandler errorHandler();
1769
1770 // convenience setters
1771 MutableDomItem addPrototypePath(const Path &prototypePath);
1772 MutableDomItem setNextScopePath(const Path &nextScopePath);
1773 MutableDomItem setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs);
1774 MutableDomItem setBindings(QMultiMap<QString, Binding> bindings);
1775 MutableDomItem setMethods(QMultiMap<QString, MethodInfo> functionDefs);
1776 MutableDomItem setChildren(const QList<QmlObject> &children);
1777 MutableDomItem setAnnotations(const QList<QmlObject> &annotations);
1778 MutableDomItem setScript(const std::shared_ptr<ScriptExpression> &exp);
1779 MutableDomItem setCode(const QString &code);
1780 MutableDomItem addPropertyDef(const PropertyDefinition &propertyDef,
1781 AddOption option = AddOption::Overwrite);
1782 MutableDomItem addBinding(Binding binding, AddOption option = AddOption::Overwrite);
1783 MutableDomItem addMethod(
1784 const MethodInfo &functionDef, AddOption option = AddOption::Overwrite);
1785 MutableDomItem addChild(QmlObject child);
1786 MutableDomItem addAnnotation(QmlObject child);
1787 MutableDomItem addPreComment(const Comment &comment, FileLocationRegion region);
1788 MutableDomItem addPostComment(const Comment &comment, FileLocationRegion region);
1789 QQmlJSScope::ConstPtr semanticScope();
1790 void setSemanticScope(const QQmlJSScope::ConstPtr &scope);
1791
1792 MutableDomItem() = default;
1793 MutableDomItem(const DomItem &owner, const Path &pathFromOwner):
1794 m_owner(owner), m_pathFromOwner(pathFromOwner)
1795 {}
1796 MutableDomItem(const DomItem &item):
1797 m_owner(item.owner()), m_pathFromOwner(item.pathFromOwner())
1798 {}
1799
1800 std::shared_ptr<DomTop> topPtr() { return m_owner.topPtr(); }
1801 std::shared_ptr<OwningItem> owningItemPtr() { return m_owner.owningItemPtr(); }
1802
1803 template<typename T>
1804 T const *as()
1805 {
1806 return item().as<T>();
1807 }
1808
1809 template <typename T>
1810 T *mutableAs() {
1811 Q_ASSERT(!m_owner || !m_owner.owningItemPtr()->frozen());
1812
1813 DomItem self = item();
1814 if (self.m_kind != T::kindValue)
1815 return nullptr;
1816
1817 const T *t = nullptr;
1818 if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
1819 t = static_cast<const SimpleObjectWrapBase *>(self.base())->as<T>();
1820 else if constexpr (std::is_base_of<DomBase, T>::value)
1821 t = static_cast<const T *>(self.base());
1822 else
1823 Q_UNREACHABLE_RETURN(nullptr);
1824
1825 // Nasty. But since ElementT has to store the const pointers, we allow it in this one place.
1826 return const_cast<T *>(t);
1827 }
1828
1829 template<typename T>
1830 std::shared_ptr<T> ownerAs() const
1831 {
1832 return m_owner.ownerAs<T>();
1833 }
1834 // it is dangerous to assume it stays valid when updates are preformed...
1835 DomItem item() const { return m_owner.path(p: m_pathFromOwner); }
1836
1837 friend bool operator==(const MutableDomItem &o1, const MutableDomItem &o2)
1838 {
1839 return o1.m_owner == o2.m_owner && o1.m_pathFromOwner == o2.m_pathFromOwner;
1840 }
1841 friend bool operator!=(const MutableDomItem &o1, const MutableDomItem &o2)
1842 {
1843 return !(o1 == o2);
1844 }
1845
1846private:
1847 DomItem m_owner;
1848 Path m_pathFromOwner;
1849};
1850
1851QMLDOM_EXPORT QDebug operator<<(QDebug debug, const MutableDomItem &c);
1852
1853template<typename K, typename T>
1854Path insertUpdatableElementInMultiMap(const Path &mapPathFromOwner, QMultiMap<K, T> &mmap, K key,
1855 const T &value, AddOption option = AddOption::KeepExisting,
1856 T **valuePtr = nullptr)
1857{
1858 if (option == AddOption::Overwrite) {
1859 auto it = mmap.find(key);
1860 if (it != mmap.end()) {
1861 T &v = *it;
1862 v = value;
1863 if (++it != mmap.end() && it.key() == key) {
1864 qWarning() << " requested overwrite of " << key
1865 << " that contains aleready multiple entries in" << mapPathFromOwner;
1866 }
1867 Path newPath = mapPathFromOwner.key(key).index(0);
1868 v.updatePathFromOwner(newPath);
1869 if (valuePtr)
1870 *valuePtr = &v;
1871 return newPath;
1872 }
1873 }
1874 mmap.insert(key, value);
1875 auto it = mmap.find(key);
1876 auto it2 = it;
1877 int nVal = 0;
1878 while (it2 != mmap.end() && it2.key() == key) {
1879 ++nVal;
1880 ++it2;
1881 }
1882 Path newPath = mapPathFromOwner.key(key).index(nVal-1);
1883 T &v = *it;
1884 v.updatePathFromOwner(newPath);
1885 if (valuePtr)
1886 *valuePtr = &v;
1887 return newPath;
1888}
1889
1890template<typename T>
1891Path appendUpdatableElementInQList(const Path &listPathFromOwner, QList<T> &list, const T &value,
1892 T **vPtr = nullptr)
1893{
1894 int idx = list.size();
1895 list.append(value);
1896 Path newPath = listPathFromOwner.index(i: idx);
1897 T &targetV = list[idx];
1898 targetV.updatePathFromOwner(newPath);
1899 if (vPtr)
1900 *vPtr = &targetV;
1901 return newPath;
1902}
1903
1904template <typename T, typename K = QString>
1905void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, const Path &newPath)
1906{
1907 auto it = mmap.begin();
1908 auto end = mmap.end();
1909 index_type i = 0;
1910 K name;
1911 QList<T*> els;
1912 while (it != end) {
1913 if (i > 0 && name != it.key()) {
1914 Path pName = newPath.key(name: QString(name));
1915 for (T *el : els)
1916 el->updatePathFromOwner(pName.index(i: --i));
1917 els.clear();
1918 els.append(&(*it));
1919 name = it.key();
1920 i = 1;
1921 } else {
1922 els.append(&(*it));
1923 name = it.key();
1924 ++i;
1925 }
1926 ++it;
1927 }
1928 Path pName = newPath.key(name);
1929 for (T *el : els)
1930 el->updatePathFromOwner(pName.index(i: --i));
1931}
1932
1933template <typename T>
1934void updatePathFromOwnerQList(QList<T> &list, const Path &newPath)
1935{
1936 auto it = list.begin();
1937 auto end = list.end();
1938 index_type i = 0;
1939 while (it != end)
1940 (it++)->updatePathFromOwner(newPath.index(i: i++));
1941}
1942
1943constexpr bool domTypeIsObjWrap(DomType k)
1944{
1945 switch (k) {
1946 case DomType::Binding:
1947 case DomType::EnumItem:
1948 case DomType::ErrorMessage:
1949 case DomType::Export:
1950 case DomType::Id:
1951 case DomType::Import:
1952 case DomType::ImportScope:
1953 case DomType::MethodInfo:
1954 case DomType::MethodParameter:
1955 case DomType::ModuleAutoExport:
1956 case DomType::Pragma:
1957 case DomType::PropertyDefinition:
1958 case DomType::Version:
1959 case DomType::Comment:
1960 case DomType::CommentedElement:
1961 case DomType::RegionComments:
1962 case DomType::FileLocations:
1963 case DomType::UpdatedScriptExpression:
1964 return true;
1965 default:
1966 return false;
1967 }
1968}
1969
1970constexpr bool domTypeIsValueWrap(DomType k)
1971{
1972 switch (k) {
1973 case DomType::PropertyInfo:
1974 return true;
1975 default:
1976 return false;
1977 }
1978}
1979
1980constexpr bool domTypeIsDomElement(DomType k)
1981{
1982 switch (k) {
1983 case DomType::ModuleScope:
1984 case DomType::QmlObject:
1985 case DomType::ConstantData:
1986 case DomType::SimpleObjectWrap:
1987 case DomType::Reference:
1988 case DomType::Map:
1989 case DomType::List:
1990 case DomType::ListP:
1991 case DomType::EnumDecl:
1992 case DomType::JsResource:
1993 case DomType::QmltypesComponent:
1994 case DomType::QmlComponent:
1995 case DomType::GlobalComponent:
1996 case DomType::MockObject:
1997 return true;
1998 default:
1999 return false;
2000 }
2001}
2002
2003constexpr bool domTypeIsOwningItem(DomType k)
2004{
2005 switch (k) {
2006 case DomType::ModuleIndex:
2007
2008 case DomType::MockOwner:
2009
2010 case DomType::ExternalItemInfo:
2011 case DomType::ExternalItemPair:
2012
2013 case DomType::QmlDirectory:
2014 case DomType::QmldirFile:
2015 case DomType::JsFile:
2016 case DomType::QmlFile:
2017 case DomType::QmltypesFile:
2018 case DomType::GlobalScope:
2019
2020 case DomType::ScriptExpression:
2021 case DomType::AstComments:
2022
2023 case DomType::LoadInfo:
2024 case DomType::AttachedInfo:
2025
2026 case DomType::DomEnvironment:
2027 case DomType::DomUniverse:
2028 return true;
2029 default:
2030 return false;
2031 }
2032}
2033
2034constexpr bool domTypeIsUnattachedOwningItem(DomType k)
2035{
2036 switch (k) {
2037 case DomType::ScriptExpression:
2038 case DomType::AstComments:
2039 case DomType::AttachedInfo:
2040 return true;
2041 default:
2042 return false;
2043 }
2044}
2045
2046constexpr bool domTypeIsScriptElement(DomType k)
2047{
2048 return DomType::ScriptElementStart <= k && k <= DomType::ScriptElementStop;
2049}
2050
2051template<typename T>
2052DomItem DomItem::subValueItem(const PathEls::PathComponent &c, const T &value,
2053 ConstantData::Options options) const
2054{
2055 using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
2056 if constexpr (
2057 std::is_base_of_v<
2058 QCborValue,
2059 BaseT> || std::is_base_of_v<QCborArray, BaseT> || std::is_base_of_v<QCborMap, BaseT>) {
2060 return DomItem(m_top, m_owner, m_ownerPath,
2061 ConstantData(pathFromOwner().appendComponent(c), value, options));
2062 } else if constexpr (std::is_same_v<DomItem, BaseT>) {
2063 Q_UNUSED(options);
2064 return value;
2065 } else if constexpr (IsList<T>::value && !std::is_convertible_v<BaseT, QStringView>) {
2066 return subListItem(list: List::fromQList<typename BaseT::value_type>(
2067 pathFromOwner().appendComponent(c), value,
2068 [options](const DomItem &list, const PathEls::PathComponent &p,
2069 const typename T::value_type &v) { return list.subValueItem(p, v, options); }));
2070 } else if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
2071 Q_UNUSED(options);
2072 return subOwnerItem(c, value);
2073 } else {
2074 return subDataItem(c, value, options);
2075 }
2076}
2077
2078template<typename T>
2079DomItem DomItem::subDataItem(const PathEls::PathComponent &c, const T &value,
2080 ConstantData::Options options) const
2081{
2082 using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
2083 if constexpr (std::is_same_v<BaseT, ConstantData>) {
2084 return this->copy(value);
2085 } else if constexpr (std::is_base_of_v<QCborValue, BaseT>) {
2086 return DomItem(m_top, m_owner, m_ownerPath,
2087 ConstantData(pathFromOwner().appendComponent(c), value, options));
2088 } else {
2089 return DomItem(
2090 m_top, m_owner, m_ownerPath,
2091 ConstantData(pathFromOwner().appendComponent(c), QCborValue(value), options));
2092 }
2093}
2094
2095template<typename T>
2096bool DomItem::dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, const T &value,
2097 ConstantData::Options options) const
2098{
2099 auto lazyWrap = [this, &c, &value, options]() {
2100 return this->subValueItem<T>(c, value, options);
2101 };
2102 return visitor(c, lazyWrap);
2103}
2104
2105template<typename F>
2106bool DomItem::dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
2107 ConstantData::Options options) const
2108{
2109 auto lazyWrap = [this, &c, &valueF, options]() {
2110 return this->subValueItem<decltype(valueF())>(c, valueF(), options);
2111 };
2112 return visitor(c, lazyWrap);
2113}
2114
2115template<typename T>
2116DomItem DomItem::wrap(const PathEls::PathComponent &c, const T &obj) const
2117{
2118 using BaseT = std::decay_t<T>;
2119 if constexpr (std::is_same_v<QString, BaseT> || std::is_arithmetic_v<BaseT>) {
2120 return this->subDataItem(c, value: QCborValue(obj));
2121 } else if constexpr (std::is_same_v<SourceLocation, BaseT>) {
2122 return this->subLocationItem(c, loc: obj);
2123 } else if constexpr (std::is_same_v<BaseT, Reference>) {
2124 Q_ASSERT_X(false, "DomItem::wrap",
2125 "wrapping a reference object, probably an error (wrap the target path instead)");
2126 return this->copy(obj);
2127 } else if constexpr (std::is_same_v<BaseT, ConstantData>) {
2128 return this->subDataItem(c, obj);
2129 } else if constexpr (std::is_same_v<BaseT, Map>) {
2130 return this->subMapItem(map: obj);
2131 } else if constexpr (std::is_same_v<BaseT, List>) {
2132 return this->subListItem(list: obj);
2133 } else if constexpr (std::is_base_of_v<ListPBase, BaseT>) {
2134 return this->subListItem(list: obj);
2135 } else if constexpr (std::is_same_v<BaseT, SimpleObjectWrap>) {
2136 return this->subObjectWrapItem(obj);
2137 } else if constexpr (IsDomObject<BaseT>::value) {
2138 if constexpr (domTypeIsObjWrap(BaseT::kindValue) || domTypeIsValueWrap(BaseT::kindValue)) {
2139 return this->subObjectWrapItem(
2140 obj: SimpleObjectWrap::fromObjectRef(this->pathFromOwner().appendComponent(c), obj));
2141 } else if constexpr (domTypeIsDomElement(BaseT::kindValue)) {
2142 return this->copy(&obj);
2143 } else {
2144 qCWarning(domLog) << "Unhandled object of type " << domTypeToString(BaseT::kindValue)
2145 << " in DomItem::wrap, not using a shared_ptr for an "
2146 << "OwningItem, or unexpected wrapped object?";
2147 return DomItem();
2148 }
2149 } else if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
2150 if constexpr (domTypeIsOwningItem(BaseT::element_type::kindValue)) {
2151 return this->subOwnerItem(c, obj);
2152 } else {
2153 Q_ASSERT_X(false, "DomItem::wrap", "shared_ptr with non owning item");
2154 return DomItem();
2155 }
2156 } else if constexpr (IsMultiMap<BaseT>::value) {
2157 if constexpr (std::is_same_v<typename BaseT::key_type, QString>) {
2158 return subMapItem(map: Map::fromMultiMapRef<typename BaseT::mapped_type>(
2159 pathFromOwner().appendComponent(c), obj));
2160 } else {
2161 Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)");
2162 }
2163 } else if constexpr (IsMap<BaseT>::value) {
2164 if constexpr (std::is_same_v<typename BaseT::key_type, QString>) {
2165 return subMapItem(map: Map::fromMapRef<typename BaseT::mapped_type>(
2166 pathFromOwner().appendComponent(c), obj,
2167 [](const DomItem &map, const PathEls::PathComponent &p,
2168 const typename BaseT::mapped_type &el) { return map.wrap(p, el); }));
2169 } else {
2170 Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)");
2171 }
2172 } else if constexpr (IsList<BaseT>::value) {
2173 if constexpr (IsDomObject<typename BaseT::value_type>::value) {
2174 return subListItem(list: List::fromQListRef<typename BaseT::value_type>(
2175 pathFromOwner().appendComponent(c), obj,
2176 [](const DomItem &list, const PathEls::PathComponent &p,
2177 const typename BaseT::value_type &el) { return list.wrap(p, el); }));
2178 } else {
2179 Q_ASSERT_X(false, "DomItem::wrap", "Unsupported list type T");
2180 return DomItem();
2181 }
2182 } else {
2183 qCWarning(domLog) << "Cannot wrap " << typeid(BaseT).name();
2184 Q_ASSERT_X(false, "DomItem::wrap", "Do not know how to wrap type T");
2185 return DomItem();
2186 }
2187}
2188
2189template<typename T>
2190bool DomItem::dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj) const
2191{
2192 auto lazyWrap = [this, &c, &obj]() { return this->wrap<T>(c, obj); };
2193 return visitor(c, lazyWrap);
2194}
2195
2196template<typename T>
2197bool ListPT<T>::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const
2198{
2199 index_type len = index_type(m_pList.size());
2200 for (index_type i = 0; i < len; ++i) {
2201 if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, i); }))
2202 return false;
2203 }
2204 return true;
2205}
2206
2207template<typename T>
2208DomItem ListPT<T>::index(const DomItem &self, index_type index) const
2209{
2210 if (index >= 0 && index < m_pList.size())
2211 return self.wrap(PathEls::Index(index), *static_cast<const T *>(m_pList.value(index)));
2212 return DomItem();
2213}
2214
2215// allow inlining of DomBase
2216inline DomKind DomBase::domKind() const
2217{
2218 return kind2domKind(k: kind());
2219}
2220
2221inline bool DomBase::iterateDirectSubpathsConst(const DomItem &self, DirectVisitor visitor) const
2222{
2223 Q_ASSERT(self.base() == this);
2224 return self.iterateDirectSubpaths(v: std::move(visitor));
2225}
2226
2227inline DomItem DomBase::containingObject(const DomItem &self) const
2228{
2229 Path path = pathFromOwner(self);
2230 DomItem base = self.owner();
2231 if (!path) {
2232 path = canonicalPath(self);
2233 base = self;
2234 }
2235 Source source = path.split();
2236 return base.path(p: source.pathToSource);
2237}
2238
2239inline quintptr DomBase::id() const
2240{
2241 return quintptr(this);
2242}
2243
2244inline QString DomBase::typeName() const
2245{
2246 return domTypeToString(k: kind());
2247}
2248
2249inline QList<QString> DomBase::fields(const DomItem &self) const
2250{
2251 QList<QString> res;
2252 self.iterateDirectSubpaths(v: [&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
2253 if (c.kind() == Path::Kind::Field)
2254 res.append(t: c.name());
2255 return true;
2256 });
2257 return res;
2258}
2259
2260inline DomItem DomBase::field(const DomItem &self, QStringView name) const
2261{
2262 DomItem res;
2263 self.iterateDirectSubpaths(
2264 v: [&res, name](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
2265 if (c.kind() == Path::Kind::Field && c.checkName(s: name)) {
2266 res = obj();
2267 return false;
2268 }
2269 return true;
2270 });
2271 return res;
2272}
2273
2274inline index_type DomBase::indexes(const DomItem &self) const
2275{
2276 index_type res = 0;
2277 self.iterateDirectSubpaths(v: [&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
2278 if (c.kind() == Path::Kind::Index) {
2279 index_type i = c.index() + 1;
2280 if (res < i)
2281 res = i;
2282 }
2283 return true;
2284 });
2285 return res;
2286}
2287
2288inline DomItem DomBase::index(const DomItem &self, qint64 index) const
2289{
2290 DomItem res;
2291 self.iterateDirectSubpaths(
2292 v: [&res, index](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
2293 if (c.kind() == Path::Kind::Index && c.index() == index) {
2294 res = obj();
2295 return false;
2296 }
2297 return true;
2298 });
2299 return res;
2300}
2301
2302inline QSet<QString> const DomBase::keys(const DomItem &self) const
2303{
2304 QSet<QString> res;
2305 self.iterateDirectSubpaths(v: [&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
2306 if (c.kind() == Path::Kind::Key)
2307 res.insert(value: c.name());
2308 return true;
2309 });
2310 return res;
2311}
2312
2313inline DomItem DomBase::key(const DomItem &self, const QString &name) const
2314{
2315 DomItem res;
2316 self.iterateDirectSubpaths(
2317 v: [&res, name](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
2318 if (c.kind() == Path::Kind::Key && c.checkName(s: name)) {
2319 res = obj();
2320 return false;
2321 }
2322 return true;
2323 });
2324 return res;
2325}
2326
2327inline DomItem DomItem::subListItem(const List &list) const
2328{
2329 return DomItem(m_top, m_owner, m_ownerPath, list);
2330}
2331
2332inline DomItem DomItem::subMapItem(const Map &map) const
2333{
2334 return DomItem(m_top, m_owner, m_ownerPath, map);
2335}
2336
2337} // end namespace Dom
2338} // end namespace QQmlJS
2339
2340QT_END_NAMESPACE
2341#endif // QMLDOMITEM_H
2342

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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