/**************************************************************************** ** ** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ **/ #ifndef QMLDOMITEM_H #define QMLDOMITEM_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "qqmldom_global.h" #include "qqmldom_fwd_p.h" #include "qqmldomconstants_p.h" #include "qqmldomstringdumper_p.h" #include "qqmldompath_p.h" #include "qqmldomerrormessage_p.h" #include "qqmldomfunctionref_p.h" #include "qqmldomfilewriter_p.h" #include "qqmldomlinewriter_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QQmlJS { // we didn't have enough 'O's to properly name everything... namespace Dom { class Path; Q_DECLARE_LOGGING_CATEGORY(writeOutLog); constexpr bool domTypeIsObjWrap(DomType k); constexpr bool domTypeIsValueWrap(DomType k); constexpr bool domTypeIsDomElement(DomType); constexpr bool domTypeIsOwningItem(DomType); constexpr bool domTypeIsUnattachedOwningItem(DomType); QMLDOM_EXPORT bool domTypeIsExternalItem(DomType k); QMLDOM_EXPORT bool domTypeIsTopItem(DomType k); QMLDOM_EXPORT bool domTypeIsContainer(DomType k); constexpr bool domTypeCanBeInline(DomType k) { switch (k) { case DomType::Empty: case DomType::Map: case DomType::List: case DomType::ListP: case DomType::ConstantData: case DomType::SimpleObjectWrap: case DomType::Reference: return true; default: return false; } } QMLDOM_EXPORT bool domTypeIsScope(DomType k); QMLDOM_EXPORT QMap domTypeToStringMap(); QMLDOM_EXPORT QString domTypeToString(DomType k); QMLDOM_EXPORT QMap domKindToStringMap(); QMLDOM_EXPORT QString domKindToString(DomKind k); QMLDOM_EXPORT QCborValue locationToData(SourceLocation loc, QStringView strValue=u""); inline bool noFilter(DomItem &, const PathEls::PathComponent &, DomItem &) { return true; } using DirectVisitor = function_ref)>; // using DirectVisitor = function_ref; namespace { template struct IsMultiMap : std::false_type { }; template struct IsMultiMap> : std::true_type { }; template struct IsMap : std::false_type { }; template struct IsMap> : std::true_type { }; template using void_t = void; template struct IsDomObject : std::false_type { }; template struct IsDomObject> : std::true_type { }; template struct IsInlineDom : std::false_type { }; template struct IsInlineDom> : std::integral_constant { }; template struct IsInlineDom> : std::true_type { }; template struct IsInlineDom, void_t> : std::true_type { }; template struct IsSharedPointerToDomObject : std::false_type { }; template struct IsSharedPointerToDomObject> : IsDomObject { }; template struct IsList : std::false_type { }; template struct IsList> : std::true_type { }; } template union SubclassStorage { int i; T lp; T *data() { return reinterpret_cast(this); } const T *data() const { return reinterpret_cast(this); } SubclassStorage() { } SubclassStorage(T &&el) { el.moveTo(data()); } SubclassStorage(const T *el) { el->copyTo(data()); } SubclassStorage(const SubclassStorage &o) : SubclassStorage(o.data()) { } SubclassStorage(const SubclassStorage &&o) : SubclassStorage(o.data()) { } SubclassStorage &operator=(const SubclassStorage &o) { data()->~T(); o.data()->copyTo(data()); return *this; } ~SubclassStorage() { data()->~T(); } }; class QMLDOM_EXPORT DomBase{ public: virtual ~DomBase() = default; // minimal overload set: virtual DomType kind() const = 0; virtual DomKind domKind() const; virtual Path pathFromOwner(DomItem &self) const = 0; virtual Path canonicalPath(DomItem &self) const = 0; virtual bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) = 0; // iterates the *direct* subpaths, returns // false if a quick end was requested bool iterateDirectSubpathsConst(DomItem &self, DirectVisitor) const; // iterates the *direct* subpaths, returns false if a quick end was requested virtual DomItem containingObject( DomItem &self) const; // the DomItem corresponding to the canonicalSource source virtual void dump(DomItem &, Sink sink, int indent, function_ref filter) const; virtual quintptr id() const; QString typeName() const; virtual QList fields(DomItem &self) const; virtual DomItem field(DomItem &self, QStringView name) const; virtual index_type indexes(DomItem &self) const; virtual DomItem index(DomItem &self, index_type index) const; virtual QSet const keys(DomItem &self) const; virtual DomItem key(DomItem &self, QString name) const; virtual QString canonicalFilePath(DomItem &self) const; virtual void writeOut(DomItem &self, OutWriter &lw) const; virtual QCborValue value() const { return QCborValue(); } }; inline DomKind kind2domKind(DomType k) { switch (k) { case DomType::Empty: return DomKind::Empty; case DomType::List: case DomType::ListP: return DomKind::List; case DomType::Map: return DomKind::Map; case DomType::ConstantData: return DomKind::Value; default: return DomKind::Object; } } class QMLDOM_EXPORT Empty final : public DomBase { public: constexpr static DomType kindValue = DomType::Empty; DomType kind() const override { return kindValue; } Empty *operator->() { return this; } const Empty *operator->() const { return this; } Empty &operator*() { return *this; } const Empty &operator*() const { return *this; } Empty(); quintptr id() const override { return ~quintptr(0); } Path pathFromOwner(DomItem &self) const override; Path canonicalPath(DomItem &self) const override; DomItem containingObject(DomItem &self) const override; bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; void dump(DomItem &, Sink s, int indent, function_ref filter) const override; }; class QMLDOM_EXPORT DomElement: public DomBase { protected: DomElement& operator=(const DomElement&) = default; public: DomElement(Path pathFromOwner = Path()); DomElement(const DomElement &o) = default; Path pathFromOwner(DomItem &self) const override; Path pathFromOwner() const { return m_pathFromOwner; } Path canonicalPath(DomItem &self) const override; DomItem containingObject(DomItem &self) const override; virtual void updatePathFromOwner(Path newPath); private: Path m_pathFromOwner; }; class QMLDOM_EXPORT Map final : public DomElement { public: constexpr static DomType kindValue = DomType::Map; DomType kind() const override { return kindValue; } Map *operator->() { return this; } const Map *operator->() const { return this; } Map &operator*() { return *this; } const Map &operator*() const { return *this; } using LookupFunction = std::function; using Keys = std::function(DomItem &)>; Map(Path pathFromOwner, LookupFunction lookup, Keys keys, QString targetType); quintptr id() const override; bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; QSet const keys(DomItem &self) const override; DomItem key(DomItem &self, QString name) const override; template static Map fromMultiMapRef( Path pathFromOwner, QMultiMap &mmap, std::function elWrapper); template static Map fromMapRef(Path pathFromOwner, QMap &mmap, std::function elWrapper); private: LookupFunction m_lookup; Keys m_keys; QString m_targetType; }; class QMLDOM_EXPORT List final : public DomElement { public: constexpr static DomType kindValue = DomType::List; DomType kind() const override { return kindValue; } List *operator->() { return this; } const List *operator->() const { return this; } List &operator*() { return *this; } const List &operator*() const { return *this; } using LookupFunction = std::function; using Length = std::function; using IteratorFunction = std::function)>)>; List(Path pathFromOwner, LookupFunction lookup, Length length, IteratorFunction iterator, QString elType); quintptr id() const override; bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; void dump(DomItem &, Sink s, int indent, function_ref) const override; index_type indexes(DomItem &self) const override; DomItem index(DomItem &self, index_type index) const override; template static List fromQList(Path pathFromOwner, QList list, std::function elWrapper, ListOptions options = ListOptions::Normal); template static List fromQListRef(Path pathFromOwner, QList &list, std::function elWrapper, ListOptions options = ListOptions::Normal); void writeOut(DomItem &self, OutWriter &ow, bool compact) const; void writeOut(DomItem &self, OutWriter &ow) const override { writeOut(self, ow, true); } private: LookupFunction m_lookup; Length m_length; IteratorFunction m_iterator; QString m_elType; }; class QMLDOM_EXPORT ListPBase : public DomElement { public: constexpr static DomType kindValue = DomType::ListP; DomType kind() const override { return kindValue; } ListPBase(Path pathFromOwner, const QList &pList, QString elType) : DomElement(pathFromOwner), m_pList(pList), m_elType(elType) { } bool iterateDirectSubpaths(DomItem &self, DirectVisitor v) override; virtual void copyTo(ListPBase *) const { Q_ASSERT(false); }; virtual void moveTo(ListPBase *) const { Q_ASSERT(false); }; quintptr id() const override { return quintptr(0); } index_type indexes(DomItem &) const override { return index_type(m_pList.size()); } void writeOut(DomItem &self, OutWriter &ow, bool compact) const; void writeOut(DomItem &self, OutWriter &ow) const override { writeOut(self, ow, true); } protected: QList m_pList; QString m_elType; }; template class ListPT final : public ListPBase { public: constexpr static DomType kindValue = DomType::ListP; ListPT(Path pathFromOwner, QList pList, QString elType = QString(), ListOptions options = ListOptions::Normal) : ListPBase(pathFromOwner, {}, (elType.isEmpty() ? QLatin1String(typeid(T).name()) : elType)) { static_assert(sizeof(ListPBase) == sizeof(ListPT), "ListPT does not have the same size as ListPBase"); static_assert(alignof(ListPBase) == alignof(ListPT), "ListPT does not have the same size as ListPBase"); m_pList.reserve(pList.size()); if (options == ListOptions::Normal) { for (void *p : pList) m_pList.append(p); } else if (options == ListOptions::Reverse) { for (qsizetype i = pList.length(); i-- != 0;) // probably writing in reverse and reading sequentially would be better m_pList.append(pList.at(i)); } else { Q_ASSERT(false); } } void copyTo(ListPBase *t) const override { new (t) ListPT(*this); } void moveTo(ListPBase *t) const override { new (t) ListPT(std::move(*this)); } bool iterateDirectSubpaths(DomItem &self, DirectVisitor v) override; DomItem index(DomItem &self, index_type index) const override; }; class QMLDOM_EXPORT ListP { public: constexpr static DomType kindValue = DomType::ListP; template ListP(Path pathFromOwner, QList pList, QString elType = QString(), ListOptions options = ListOptions::Normal) : list(ListPT(pathFromOwner, pList, elType, options)) { } ListP() = delete; ListPBase *operator->() { return list.data(); } const ListPBase *operator->() const { return list.data(); } ListPBase &operator*() { return *list.data(); } const ListPBase &operator*() const { return *list.data(); } private: SubclassStorage list; }; class QMLDOM_EXPORT ConstantData final : public DomElement { public: constexpr static DomType kindValue = DomType::ConstantData; DomType kind() const override { return kindValue; } enum class Options { MapIsMap, FirstMapIsFields }; ConstantData *operator->() { return this; } const ConstantData *operator->() const { return this; } ConstantData &operator*() { return *this; } const ConstantData &operator*() const { return *this; } ConstantData(Path pathFromOwner, QCborValue value, Options options = Options::MapIsMap); bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; quintptr id() const override; DomKind domKind() const override; QCborValue value() const override { return m_value; } Options options() const { return m_options; } private: QCborValue m_value; Options m_options; }; class QMLDOM_EXPORT SimpleObjectWrapBase : public DomElement { public: constexpr static DomType kindValue = DomType::SimpleObjectWrap; DomType kind() const final override { return m_kind; } quintptr id() const final override { return m_id; } DomKind domKind() const final override { return m_domKind; } template T const *as() const { if (m_options & SimpleWrapOption::ValueType) { if (m_value.metaType() == QMetaType::fromType()) return reinterpret_cast(m_value.constData()); return nullptr; } else { return m_value.value(); } } template T *mutableAs() { if (m_options & SimpleWrapOption::ValueType) { if (m_value.metaType() == QMetaType::fromType()) return reinterpret_cast(m_value.data()); return nullptr; } else { return m_value.value(); } } SimpleObjectWrapBase() = delete; virtual void copyTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); } virtual void moveTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); } bool iterateDirectSubpaths(DomItem &, DirectVisitor) override { Q_ASSERT(false); return true; } protected: friend class TestDomItem; SimpleObjectWrapBase(Path pathFromOwner, QVariant value, quintptr idValue, DomType kind = kindValue, SimpleWrapOptions options = SimpleWrapOption::None) : DomElement(pathFromOwner), m_kind(kind), m_domKind(kind2domKind(kind)), m_value(value), m_id(idValue), m_options(options) { } DomType m_kind; DomKind m_domKind; QVariant m_value; quintptr m_id; SimpleWrapOptions m_options; }; template class SimpleObjectWrapT final : public SimpleObjectWrapBase { public: constexpr static DomType kindValue = DomType::SimpleObjectWrap; bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override { return mutableAsT()->iterateDirectSubpaths(self, visitor); } void writeOut(DomItem &self, OutWriter &lw) const override; T const *asT() const { if constexpr (domTypeIsValueWrap(T::kindValue)) { if (m_value.metaType() == QMetaType::fromType()) return reinterpret_cast(m_value.constData()); return nullptr; } else if constexpr (domTypeIsObjWrap(T::kindValue)) { return m_value.value(); } else { // need dependent static assert to not unconditially trigger static_assert(!std::is_same_v, "wrapping of unexpected type"); return nullptr; // necessary to avoid warnings on INTEGRITY } } T *mutableAsT() { if (domTypeIsValueWrap(T::kindValue)) { if (m_value.metaType() == QMetaType::fromType()) return reinterpret_cast(m_value.data()); return nullptr; } else if constexpr (domTypeIsObjWrap(T::kindValue)) { return m_value.value(); } else { Q_ASSERT_X(false, "SimpleObjectWrap", "wrapping of unexpected type"); return nullptr; } } void copyTo(SimpleObjectWrapBase *target) const override { static_assert(sizeof(SimpleObjectWrapBase) == sizeof(SimpleObjectWrapT), "Size mismatch in SimpleObjectWrapT"); static_assert(alignof(SimpleObjectWrapBase) == alignof(SimpleObjectWrapT), "Size mismatch in SimpleObjectWrapT"); new (target) SimpleObjectWrapT(*this); } void moveTo(SimpleObjectWrapBase *target) const override { static_assert(sizeof(SimpleObjectWrapBase) == sizeof(SimpleObjectWrapT), "Size mismatch in SimpleObjectWrapT"); static_assert(alignof(SimpleObjectWrapBase) == alignof(SimpleObjectWrapT), "Size mismatch in SimpleObjectWrapT"); new (target) SimpleObjectWrapT(std::move(*this)); } SimpleObjectWrapT(Path pathFromOwner, QVariant v, quintptr idValue, SimpleWrapOptions o) : SimpleObjectWrapBase(pathFromOwner, v, idValue, T::kindValue, o) { Q_ASSERT(domTypeIsValueWrap(T::kindValue) == bool(o & SimpleWrapOption::ValueType)); } }; class QMLDOM_EXPORT SimpleObjectWrap { public: constexpr static DomType kindValue = DomType::SimpleObjectWrap; SimpleObjectWrapBase *operator->() { return wrap.data(); } const SimpleObjectWrapBase *operator->() const { return wrap.data(); } SimpleObjectWrapBase &operator*() { return *wrap.data(); } const SimpleObjectWrapBase &operator*() const { return *wrap.data(); } template static SimpleObjectWrap fromObjectRef(Path pathFromOwner, T &value) { return SimpleObjectWrap(pathFromOwner, value); } SimpleObjectWrap() = delete; private: template SimpleObjectWrap(Path pathFromOwner, T &value) { using BaseT = std::decay_t; if constexpr (domTypeIsObjWrap(BaseT::kindValue)) { new (wrap.data()) SimpleObjectWrapT(pathFromOwner, QVariant::fromValue(&value), quintptr(&value), SimpleWrapOption::None); } else if constexpr (domTypeIsValueWrap(BaseT::kindValue)) { new (wrap.data()) SimpleObjectWrapT(pathFromOwner, QVariant::fromValue(value), quintptr(0), SimpleWrapOption::ValueType); } else { qCWarning(domLog) << "Unexpected object to wrap in SimpleObjectWrap: " << domTypeToString(BaseT::kindValue); Q_ASSERT_X(false, "SimpleObjectWrap", "simple wrap of unexpected object"); // allow? (mocks for testing,...) new (wrap.data()) SimpleObjectWrapT(pathFromOwner, nullptr, 0, SimpleWrapOption::None); } } SubclassStorage wrap; }; class QMLDOM_EXPORT Reference final : public DomElement { Q_GADGET public: constexpr static DomType kindValue = DomType::Reference; DomType kind() const override { return kindValue; } Reference *operator->() { return this; } const Reference *operator->() const { return this; } Reference &operator*() { return *this; } const Reference &operator*() const { return *this; } bool shouldCache() const; Reference(Path referredObject = Path(), Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation()); quintptr id() const override; bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; DomItem field(DomItem &self, QStringView name) const override; QList fields(DomItem &self) const override; index_type indexes(DomItem &) const override { return 0; } DomItem index(DomItem &, index_type) const override; QSet const keys(DomItem &) const override { return {}; } DomItem key(DomItem &, QString) const override; DomItem get(DomItem &self, ErrorHandler h = nullptr, QList *visitedRefs = nullptr) const; QList getAll(DomItem &self, ErrorHandler h = nullptr, QList *visitedRefs = nullptr) const; Path referredObjectPath; }; using ElementT = std::variant< Empty, Map, List, ListP, ConstantData, SimpleObjectWrap, Reference, GlobalComponent *, JsResource *, QmlComponent *, QmltypesComponent *, EnumDecl *, MockObject *, ModuleScope *, AstComments *, AttachedInfo *, DomEnvironment *, DomUniverse *, ExternalItemInfoBase *, ExternalItemPairBase *, GlobalScope *, JsFile *, QmlDirectory *, QmlFile *, QmldirFile *, QmlObject *, QmltypesFile *, LoadInfo *, MockOwner *, ModuleIndex *, ScriptExpression *>; using TopT = std::variant, std::shared_ptr>; using OwnerT = std::variant, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr>; inline bool emptyChildrenVisitor(Path, DomItem &, bool) { return true; } class MutableDomItem; class QMLDOM_EXPORT DomItem { Q_DECLARE_TR_FUNCTIONS(DomItem); public: using Callback = function; using InternalKind = DomType; using Visitor = function_ref; using ChildrenVisitor = function_ref; static ErrorGroup domErrorGroup; static ErrorGroups myErrors(); static ErrorGroups myResolveErrors(); static DomItem empty; enum class CopyOption { EnvConnected, EnvDisconnected }; template auto visitMutableEl(F f) { return std::visit(f, this->m_element); } template auto visitEl(F f) { return std::visit(f, this->m_element); } explicit operator bool() const { return m_kind != DomType::Empty; } InternalKind internalKind() const { return m_kind; } QString internalKindStr() const { return domTypeToString(internalKind()); } DomKind domKind() const { if (m_kind == DomType::ConstantData) return std::get(m_element).domKind(); else return kind2domKind(m_kind); } Path canonicalPath(); DomItem filterUp(function_ref filter, FilterUpOptions options); DomItem containingObject(); DomItem container(); DomItem owner(); DomItem top(); DomItem environment(); DomItem universe(); DomItem qmlObject(GoTo option = GoTo::Strict, FilterUpOptions options = FilterUpOptions::ReturnOuter); DomItem fileObject(GoTo option = GoTo::Strict); DomItem rootQmlObject(GoTo option = GoTo::Strict); DomItem globalScope(); DomItem component(GoTo option = GoTo::Strict); DomItem scope(FilterUpOptions options = FilterUpOptions::ReturnOuter); // convenience getters DomItem get(ErrorHandler h = nullptr, QList *visitedRefs = nullptr); QList getAll(ErrorHandler h = nullptr, QList *visitedRefs = nullptr); bool isOwningItem() { return domTypeIsOwningItem(internalKind()); } bool isExternalItem() { return domTypeIsExternalItem(internalKind()); } bool isTopItem() { return domTypeIsTopItem(internalKind()); } bool isContainer() { return domTypeIsContainer(internalKind()); } bool isScope() { return domTypeIsScope(internalKind()); } bool isCanonicalChild(DomItem &child); bool hasAnnotations(); QString name() { return field(Fields::name).value().toString(); } DomItem pragmas() { return field(Fields::pragmas); } DomItem ids() { return field(Fields::ids); } QString idStr() { return field(Fields::idStr).value().toString(); } DomItem propertyInfos() { return field(Fields::propertyInfos); } PropertyInfo propertyInfoWithName(QString name); QSet propertyInfoNames(); DomItem propertyDefs() { return field(Fields::propertyDefs); } DomItem bindings() { return field(Fields::bindings); } DomItem methods() { return field(Fields::methods); } DomItem enumerations() { return field(Fields::enumerations); } DomItem children() { return field(Fields::children); } DomItem child(index_type i) { return field(Fields::children).index(i); } DomItem annotations() { if (hasAnnotations()) return field(Fields::annotations); else return DomItem(); } bool resolve(Path path, Visitor visitor, ErrorHandler errorHandler, ResolveOptions options = ResolveOption::None, Path fullPath = Path(), QList *visitedRefs = nullptr); DomItem operator[](Path path); DomItem operator[](QStringView component); DomItem operator[](const QString &component); DomItem operator[](const char16_t *component) { return (*this)[QStringView(component)]; } // to avoid clash with stupid builtin ptrdiff_t[DomItem&], coming from C DomItem operator[](index_type i) { return index(i); } DomItem operator[](int i) { return index(i); } index_type size() { return indexes() + keys().size(); } index_type length() { return size(); } DomItem path(Path p, ErrorHandler h = &defaultErrorHandler); DomItem path(QString p, ErrorHandler h = &defaultErrorHandler); DomItem path(QStringView p, ErrorHandler h = &defaultErrorHandler); QList fields(); DomItem field(QStringView name); index_type indexes(); DomItem index(index_type); bool visitIndexes(function_ref visitor); QSet keys(); QStringList sortedKeys(); DomItem key(QString name); DomItem key(QStringView name) { return key(name.toString()); } bool visitKeys(function_ref visitor); QList values(); void writeOutPre(OutWriter &lw); void writeOut(OutWriter &lw); void writeOutPost(OutWriter &lw); DomItem writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks); DomItem writeOut(QString path, int nBackups = 2, const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr, WriteOutChecks extraChecks = WriteOutCheck::Default); bool visitTree(Path basePath, ChildrenVisitor visitor, VisitOptions options = VisitOption::Default, ChildrenVisitor openingVisitor = emptyChildrenVisitor, ChildrenVisitor closingVisitor = emptyChildrenVisitor); bool visitPrototypeChain(function_ref visitor, VisitPrototypesOptions options = VisitPrototypesOption::Normal, ErrorHandler h = nullptr, QSet *visited = nullptr, QList *visitedRefs = nullptr); bool visitDirectAccessibleScopes(function_ref visitor, VisitPrototypesOptions options = VisitPrototypesOption::Normal, ErrorHandler h = nullptr, QSet *visited = nullptr, QList *visitedRefs = nullptr); bool visitStaticTypePrototypeChains(function_ref visitor, VisitPrototypesOptions options = VisitPrototypesOption::Normal, ErrorHandler h = nullptr, QSet *visited = nullptr, QList *visitedRefs = nullptr); bool visitScopeChain(function_ref visitor, LookupOptions = LookupOption::Normal, ErrorHandler h = nullptr, QSet *visited = nullptr, QList *visitedRefs = nullptr); bool visitLocalSymbolsNamed(QString name, function_ref visitor); QSet localSymbolNames(); bool visitLookup1(QString symbolName, function_ref visitor, LookupOptions = LookupOption::Normal, ErrorHandler h = nullptr, QSet *visited = nullptr, QList *visitedRefs = nullptr); bool visitLookup(QString symbolName, function_ref visitor, LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal, ErrorHandler errorHandler = nullptr, QSet *visited = nullptr, QList *visitedRefs = nullptr); bool visitSubSymbolsNamed(QString name, function_ref visitor); DomItem proceedToScope(ErrorHandler h = nullptr, QList *visitedRefs = nullptr); QList lookup(QString symbolName, LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal, ErrorHandler errorHandler = nullptr); DomItem lookupFirst(QString symbolName, LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal, ErrorHandler errorHandler = nullptr); quintptr id(); Path pathFromOwner(); QString canonicalFilePath(); DomItem fileLocationsTree(); DomItem fileLocations(); MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected); bool commitToBase(); DomItem refreshed() { return top().path(canonicalPath()); } QCborValue value(); void dumpPtr(Sink sink); void dump(Sink, int indent = 0, function_ref filter = noFilter); FileWriter::Status dump(QString path, function_ref filter = noFilter, int nBackups = 2, int indent = 0, FileWriter *fw = nullptr); QString toString(); QString toString() const { DomItem self = *this; return self.toString(); } // OwnigItem elements int derivedFrom(); int revision(); QDateTime createdAt(); QDateTime frozenAt(); QDateTime lastDataUpdateAt(); void addError(ErrorMessage msg); ErrorHandler errorHandler(); void clearErrors(ErrorGroups groups = ErrorGroups({}), bool iterate = true); // return false if a quick exit was requested bool iterateErrors(function_ref visitor, bool iterate, Path inPath = Path()); bool iterateSubOwners(function_ref visitor); bool iterateDirectSubpaths(DirectVisitor v); template DomItem subDataItem(const PathEls::PathComponent &c, T value, ConstantData::Options options = ConstantData::Options::MapIsMap); template DomItem subDataItemField(QStringView f, T value, ConstantData::Options options = ConstantData::Options::MapIsMap) { return subDataItem(PathEls::Field(f), value, options); } template DomItem subValueItem(const PathEls::PathComponent &c, T value, ConstantData::Options options = ConstantData::Options::MapIsMap); template bool dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, T value, ConstantData::Options options = ConstantData::Options::MapIsMap); template bool dvValueField(DirectVisitor visitor, QStringView f, T value, ConstantData::Options options = ConstantData::Options::MapIsMap) { return this->dvValue(visitor, PathEls::Field(f), value, options); } template bool dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF, ConstantData::Options options = ConstantData::Options::MapIsMap); template bool dvValueLazyField(DirectVisitor visitor, QStringView f, F valueF, ConstantData::Options options = ConstantData::Options::MapIsMap) { return this->dvValueLazy(visitor, PathEls::Field(f), valueF, options); } DomItem subLocationItem(const PathEls::PathComponent &c, SourceLocation loc, QStringView code = QStringView()) { return this->subDataItem(c, locationToData(loc, code)); } // bool dvSubReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path // referencedObject); DomItem subReferencesItem(const PathEls::PathComponent &c, QList paths); DomItem subReferenceItem(const PathEls::PathComponent &c, Path referencedObject); bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path referencedObject) { return dvItem(visitor, c, [c, this, referencedObject]() { return this->subReferenceItem(c, referencedObject); }); } bool dvReferences(DirectVisitor visitor, const PathEls::PathComponent &c, QList paths) { return dvItem(visitor, c, [c, this, paths]() { return this->subReferencesItem(c, paths); }); } bool dvReferenceField(DirectVisitor visitor, QStringView f, Path referencedObject) { return dvReference(visitor, PathEls::Field(f), referencedObject); } bool dvReferencesField(DirectVisitor visitor, QStringView f, QList paths) { return dvReferences(visitor, PathEls::Field(f), paths); } bool dvItem(DirectVisitor visitor, const PathEls::PathComponent &c, function_ref it) { return visitor(c, it); } bool dvItemField(DirectVisitor visitor, QStringView f, function_ref it) { return dvItem(visitor, PathEls::Field(f), it); } DomItem subListItem(const List &list); DomItem subMapItem(const Map &map); DomItem subObjectWrapItem(SimpleObjectWrap obj) { return DomItem(m_top, m_owner, m_ownerPath, obj); } template DomItem subOwnerItem(const PathEls::PathComponent &c, Owner o) { if constexpr (domTypeIsUnattachedOwningItem(Owner::element_type::kindValue)) return DomItem(m_top, o, canonicalPath().appendComponent(c), o.get()); else return DomItem(m_top, o, Path(), o.get()); } template DomItem wrap(const PathEls::PathComponent &c, T &obj); template DomItem wrapField(QStringView f, T &obj) { return wrap(PathEls::Field(f), obj); } template bool dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj); template bool dvWrapField(DirectVisitor visitor, QStringView f, T &obj) { return dvWrap(visitor, PathEls::Field(f), obj); } DomItem() = default; DomItem(std::shared_ptr); DomItem(std::shared_ptr); static DomItem fromCode(QString code, DomType fileType = DomType::QmlFile); void loadFile(QString filePath, QString logicalPath, std::function callback, LoadOptions loadOptions, std::optional fileType = std::optional()); void loadFile(QString canonicalFilePath, QString logicalPath, QString code, QDateTime codeDate, std::function callback, LoadOptions loadOptions, std::optional fileType = std::optional()); void loadModuleDependency(QString uri, Version v, std::function callback = nullptr, ErrorHandler = nullptr); void loadBuiltins(std::function callback = nullptr, ErrorHandler = nullptr); void loadPendingDependencies(); // --- start of potentially dangerous stuff, make private? --- std::shared_ptr topPtr(); std::shared_ptr owningItemPtr(); // keep the DomItem around to ensure that it doesn't get deleted template, bool>::type = true> T const *as() { if (m_kind == T::kindValue) { if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue)) return std::get(m_element)->as(); else return static_cast(base()); } return nullptr; } template, bool>::type = true> T const *as() { if (m_kind == T::kindValue) { Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind)); return std::get(m_element)->as(); } return nullptr; } template std::shared_ptr ownerAs(); template DomItem copy(Owner owner, Path ownerPath, T base) { Q_ASSERT(m_top); static_assert(IsInlineDom>::value, "Expected an inline item or pointer"); return DomItem(m_top, owner, ownerPath, base); } template DomItem copy(Owner owner, Path ownerPath) { Q_ASSERT(m_top); return DomItem(m_top, owner, ownerPath, owner.get()); } template DomItem copy(T base) { Q_ASSERT(m_top); using BaseT = std::decay_t; static_assert(!std::is_same_v, "variant not supported, pass in the stored types"); static_assert(IsInlineDom::value, "expected either a pointer or an inline item"); if constexpr (IsSharedPointerToDomObject::value) { return DomItem(m_top, base, Path(), base.get()); } else { return DomItem(m_top, m_owner, m_ownerPath, base); } } private: DomBase const *base(); template ::value, bool>::type = true> T *mutableAs() { if (m_kind == T::kindValue) { if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue)) return static_cast(mutableBase())->mutableAs(); else return static_cast(mutableBase()); } return nullptr; } template ::value, bool>::type = true> T *mutableAs() { if (m_kind == T::kindValue) { Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind)); return static_cast(mutableBase())->mutableAs(); } return nullptr; } DomBase *mutableBase(); template DomItem(Env, Owner, Path, std::nullptr_t) : DomItem() { } template>::value>> DomItem(Env env, Owner owner, Path ownerPath, T el) : m_top(env), m_owner(owner), m_ownerPath(ownerPath), m_element(el) { using BaseT = std::decay_t; if constexpr (std::is_pointer_v) { if (!el || el->kind() == DomType::Empty) { // avoid null ptr, and allow only a // single kind of Empty m_kind = DomType::Empty; m_top.reset(); m_owner.reset(); m_ownerPath = Path(); m_element = Empty(); } else { using DomT = std::remove_pointer_t; m_element = el; m_kind = DomT::kindValue; } } else { static_assert(!std::is_same_v, "variant not supported, pass in the internal type"); m_kind = el->kind(); } } friend class DomBase; friend class DomElement; friend class Map; friend class List; friend class QmlObject; friend class DomUniverse; friend class DomEnvironment; friend class ExternalItemInfoBase; friend class ConstantData; friend class MutableDomItem; friend class ScriptExpression; friend class AstComments; friend class AttachedInfo; friend class TestDomItem; friend QMLDOM_EXPORT bool operator==(const DomItem &, const DomItem &); DomType m_kind = DomType::Empty; std::optional m_top; std::optional m_owner; Path m_ownerPath; ElementT m_element = Empty(); }; QMLDOM_EXPORT bool operator==(const DomItem &o1, const DomItem &o2); inline bool operator!=(const DomItem &o1, const DomItem &o2) { return !(o1 == o2); } template Map Map::fromMultiMapRef( Path pathFromOwner, QMultiMap &mmap, std::function elWrapper) { return Map( pathFromOwner, [&mmap, elWrapper](DomItem &self, QString key) { auto it = mmap.find(key); auto end = mmap.cend(); if (it == end) return DomItem(); else { // special case single element (++it == end || it.key() != key)? QList values; while (it != end && it.key() == key) values.append(&(*it++)); ListP ll(self.pathFromOwner().appendComponent(PathEls::Key(key)), values, QString(), ListOptions::Reverse); return self.copy(ll); } }, [&mmap](DomItem &) { return QSet(mmap.keyBegin(), mmap.keyEnd()); }, QLatin1String(typeid(T).name())); } template Map Map::fromMapRef( Path pathFromOwner, QMap &map, std::function elWrapper) { return Map( pathFromOwner, [&map, elWrapper](DomItem &self, QString key) { if (!map.contains(key)) return DomItem(); else { return elWrapper(self, PathEls::Key(key), map[key]); } }, [&map](DomItem &) { return QSet(map.keyBegin(), map.keyEnd()); }, QLatin1String(typeid(T).name())); } template List List::fromQList( Path pathFromOwner, QList list, std::function elWrapper, ListOptions options) { index_type len = list.length(); if (options == ListOptions::Reverse) { return List( pathFromOwner, [list, elWrapper](DomItem &self, index_type i) mutable { if (i < 0 || i >= list.length()) return DomItem(); return elWrapper(self, PathEls::Index(i), list[list.length() - i - 1]); }, [len](DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name())); } else { return List( pathFromOwner, [list, elWrapper](DomItem &self, index_type i) mutable { if (i < 0 || i >= list.length()) return DomItem(); return elWrapper(self, PathEls::Index(i), list[i]); }, [len](DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name())); } } template List List::fromQListRef( Path pathFromOwner, QList &list, std::function elWrapper, ListOptions options) { if (options == ListOptions::Reverse) { return List( pathFromOwner, [&list, elWrapper](DomItem &self, index_type i) { if (i < 0 || i >= list.length()) return DomItem(); return elWrapper(self, PathEls::Index(i), list[list.length() - i - 1]); }, [&list](DomItem &) { return list.length(); }, nullptr, QLatin1String(typeid(T).name())); } else { return List( pathFromOwner, [&list, elWrapper](DomItem &self, index_type i) { if (i < 0 || i >= list.length()) return DomItem(); return elWrapper(self, PathEls::Index(i), list[i]); }, [&list](DomItem &) { return list.length(); }, nullptr, QLatin1String(typeid(T).name())); } } class QMLDOM_EXPORT OwningItem: public DomBase { protected: virtual std::shared_ptr doCopy(DomItem &self) const = 0; public: OwningItem(const OwningItem &o); OwningItem(int derivedFrom=0); OwningItem(int derivedFrom, QDateTime lastDataUpdateAt); OwningItem(const OwningItem &&) = delete; OwningItem &operator=(const OwningItem &&) = delete; static int nextRevision(); Path canonicalPath(DomItem &self) const override = 0; bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; std::shared_ptr makeCopy(DomItem &self) const { return doCopy(self); } Path pathFromOwner() const { return Path(); } Path pathFromOwner(DomItem &) const override final { return Path(); } DomItem containingObject(DomItem &self) const override; int derivedFrom() const; virtual int revision() const; QDateTime createdAt() const; virtual QDateTime lastDataUpdateAt() const; virtual void refreshedDataAt(QDateTime tNew); // explicit freeze handling needed? virtual bool frozen() const; virtual bool freeze(); QDateTime frozenAt() const; virtual void addError(DomItem &self, ErrorMessage msg); void addErrorLocal(ErrorMessage msg); void clearErrors(ErrorGroups groups = ErrorGroups({})); // return false if a quick exit was requested bool iterateErrors(DomItem &self, function_ref visitor, Path inPath = Path()); QMultiMap localErrors() const { QMutexLocker l(mutex()); return m_errors; } virtual bool iterateSubOwners(DomItem &self, function_ref visitor); QBasicMutex *mutex() const { return &m_mutex; } private: mutable QBasicMutex m_mutex; int m_derivedFrom; int m_revision; QDateTime m_createdAt; QDateTime m_lastDataUpdateAt; QDateTime m_frozenAt; QMultiMap m_errors; QMap m_errorsCounts; }; template std::shared_ptr DomItem::ownerAs() { if constexpr (domTypeIsOwningItem(T::kindValue)) { if (m_owner) { if constexpr (T::kindValue == DomType::AttachedInfo) { if (std::holds_alternative>(*m_owner)) return std::static_pointer_cast( std::get>(*m_owner)); } else if constexpr (T::kindValue == DomType::ExternalItemInfo) { if (std::holds_alternative>(*m_owner)) return std::static_pointer_cast( std::get>(*m_owner)); } else if constexpr (T::kindValue == DomType::ExternalItemPair) { if (std::holds_alternative>(*m_owner)) return std::static_pointer_cast( std::get>(*m_owner)); } else { if (std::holds_alternative>(*m_owner)) { return std::get>(*m_owner); } } } } else { Q_ASSERT_X(false, "DomItem::ownerAs", "unexpected non owning value in ownerAs"); } return std::shared_ptr {}; } template struct rank : rank { static_assert(I > 0, ""); }; template<> struct rank<0> { }; template auto writeOutWrap(const T &t, DomItem &self, OutWriter &lw, rank<1>) -> decltype(t.writeOut(self, lw)) { t.writeOut(self, lw); } template auto writeOutWrap(const T &, DomItem &, OutWriter &, rank<0>) -> void { qCWarning(writeOutLog) << "Ignoring writeout to wrapped object not supporting it (" << typeid(T).name(); } template auto writeOutWrap(const T &t, DomItem &self, OutWriter &lw) -> void { writeOutWrap(t, self, lw, rank<1>()); } template void SimpleObjectWrapT::writeOut(DomItem &self, OutWriter &lw) const { writeOutWrap(*asT(), self, lw); } QMLDOM_EXPORT QDebug operator<<(QDebug debug, const DomItem &c); class QMLDOM_EXPORT MutableDomItem { public: using CopyOption = DomItem::CopyOption; explicit operator bool() const { return bool(m_owner); } // this is weaker than item(), but normally correct DomType internalKind() { return item().internalKind(); } QString internalKindStr() { return domTypeToString(internalKind()); } DomKind domKind() { return kind2domKind(internalKind()); } Path canonicalPath() { return m_owner.canonicalPath().path(m_pathFromOwner); } MutableDomItem containingObject() { if (m_pathFromOwner) return MutableDomItem(m_owner, m_pathFromOwner.split().pathToSource); else { DomItem cObj = m_owner.containingObject(); return MutableDomItem(cObj.owner(), (domTypeIsOwningItem(cObj.internalKind()) ? Path() :cObj.pathFromOwner())); } } MutableDomItem container() { if (m_pathFromOwner) return MutableDomItem(m_owner, m_pathFromOwner.dropTail()); else { return MutableDomItem(item().container()); } } MutableDomItem qmlObject(GoTo option = GoTo::Strict, FilterUpOptions fOptions = FilterUpOptions::ReturnOuter) { return MutableDomItem(item().qmlObject(option, fOptions)); } MutableDomItem fileObject(GoTo option = GoTo::Strict) { return MutableDomItem(item().fileObject(option)); } MutableDomItem rootQmlObject(GoTo option = GoTo::Strict) { return MutableDomItem(item().rootQmlObject(option)); } MutableDomItem globalScope() { return MutableDomItem(item().globalScope()); } MutableDomItem scope() { return MutableDomItem(item().scope()); } MutableDomItem component(GoTo option = GoTo::Strict) { return MutableDomItem { item().component(option) }; } MutableDomItem owner() { return MutableDomItem(m_owner); } MutableDomItem top() { return MutableDomItem(item().top()); } MutableDomItem environment() { return MutableDomItem(item().environment()); } MutableDomItem universe() { return MutableDomItem(item().universe()); } Path pathFromOwner() { return m_pathFromOwner; } MutableDomItem operator[](const Path &path) { return MutableDomItem(item()[path]); } MutableDomItem operator[](QStringView component) { return MutableDomItem(item()[component]); } MutableDomItem operator[](const QString &component) { return MutableDomItem(item()[component]); } MutableDomItem operator[](const char16_t *component) { // to avoid clash with stupid builtin ptrdiff_t[MutableDomItem&], coming from C return MutableDomItem(item()[QStringView(component)]); } MutableDomItem operator[](index_type i) { return MutableDomItem(item().index(i)); } MutableDomItem path(const Path &p) { return MutableDomItem(item().path(p)); } MutableDomItem path(const QString &p) { return path(Path::fromString(p)); } MutableDomItem path(QStringView p) { return path(Path::fromString(p)); } QList const fields() { return item().fields(); } MutableDomItem field(QStringView name) { return MutableDomItem(item().field(name)); } index_type indexes() { return item().indexes(); } MutableDomItem index(index_type i) { return MutableDomItem(item().index(i)); } QSet const keys() { return item().keys(); } MutableDomItem key(QString name) { return MutableDomItem(item().key(name)); } MutableDomItem key(QStringView name) { return key(name.toString()); } void dump(Sink s, int indent = 0, function_ref filter = noFilter) { item().dump(s, indent, filter); } FileWriter::Status dump(QString path, function_ref filter = noFilter, int nBackups = 2, int indent = 0, FileWriter *fw = nullptr) { return item().dump(path, filter, nBackups, indent, fw); } void writeOut(OutWriter &lw) { return item().writeOut(lw); } MutableDomItem writeOut(QString path, int nBackups = 2, const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr) { return MutableDomItem(item().writeOut(path, nBackups, opt, fw)); } MutableDomItem fileLocations() { return MutableDomItem(item().fileLocations()); } MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected) { return item().makeCopy(option); } bool commitToBase() { return item().commitToBase(); } QString canonicalFilePath() { return item().canonicalFilePath(); } MutableDomItem refreshed() { return MutableDomItem(item().refreshed()); } QCborValue value() { return item().value(); } QString toString() { return item().toString(); } // convenience getters QString name() { return item().name(); } MutableDomItem pragmas() { return item().pragmas(); } MutableDomItem ids() { return MutableDomItem::item().ids(); } QString idStr() { return item().idStr(); } MutableDomItem propertyDefs() { return MutableDomItem(item().propertyDefs()); } MutableDomItem bindings() { return MutableDomItem(item().bindings()); } MutableDomItem methods() { return MutableDomItem(item().methods()); } MutableDomItem children() { return MutableDomItem(item().children()); } MutableDomItem child(index_type i) { return MutableDomItem(item().child(i)); } MutableDomItem annotations() { return MutableDomItem(item().annotations()); } // // OwnigItem elements int derivedFrom() { return m_owner.derivedFrom(); } int revision() { return m_owner.revision(); } QDateTime createdAt() { return m_owner.createdAt(); } QDateTime frozenAt() { return m_owner.frozenAt(); } QDateTime lastDataUpdateAt() { return m_owner.lastDataUpdateAt(); } void addError(ErrorMessage msg) { item().addError(msg); } ErrorHandler errorHandler(); // convenience setters MutableDomItem addPrototypePath(Path prototypePath); MutableDomItem setNextScopePath(Path nextScopePath); MutableDomItem setPropertyDefs(QMultiMap propertyDefs); MutableDomItem setBindings(QMultiMap bindings); MutableDomItem setMethods(QMultiMap functionDefs); MutableDomItem setChildren(QList children); MutableDomItem setAnnotations(QList annotations); MutableDomItem setScript(std::shared_ptr exp); MutableDomItem setCode(QString code); MutableDomItem addPropertyDef(PropertyDefinition propertyDef, AddOption option = AddOption::Overwrite); MutableDomItem addBinding(Binding binding, AddOption option = AddOption::Overwrite); MutableDomItem addMethod(MethodInfo functionDef, AddOption option = AddOption::Overwrite); MutableDomItem addChild(QmlObject child); MutableDomItem addAnnotation(QmlObject child); MutableDomItem addPreComment(const Comment &comment, QString regionName = QString()); MutableDomItem addPreComment(const Comment &comment, QStringView regionName) { return addPreComment(comment, regionName.toString()); } MutableDomItem addPostComment(const Comment &comment, QString regionName = QString()); MutableDomItem addPostComment(const Comment &comment, QStringView regionName) { return addPostComment(comment, regionName.toString()); } MutableDomItem() = default; MutableDomItem(DomItem owner, Path pathFromOwner): m_owner(owner), m_pathFromOwner(pathFromOwner) {} MutableDomItem(DomItem item): m_owner(item.owner()), m_pathFromOwner(item.pathFromOwner()) {} std::shared_ptr topPtr() { return m_owner.topPtr(); } std::shared_ptr owningItemPtr() { return m_owner.owningItemPtr(); } template T const *as() { return item().as(); } template T *mutableAs() { Q_ASSERT(!m_owner || !m_owner.owningItemPtr()->frozen()); return item().mutableAs(); } template std::shared_ptr ownerAs() { return m_owner.ownerAs(); } // it is dangerous to assume it stays valid when updates are preformed... DomItem item() { return m_owner.path(m_pathFromOwner); } friend bool operator==(const MutableDomItem o1, const MutableDomItem &o2) { return o1.m_owner == o2.m_owner && o1.m_pathFromOwner == o2.m_pathFromOwner; } friend bool operator!=(const MutableDomItem &o1, const MutableDomItem &o2) { return !(o1 == o2); } private: DomItem m_owner; Path m_pathFromOwner; }; QMLDOM_EXPORT QDebug operator<<(QDebug debug, const MutableDomItem &c); template Path insertUpdatableElementInMultiMap(Path mapPathFromOwner, QMultiMap &mmap, K key, const T &value, AddOption option = AddOption::KeepExisting, T **valuePtr = nullptr) { if (option == AddOption::Overwrite) { auto it = mmap.find(key); if (it != mmap.end()) { T &v = *it; v = value; if (++it != mmap.end() && it.key() == key) { qWarning() << " requested overwrite of " << key << " that contains aleready multiple entries in" << mapPathFromOwner; } Path newPath = mapPathFromOwner.key(key).index(0); v.updatePathFromOwner(newPath); if (valuePtr) *valuePtr = &v; return newPath; } } mmap.insert(key, value); auto it = mmap.find(key); auto it2 = it; int nVal = 0; while (it2 != mmap.end() && it2.key() == key) { ++nVal; ++it2; } Path newPath = mapPathFromOwner.key(key).index(nVal-1); T &v = *it; v.updatePathFromOwner(newPath); if (valuePtr) *valuePtr = &v; return newPath; } template Path appendUpdatableElementInQList(Path listPathFromOwner, QList &list, const T &value, T **vPtr = nullptr) { int idx = list.length(); list.append(value); Path newPath = listPathFromOwner.index(idx); T &targetV = list[idx]; targetV.updatePathFromOwner(newPath); if (vPtr) *vPtr = &targetV; return newPath; } template void updatePathFromOwnerMultiMap(QMultiMap &mmap, Path newPath) { auto it = mmap.begin(); auto end = mmap.end(); index_type i = 0; K name; QList els; while (it != end) { if (i > 0 && name != it.key()) { Path pName = newPath.key(QString(name)); for (T *el : els) el->updatePathFromOwner(pName.index(--i)); els.clear(); els.append(&(*it)); name = it.key(); i = 1; } else { els.append(&(*it)); name = it.key(); ++i; } ++it; } Path pName = newPath.key(name); for (T *el : els) el->updatePathFromOwner(pName.index(--i)); } template void updatePathFromOwnerQList(QList &list, Path newPath) { auto it = list.begin(); auto end = list.end(); index_type i = 0; while (it != end) (it++)->updatePathFromOwner(newPath.index(i++)); } constexpr bool domTypeIsObjWrap(DomType k) { switch (k) { case DomType::Binding: case DomType::EnumItem: case DomType::ErrorMessage: case DomType::Export: case DomType::Id: case DomType::Import: case DomType::ImportScope: case DomType::MethodInfo: case DomType::MethodParameter: case DomType::ModuleAutoExport: case DomType::Pragma: case DomType::PropertyDefinition: case DomType::Version: case DomType::Comment: case DomType::CommentedElement: case DomType::RegionComments: case DomType::FileLocations: case DomType::UpdatedScriptExpression: return true; default: return false; } } constexpr bool domTypeIsValueWrap(DomType k) { switch (k) { case DomType::PropertyInfo: return true; default: return false; } } constexpr bool domTypeIsDomElement(DomType k) { switch (k) { case DomType::ModuleScope: case DomType::QmlObject: case DomType::ConstantData: case DomType::SimpleObjectWrap: case DomType::Reference: case DomType::Map: case DomType::List: case DomType::ListP: case DomType::EnumDecl: case DomType::JsResource: case DomType::QmltypesComponent: case DomType::QmlComponent: case DomType::GlobalComponent: case DomType::MockObject: return true; default: return false; } } constexpr bool domTypeIsOwningItem(DomType k) { switch (k) { case DomType::ModuleIndex: case DomType::MockOwner: case DomType::ExternalItemInfo: case DomType::ExternalItemPair: case DomType::QmlDirectory: case DomType::QmldirFile: case DomType::JsFile: case DomType::QmlFile: case DomType::QmltypesFile: case DomType::GlobalScope: case DomType::ScriptExpression: case DomType::AstComments: case DomType::LoadInfo: case DomType::AttachedInfo: case DomType::DomEnvironment: case DomType::DomUniverse: return true; default: return false; } } constexpr bool domTypeIsUnattachedOwningItem(DomType k) { switch (k) { case DomType::ScriptExpression: case DomType::AstComments: case DomType::AttachedInfo: return true; default: return false; } } template DomItem DomItem::subValueItem(const PathEls::PathComponent &c, T value, ConstantData::Options options) { using BaseT = std::remove_cv_t>; if constexpr ( std::is_base_of_v< QCborValue, BaseT> || std::is_base_of_v || std::is_base_of_v) { return DomItem(m_top, m_owner, m_ownerPath, ConstantData(pathFromOwner().appendComponent(c), value, options)); } else if constexpr (std::is_same_v) { Q_UNUSED(options); return value; } else if constexpr (IsList::value && !std::is_convertible_v) { return subListItem(List::fromQList( pathFromOwner().appendComponent(c), value, [options](DomItem &list, const PathEls::PathComponent &p, typename T::value_type &v) { return list.subValueItem(p, v, options); })); } else if constexpr (IsSharedPointerToDomObject::value) { Q_UNUSED(options); return subOwnerItem(c, value); } else { return subDataItem(c, value, options); } } template DomItem DomItem::subDataItem(const PathEls::PathComponent &c, T value, ConstantData::Options options) { using BaseT = std::remove_cv_t>; if constexpr (std::is_same_v) { return this->copy(value); } else if constexpr (std::is_base_of_v) { return DomItem(m_top, m_owner, m_ownerPath, ConstantData(pathFromOwner().appendComponent(c), value, options)); } else { return DomItem( m_top, m_owner, m_ownerPath, ConstantData(pathFromOwner().appendComponent(c), QCborValue(value), options)); } } template bool DomItem::dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, T value, ConstantData::Options options) { auto lazyWrap = [this, &c, &value, options]() { return this->subValueItem(c, value, options); }; return visitor(c, lazyWrap); } template bool DomItem::dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF, ConstantData::Options options) { auto lazyWrap = [this, &c, &valueF, options]() { return this->subValueItem(c, valueF(), options); }; return visitor(c, lazyWrap); } template DomItem DomItem::wrap(const PathEls::PathComponent &c, T &obj) { using BaseT = std::decay_t; if constexpr (std::is_same_v || std::is_arithmetic_v) { return this->subDataItem(c, QCborValue(obj)); } else if constexpr (std::is_same_v) { return this->subLocationItem(c, obj); } else if constexpr (std::is_same_v) { Q_ASSERT_X(false, "DomItem::wrap", "wrapping a reference object, probably an error (wrap the target path instead)"); return this->copy(obj); } else if constexpr (std::is_same_v) { return this->subDataItem(c, obj); } else if constexpr (std::is_same_v) { return this->subMapItem(obj); } else if constexpr (std::is_same_v) { return this->subListItem(obj); } else if constexpr (std::is_base_of_v) { return this->subListItem(obj); } else if constexpr (std::is_same_v) { return this->subObjectWrapItem(obj); } else if constexpr (IsDomObject::value) { if constexpr (domTypeIsObjWrap(BaseT::kindValue) || domTypeIsValueWrap(BaseT::kindValue)) { return this->subObjectWrapItem( SimpleObjectWrap::fromObjectRef(this->pathFromOwner().appendComponent(c), obj)); } else if constexpr (domTypeIsDomElement(BaseT::kindValue)) { return this->copy(&obj); } else { qCWarning(domLog) << "Unhandled object of type " << domTypeToString(BaseT::kindValue) << " in DomItem::wrap, not using a shared_ptr for an " << "OwningItem, or unexpected wrapped object?"; return DomItem(); } } else if constexpr (IsSharedPointerToDomObject::value) { if constexpr (domTypeIsOwningItem(BaseT::element_type::kindValue)) { return this->subOwnerItem(c, obj); } else { Q_ASSERT_X(false, "DomItem::wrap", "shared_ptr with non owning item"); return DomItem(); } } else if constexpr (IsMultiMap::value) { if constexpr (std::is_same_v) { return subMapItem(Map::fromMultiMapRef( pathFromOwner().appendComponent(c), obj, [](DomItem &map, const PathEls::PathComponent &p, typename BaseT::mapped_type &el) { return map.wrap(p, el); })); } else { Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)"); } } else if constexpr (IsMap::value) { if constexpr (std::is_same_v) { return subMapItem(Map::fromMapRef( pathFromOwner().appendComponent(c), obj, [](DomItem &map, const PathEls::PathComponent &p, typename BaseT::mapped_type &el) { return map.wrap(p, el); })); } else { Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)"); } } else if constexpr (IsList::value) { if constexpr (IsDomObject::value) { return subListItem(List::fromQListRef( pathFromOwner().appendComponent(c), obj, [](DomItem &list, const PathEls::PathComponent &p, typename BaseT::value_type &el) { return list.wrap(p, el); })); } else { Q_ASSERT_X(false, "DomItem::wrap", "Unsupported list type T"); return DomItem(); } } else { qCWarning(domLog) << "Cannot wrap " << typeid(BaseT).name(); Q_ASSERT_X(false, "DomItem::wrap", "Do not know how to wrap type T"); return DomItem(); } } template bool DomItem::dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj) { auto lazyWrap = [this, &c, &obj]() { return this->wrap(c, obj); }; return visitor(c, lazyWrap); } template bool ListPT::iterateDirectSubpaths(DomItem &self, DirectVisitor v) { index_type len = index_type(m_pList.size()); for (index_type i = 0; i < len; ++i) { if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, i); })) return false; } return true; } template DomItem ListPT::index(DomItem &self, index_type index) const { if (index >= 0 && index < m_pList.length()) return self.wrap(PathEls::Index(index), *reinterpret_cast(m_pList.value(index))); return DomItem(); } // allow inlining of DomBase inline DomKind DomBase::domKind() const { return kind2domKind(kind()); } inline bool DomBase::iterateDirectSubpathsConst(DomItem &self, DirectVisitor visitor) const { Q_ASSERT(self.base() == this); return self.iterateDirectSubpaths(visitor); } inline DomItem DomBase::containingObject(DomItem &self) const { Path path = pathFromOwner(self); DomItem base = self.owner(); if (!path) { path = canonicalPath(self); base = self; } Source source = path.split(); return base.path(source.pathToSource); } inline quintptr DomBase::id() const { return quintptr(this); } inline QString DomBase::typeName() const { return domTypeToString(kind()); } inline QList DomBase::fields(DomItem &self) const { QList res; self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref) { if (c.kind() == Path::Kind::Field) res.append(c.name()); return true; }); return res; } inline DomItem DomBase::field(DomItem &self, QStringView name) const { DomItem res; self.iterateDirectSubpaths( [&res, name](const PathEls::PathComponent &c, function_ref obj) { if (c.kind() == Path::Kind::Field && c.checkName(name)) { res = obj(); return false; } return true; }); return res; } inline index_type DomBase::indexes(DomItem &self) const { index_type res = 0; self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref) { if (c.kind() == Path::Kind::Index) { index_type i = c.index() + 1; if (res < i) res = i; } return true; }); return res; } inline DomItem DomBase::index(DomItem &self, qint64 index) const { DomItem res; self.iterateDirectSubpaths( [&res, index](const PathEls::PathComponent &c, function_ref obj) { if (c.kind() == Path::Kind::Index && c.index() == index) { res = obj(); return false; } return true; }); return res; } inline QSet const DomBase::keys(DomItem &self) const { QSet res; self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref) { if (c.kind() == Path::Kind::Key) res.insert(c.name()); return true; }); return res; } inline DomItem DomBase::key(DomItem &self, QString name) const { DomItem res; self.iterateDirectSubpaths( [&res, name](const PathEls::PathComponent &c, function_ref obj) { if (c.kind() == Path::Kind::Key && c.checkName(name)) { res = obj(); return false; } return true; }); return res; } inline DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, QList paths) { return subListItem( List::fromQList(pathFromOwner().appendComponent(c), paths, [](DomItem &list, const PathEls::PathComponent &p, Path &el) { return list.subReferenceItem(p, el); })); } inline DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, Path referencedObject) { if (domTypeIsOwningItem(internalKind())) return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, Path(c))); else return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, pathFromOwner().appendComponent(c))); } inline DomItem DomItem::subListItem(const List &list) { return DomItem(m_top, m_owner, m_ownerPath, list); } inline DomItem DomItem::subMapItem(const Map &map) { return DomItem(m_top, m_owner, m_ownerPath, map); } } // end namespace Dom } // end namespace QQmlJS QT_END_NAMESPACE #endif // QMLDOMITEM_H