Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmldomitem.cpp
Go to the documentation of this file.
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
5#include "qqmldomitem_p.h"
6#include "qqmldompath_p.h"
7#include "qqmldomtop_p.h"
8#include "qqmldomelements_p.h"
10#include "qqmldommock_p.h"
11#include "qqmldomastdumper_p.h"
12#include "qqmldomoutwriter_p.h"
13#include "qqmldomfilewriter_p.h"
15#include "qqmldomcompare_p.h"
16#include "qqmldomastdumper_p.h"
17#include "qqmldomlinewriter_p.h"
18#include "qqmldom_utils_p.h"
20
21#include <QtQml/private/qqmljslexer_p.h>
22#include <QtQml/private/qqmljsparser_p.h>
23#include <QtQml/private/qqmljsengine_p.h>
24#include <QtQml/private/qqmljsastvisitor_p.h>
25#include <QtQml/private/qqmljsast_p.h>
26
27#include <QtCore/QCborArray>
28#include <QtCore/QCborMap>
29#include <QtCore/QDebug>
30#include <QtCore/QDir>
31#include <QtCore/QFile>
32#include <QtCore/QFileInfo>
33#include <QtCore/QJsonDocument>
34#include <QtCore/QJsonValue>
35#include <QtCore/QMutexLocker>
36#include <QtCore/QPair>
37#include <QtCore/QRegularExpression>
38#include <QtCore/QScopeGuard>
39#include <QtCore/QtGlobal>
40#include <QtCore/QTimeZone>
41#include <optional>
42#include <type_traits>
43
45
46namespace QQmlJS {
47namespace Dom {
48
49Q_LOGGING_CATEGORY(writeOutLog, "qt.qmldom.writeOut", QtWarningMsg);
50static Q_LOGGING_CATEGORY(refLog, "qt.qmldom.ref", QtWarningMsg);
51
52template<class... TypeList>
53struct CheckDomElementT;
54
55template<class... Ts>
56struct CheckDomElementT<std::variant<Ts...>> : std::conjunction<IsInlineDom<Ts>...>
57{
58};
59
78static_assert(CheckDomElementT<ElementT>::value,
79 "Types in ElementT must either be a pointer to a class inheriting "
80 "from DomBase or (for internal Dom structures) implement a smart "
81 "pointer pointing to a class inheriting from DomBase");
82
83using std::shared_ptr;
135QMap<DomType,QString> domTypeToStringMap()
136{
137 static QMap<DomType,QString> map = [](){
138 QMetaEnum metaEnum = QMetaEnum::fromType<DomType>();
139 QMap<DomType,QString> res;
140 for (int i = 0; i < metaEnum.keyCount(); ++ i) {
141 res[DomType(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
142 }
143 return res;
144 }();
145 return map;
146}
147
148QString domTypeToString(DomType k)
149{
150 QString res = domTypeToStringMap().value(k);
151 if (res.isEmpty())
152 return QString::number(int(k));
153 else
154 return res;
155}
156
157QMap<DomKind, QString> domKindToStringMap()
158{
159 static QMap<DomKind, QString> map = []() {
160 QMetaEnum metaEnum = QMetaEnum::fromType<DomKind>();
161 QMap<DomKind, QString> res;
162 for (int i = 0; i < metaEnum.keyCount(); ++i) {
163 res[DomKind(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
164 }
165 return res;
166 }();
167 return map;
168}
169
171{
172 return domKindToStringMap().value(k, QString::number(int(k)));
173}
174
175bool domTypeIsExternalItem(DomType k)
176{
177 switch (k) {
179 case DomType::JsFile:
180 case DomType::QmlFile:
183 return true;
184 default:
185 return false;
186 }
187}
188
189bool domTypeIsTopItem(DomType k)
190{
191 switch (k) {
194 return true;
195 default:
196 return false;
197 }
198
199}
200
201bool domTypeIsContainer(DomType k)
202{
203 switch (k) {
204 case DomType::Map:
205 case DomType::List:
206 case DomType::ListP:
207 return true;
208 default:
209 return false;
210 }
211}
212
213bool domTypeIsScope(DomType k)
214{
215 switch (k) {
216 case DomType::QmlObject: // prop, methods,...
217 case DomType::ScriptExpression: // Js lexical scope
218 case DomType::QmlComponent: // (ids, enums -> qmlObj)
219 case DomType::QmlFile: // (components ->importScope)
220 case DomType::MethodInfo: // method arguments
221 case DomType::ImportScope: // (types, qualifiedImports)
222 case DomType::GlobalComponent: // global scope (enums -> qmlObj)
223 case DomType::JsResource: // js resurce (enums -> qmlObj)
224 case DomType::QmltypesComponent: // qmltypes component (enums -> qmlObj)
225 return true;
226 default:
227 return false;
228 }
229}
230
232{
233 auto parent = containingObject(self);
234 if (parent)
235 return parent.canonicalFilePath();
236 return QString();
237}
238
239void DomBase::writeOut(const DomItem &self, OutWriter &) const
240{
241 qCWarning(writeOutLog) << "Ignoring unsupported writeOut for " << domTypeToString(kind()) << ":"
242 << self.canonicalPath();
243}
244
245ConstantData::ConstantData(const Path &pathFromOwner, const QCborValue &value, Options options)
246 : DomElement(pathFromOwner), m_value(value), m_options(options)
247{}
248
249bool ConstantData::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
250{
251 static QHash<QString, QString> knownFields;
252 static QBasicMutex m;
253 auto toField = [](const QString &f) -> QStringView {
254 QMutexLocker l(&m);
255 if (!knownFields.contains(f))
256 knownFields[f] = f;
257 return knownFields[f];
258 };
259 if (m_value.isMap()) {
260 QCborMap map = m_value.toMap();
261 auto it = map.cbegin();
262 auto end = map.cend();
263 while (it != end) {
264 QString key = it.key().toString();
265 PathEls::PathComponent comp;
266 switch (m_options) {
267 case ConstantData::Options::MapIsMap:
268 comp = PathEls::Key(key);
269 break;
270 case ConstantData::Options::FirstMapIsFields:
271 comp = PathEls::Field(toField(key));
272 break;
273 }
274 auto val = it.value();
275 if (!self.dvValue(visitor, comp, val))
276 return false;
277 ++it;
278 }
279 return true;
280 } else if (m_value.isArray()){
281 QCborArray array = m_value.toArray();
282 auto it = array.cbegin();
283 auto end = array.cend();
284 index_type i = 0;
285 while (it != end) {
286 if (!self.dvValue(visitor, PathEls::Index(i++), *it++))
287 return false;
288 }
289 return true;
290 } else {
291 return true;
292 }
293}
294
295quintptr ConstantData::id() const
296{
297 return quintptr(0);
298}
299
300DomKind ConstantData::domKind() const
301{
302 if (m_value.isMap()) {
303 switch (m_options) {
304 case ConstantData::Options::MapIsMap:
305 return DomKind::Map;
306 case ConstantData::Options::FirstMapIsFields:
307 return DomKind::Object;
308 }
309 }
310 if (m_value.isArray())
311 return DomKind::List;
312 return DomKind::Value;
313}
350FileToLoad::FileToLoad(const std::weak_ptr<DomEnvironment> &environment,
351 const QString &canonicalPath, const QString &logicalPath,
352 const std::optional<InMemoryContents> &content)
353 : m_environment(environment),
354 m_canonicalPath(canonicalPath),
355 m_logicalPath(logicalPath),
356 m_content(content)
357{
358}
359
360FileToLoad FileToLoad::fromMemory(const std::weak_ptr<DomEnvironment> &environment,
361 const QString &path, const QString &code)
362{
364 return {
365 environment,
367 path,
368 InMemoryContents{ code },
369 };
370}
371
372FileToLoad FileToLoad::fromFileSystem(const std::weak_ptr<DomEnvironment> &environment,
373 const QString &path)
374{
375 // make the path canonical so the file content can be loaded from it later
377 return {
378 environment,
380 path,
381 std::nullopt,
382 };
383}
384
385ErrorGroup DomItem::domErrorGroup = NewErrorGroup("Dom");
387
388ErrorGroups DomItem::myErrors()
389{
390 static ErrorGroups res = {{domErrorGroup}};
391 return res;
392}
393
394ErrorGroups DomItem::myResolveErrors()
395{
396 static ErrorGroups res = {{domErrorGroup, NewErrorGroup("Resolve")}};
397 return res;
398}
399
400Path DomItem::canonicalPath() const
401{
402 Path res = visitEl([this](auto &&el) { return el->canonicalPath(*this); });
403 if (!(!res || res.headKind() == Path::Kind::Root)) {
404 qCWarning(domLog) << "non anchored canonical path:" << res.toString();
405 Q_ASSERT(false);
406 }
407 return res;
408
409}
410
412{
413 return visitEl([this](auto &&el) { return el->containingObject(*this); });
414}
415
422DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions) const
423{
424 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlObject; },
425 filterOptions))
426 return res;
427 if (options == GoTo::MostLikely) {
428 if (DomItem comp = component(options))
429 return comp.field(Fields::objects).index(0);
430 }
431 return DomItem();
432}
433
434DomItem DomItem::fileObject(GoTo options) const
435{
436 DomItem res = *this;
437 DomType k = res.internalKind();
438 if (k == DomType::List || k == DomType::Map) {
439 res = res.containingObject();
440 k = res.internalKind();
441 }
442 if (k == DomType::ExternalItemInfo || (options == GoTo::MostLikely && k == DomType::ExternalItemPair))
443 return field(Fields::currentItem);
444 res = owner();
445 k = res.internalKind();
446 while (k != DomType::Empty) {
447 if (k == DomType::QmlFile || k == DomType::QmldirFile || k == DomType::QmltypesFile
448 || k == DomType::JsFile)
449 break;
450 res = res.containingObject();
451 res = res.owner();
452 k = res.internalKind();
453 }
454 return res;
455}
456
457DomItem DomItem::rootQmlObject(GoTo options) const
458{
459 return qmlObject(options, FilterUpOptions::ReturnInner);
460}
461
463{
464 Path path = pathFromOwner();
465 if (!path)
467 Source s = path.split();
468 if (s.pathFromSource.length() > 1)
469 return containingObject().path(s.pathFromSource.dropTail());
470 return containingObject();
471}
472
474{
475 if (internalKind() == DomType::GlobalScope)
476 return *this;
477 DomItem env = environment();
478 if (shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
479 return env.copy(envPtr->ensureGlobalScopeWithName(env, envPtr->globalScopeName())->current,
480 Path());
481 }
482 return DomItem();
483}
484
490{
491 if (domTypeIsOwningItem(m_kind) || m_kind == DomType::Empty)
492 return *this;
493 return std::visit([this](auto &&el) {
494 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
495 return DomItem();
496 else
497 return DomItem(this->m_top, el, this->m_ownerPath, el.get());
498 }, m_owner);
499}
500
501DomItem DomItem::top() const
502{
503 if (domTypeIsTopItem(m_kind) || m_kind == DomType::Empty)
504 return *this;
505 return std::visit([](auto &&el) {
506 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
507 return DomItem();
508 else
509 return DomItem(el, el, Path(), el.get());
510 }, m_top);
511}
512
514{
515 DomItem res = top();
516 if (res.internalKind() == DomType::DomEnvironment)
517 return res;
518 return DomItem(); // we are in the universe, and cannot go back to the environment...
519}
520
522{
523 DomItem res = top();
524 if (res.internalKind() == DomType::DomUniverse)
525 return res;
526 if (res.internalKind() == DomType::DomEnvironment)
527 return res.field(Fields::universe);
528 return DomItem(); // we should be in an empty DomItem already...
529}
530
538{
539 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::ScriptExpression; },
540 FilterUpOptions::ReturnOuter))
541 return res;
542 return DomItem();
543}
544
552{
553 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlFile; },
554 FilterUpOptions::ReturnOuter))
555 return res;
556 return DomItem();
557}
558
565{
567 DomItem file =
568 top().field(Fields::qmlFileWithPath).key(canonicalPath).field(Fields::currentItem);
569 return file;
570}
571
576DomItem DomItem::goUp(int n) const
577{
578 Path path = canonicalPath();
579 // first entry of path is usually top(), and you cannot go up from top().
580 if (path.length() < n + 1)
581 return DomItem();
582
583 DomItem parent = top().path(path.dropTail(n));
584 return parent;
585}
586
592{
593 return goUp(1);
594}
595
601DomItem DomItem::filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const
602{
603 if (options == FilterUpOptions::ReturnOuter && filter(internalKind(), *this)) {
604 return *this;
605 }
606
607 switch (options) {
608 case FilterUpOptions::ReturnOuter:
609 case FilterUpOptions::ReturnOuterNoSelf: {
610 for (DomItem current = *this, previous = DomItem(); current;
611 previous = current, current = current.directParent()) {
612 if (filter(current.internalKind(), current)) {
613 if (options != FilterUpOptions::ReturnOuterNoSelf || current != *this)
614 return current;
615 }
616 }
617 break;
618 }
619 case FilterUpOptions::ReturnInner:
620 DomItem current = top();
621 for (const Path &currentPath : canonicalPath()) {
622 current = current.path(currentPath);
623 if (filter(current.internalKind(), current))
624 return current;
625 }
626 break;
627 }
628
629 return DomItem();
630}
631
632DomItem DomItem::scope(FilterUpOptions options) const
633{
634 DomItem res = filterUp([](DomType, const DomItem &el) { return el.isScope(); }, options);
635 return res;
636}
637
639{
641 visitUp([&scope](const DomItem &item) {
642 scope = item.semanticScope();
643 return !scope; // stop when scope was true
644 });
645 return scope;
646}
647
649{
650 QQmlJSScope::ConstPtr scope = std::visit(
651 [](auto &&e) -> QQmlJSScope::ConstPtr {
652 using T = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
653 if constexpr (std::is_same_v<T, const QmlObject *>) {
654 return e->semanticScope();
655 } else if constexpr (std::is_same_v<T, const QmlComponent *>) {
656 return e->semanticScope();
657 } else if constexpr (std::is_same_v<T, const QmltypesComponent *>) {
658 return e->semanticScope();
659 } else if constexpr (std::is_same_v<T, SimpleObjectWrap>) {
660 if (const MethodInfo *mi = e->template as<MethodInfo>()) {
661 return mi->semanticScope();
662 }
663 if (const auto *propertyDefinition = e->template as<PropertyDefinition>()) {
664 return propertyDefinition->semanticScope();
665 }
666 } else if constexpr (std::is_same_v<T, ScriptElementDomWrapper>) {
667 return e.element().base()->semanticScope();
668 }
669 return {};
670 },
671 m_element);
672 return scope;
673}
674
675DomItem DomItem::get(const ErrorHandler &h, QList<Path> *visitedRefs) const
676{
677 if (const Reference *refPtr = as<Reference>())
678 return refPtr->get(*this, h, visitedRefs);
679 return DomItem();
680}
681
682QList<DomItem> DomItem::getAll(const ErrorHandler &h, QList<Path> *visitedRefs) const
683{
684 if (const Reference *refPtr = as<Reference>())
685 return refPtr->getAll(*this, h, visitedRefs);
686 return {};
687}
688
690{
691 PropertyInfo pInfo;
692 visitPrototypeChain([&pInfo, name](const DomItem &obj) {
693 return obj.visitLocalSymbolsNamed(name, [&pInfo, name](const DomItem &el) {
694 switch (el.internalKind()) {
695 case DomType::Binding:
696 pInfo.bindings.append(el);
697 break;
698 case DomType::PropertyDefinition:
699 pInfo.propertyDefs.append(el);
700 break;
701 default:
702 break;
703 }
704 return true;
705 });
706 });
707 return pInfo;
708}
709
710QSet<QString> DomItem::propertyInfoNames() const
711{
712 QSet<QString> res;
713 visitPrototypeChain([&res](const DomItem &obj) {
714 res += obj.propertyDefs().keys();
715 res += obj.bindings().keys();
716 return true;
717 });
718 return res;
719}
720
721DomItem DomItem::component(GoTo options) const
722{
723 if (DomItem res = filterUp(
724 [](DomType kind, const DomItem &) {
725 return kind == DomType::QmlComponent || kind == DomType::QmltypesComponent
726 || kind == DomType::GlobalComponent;
727 },
728 FilterUpOptions::ReturnInner))
729 return res;
730 if (options == GoTo::MostLikely) {
731 DomItem item = *this;
732 DomType kind = item.internalKind();
733 if (kind == DomType::List || kind == DomType::Map) {
734 item = item.containingObject();
735 kind = item.internalKind();
736 }
737 switch (kind) {
738 case DomType::ExternalItemPair:
739 case DomType::ExternalItemInfo:
740 item = fileObject(options);
742 case DomType::QmlFile:
743 return item.field(Fields::components).key(QString()).index(0);
744 default:
745 break;
746 }
747 }
748 return DomItem();
749}
750
751struct ResolveToDo {
753 int pathIndex;
754};
755
756static QMap<LookupType, QString> lookupTypeToStringMap()
757{
758 static QMap<LookupType, QString> map = []() {
759 QMetaEnum metaEnum = QMetaEnum::fromType<LookupType>();
760 QMap<LookupType, QString> res;
761 for (int i = 0; i < metaEnum.keyCount(); ++i) {
762 res[LookupType(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
763 }
764 return res;
765 }();
766 return map;
767}
768
769bool DomItem::resolve(const Path &path, DomItem::Visitor visitor, const ErrorHandler &errorHandler,
770 ResolveOptions options, const Path &fullPath, QList<Path> *visitedRefs) const
771{
772 QList<Path> vRefs;
773 Path fPath = fullPath;
774 if (fullPath.length() == 0)
775 fPath = path;
776 if (path.length()==0)
777 return visitor(fPath, *this);
778 QList<QSet<quintptr>> visited(path.length() + 1);
779 Path myPath = path;
780 QVector<ResolveToDo> toDos(1); // invariant: always increase pathIndex to guarantee end even with only partial visited match
781 if (path.headKind() == Path::Kind::Root) {
782 DomItem root = *this;
783 PathRoot contextId = path.headRoot();
784 switch (contextId) {
785 case PathRoot::Modules:
786 root = root.environment().field(Fields::moduleIndexWithUri);
787 break;
788 case PathRoot::Cpp:
789 root = root.environment()[Fields::qmltypesFileWithPath];
790 break;
791 case PathRoot::Libs:
792 root = root.environment()[Fields::plugins];
793 break;
794 case PathRoot::Top:
795 root = root.top();
796 break;
797 case PathRoot::Env:
798 root = root.environment();
799 break;
800 case PathRoot::Universe:
801 root = root.environment()[u"universe"];
802 break;
803 case PathRoot::Other:
804 myResolveErrors().error(tr("Root context %1 is not known").arg(path.headName())).handle(errorHandler);
805 return false;
806 }
807 toDos[0] = {std::move(root), 1};
808 } else {
809 toDos[0] = {*this, 0};
810 }
811 while (!toDos.isEmpty()) {
812 const ResolveToDo toDo = toDos.takeLast();
813 {
814 auto idNow = toDo.item.id();
815 if (idNow == quintptr(0) && toDo.item == *this)
816 idNow = quintptr(this);
817 if (idNow != quintptr(0) && visited[0].contains(idNow))
818 continue;
819 }
820 int iPath = toDo.pathIndex;
821 DomItem it = toDo.item;
822 bool branchExhausted = false;
823 while (iPath < path.length() && it && !branchExhausted) {
824 auto idNow = it.id();
825 if (idNow == quintptr() && toDo.item == *this)
826 idNow = quintptr(this);
827 if (idNow != quintptr(0)) {
828 auto vPair = qMakePair(idNow, iPath);
829 if (visited[vPair.second].contains(vPair.first))
830 break;
831 visited[vPair.second].insert(vPair.first);
832 }
833 if (options & ResolveOption::TraceVisit && !visitor(path.mid(0,iPath), it))
834 return false;
835 auto cNow = path[iPath++];
836 switch (cNow.headKind()) {
837 case Path::Kind::Key:
838 it = it.key(cNow.headName());
839 break;
840 case Path::Kind::Field:
841 if (cNow.checkHeadName(Fields::get) && it.internalKind() == DomType::Reference) {
842 Path toResolve = it.as<Reference>()->referredObjectPath;
843 Path refRef = it.canonicalPath();
844 if (visitedRefs == nullptr) {
845 visitedRefs = &vRefs;
846 }
847 if (visitedRefs->contains(refRef)) {
848 myResolveErrors()
849 .error([visitedRefs, refRef](const Sink &sink) {
850 const QString msg = tr("Circular reference:") + QLatin1Char('\n');
851 sink(QStringView{msg});
852 for (const Path &vPath : *visitedRefs) {
853 sink(u" ");
854 vPath.dump(sink);
855 sink(u" >\n");
856 }
857 refRef.dump(sink);
858 })
859 .handle(errorHandler);
860 it = DomItem();
861 } else {
862 visitedRefs->append(refRef);
863 DomItem resolveRes;
864 it.resolve(
865 toResolve,
866 [&resolveRes](Path, const DomItem &r) {
867 resolveRes = r;
868 return false;
869 },
870 errorHandler, ResolveOption::None, toResolve, visitedRefs);
871 it = resolveRes;
872 }
873 } else {
874 it = it.field(cNow.headName()); // avoid instantiation of QString?
875 }
876 break;
877 case Path::Kind::Index:
878 it = it.index(cNow.headIndex());
879 break;
880 case Path::Kind::Empty:
881 {
882 // immediate expansion, might use extra memory, but simplifies code (no suspended evaluation)
883 Path toFind;
884 do {
885 if (iPath >= path.length()) {
886 myResolveErrors().warning(tr("Resolve with path ending with empty path, matches nothing."))
887 .handle(errorHandler);
888 branchExhausted = true; // allow and visit all?
889 break;
890 }
891 toFind = path[iPath++];
892 } while (toFind.headKind() == Path::Kind::Empty);
893 QVector<Path::Kind> validFind({Path::Kind::Key, Path::Kind::Field, Path::Kind::Field, Path::Kind::Index});
894 if (!validFind.contains(toFind.headKind())) {
895 myResolveErrors().error(tr("After an empty path only key, field or indexes are supported, not %1.").arg(toFind.toString()))
896 .handle(errorHandler);
897 branchExhausted = true; // allow and visit all?
898 return false;
899 }
900 if (!branchExhausted)
901 visitTree(
902 Path(),
903 [&toFind, &toDos, iPath](Path, const DomItem &item, bool) {
904 // avoid non directly attached?
905 DomItem newItem = item[toFind];
906 if (newItem)
907 toDos.append({ std::move(newItem), iPath });
908 return true;
909 },
910 VisitOption::VisitSelf | VisitOption::Recurse
911 | VisitOption::VisitAdopted | VisitOption::NoPath);
912 branchExhausted = true;
913 break;
914 }
915 case Path::Kind::Root:
916 myResolveErrors().error(tr("Root path is supported only at the beginning, and only once, found %1 at %2 in %3")
917 .arg(cNow.toString()).arg(iPath -1).arg(path.toString())).handle(errorHandler);
918 return false;
919 case Path::Kind::Current:
920 {
921 PathCurrent current = cNow.headCurrent();
922 switch (current) {
923 case PathCurrent::Other:
924 // todo
925 case PathCurrent::Obj:
926 if (domKind() != DomKind::Object)
927 it = it.containingObject();
928 break;
929 case PathCurrent::ObjChain: {
930 bool cont = it.visitPrototypeChain(
931 [&toDos, iPath](const DomItem &subEl) {
932 toDos.append({ subEl, iPath });
933 return true;
934 },
935 VisitPrototypesOption::Normal, errorHandler, nullptr,
936 visitedRefs); // avoid passing visitedRefs?
937 if (!cont)
938 return false;
939 branchExhausted = true;
940 break;
941 }
942 case PathCurrent::ScopeChain: {
943 bool cont = it.visitScopeChain(
944 [&toDos, iPath](const DomItem &subEl) {
945 toDos.append({ subEl, iPath });
946 return true;
947 },
948 LookupOption::Normal, errorHandler);
949 if (!cont)
950 return false;
951 branchExhausted = true;
952 break;
953 }
954 case PathCurrent::Component:
955 it = it.component();
956 break;
957 case PathCurrent::Module:
958 case PathCurrent::Ids:
959 it = it.component().ids();
960 break;
961 case PathCurrent::Types:
962 it = it.component()[Fields::exports];
963 break;
964 case PathCurrent::LookupStrict:
965 case PathCurrent::LookupDynamic:
966 case PathCurrent::Lookup: {
967 LookupOptions opt = LookupOption::Normal;
968 if (current == PathCurrent::Lookup) {
969 DomItem comp = it.component();
970 DomItem strict = comp.field(u"~strictLookup~");
971 if (!strict) {
972 DomItem env = it.environment();
973 strict = env.field(u"defaultStrictLookup");
974 }
975 if (strict && strict.value().toBool())
976 opt = opt | LookupOption::Strict;
977 } else if (current == PathCurrent::LookupStrict) {
978 opt = opt | LookupOption::Strict;
979 }
980 if (it.internalKind() == DomType::ScriptExpression) {
981 myResolveErrors()
982 .error(tr("Javascript lookups not yet implemented"))
983 .handle(errorHandler);
984 return false;
985 }
986 // enter lookup
987 auto idNow = it.id();
988 if (idNow == quintptr(0) && toDo.item == *this)
989 idNow = quintptr(this);
990 if (idNow != quintptr(0)) {
991 auto vPair = qMakePair(idNow, iPath);
992 if (visited[vPair.second].contains(vPair.first))
993 break;
994 visited[vPair.second].insert(vPair.first);
995 }
996 if (options & ResolveOption::TraceVisit && !visitor(path.mid(0, iPath), it))
997 return false;
998 if (iPath + 1 >= path.length()) {
999 myResolveErrors()
1000 .error(tr("Premature end of path, expected a field specifying the "
1001 "type, and a key specifying the name to search after a "
1002 "lookup directive in %2")
1003 .arg(path.toString()))
1004 .handle(errorHandler);
1005 return false;
1006 }
1007 Path cNow = path[iPath++];
1008 if (cNow.headKind() != Path::Kind::Field) {
1009 myResolveErrors()
1010 .error(tr("Expected a key path specifying the type to search after "
1011 "a lookup directive, not %1 at component %2 of %3")
1012 .arg(cNow.toString())
1013 .arg(iPath)
1014 .arg(path.toString()))
1015 .handle(errorHandler);
1016 return false;
1017 }
1018 QString expectedType = cNow.headName();
1019 LookupType lookupType = LookupType::Symbol;
1020 {
1021 bool found = false;
1022 auto m = lookupTypeToStringMap();
1023 auto it = m.begin();
1024 auto end = m.end();
1025 while (it != end) {
1026 if (it.value().compare(expectedType, Qt::CaseInsensitive) == 0) {
1027 lookupType = it.key();
1028 found = true;
1029 }
1030 ++it;
1031 }
1032 if (!found) {
1033 QString types;
1034 it = lookupTypeToStringMap().begin();
1035 while (it != end) {
1036 if (!types.isEmpty())
1037 types += QLatin1String("', '");
1038 types += it.value();
1039 ++it;
1040 }
1041 myResolveErrors()
1042 .error(tr("Type for lookup was expected to be one of '%1', not "
1043 "%2")
1044 .arg(types, expectedType))
1045 .handle(errorHandler);
1046 return false;
1047 }
1048 }
1049 cNow = path[iPath++];
1050 if (cNow.headKind() != Path::Kind::Key) {
1051 myResolveErrors()
1052 .error(tr("Expected a key specifying the path to search after the "
1053 "@lookup directive and type, not %1 at component %2 of "
1054 "%3")
1055 .arg(cNow.toString())
1056 .arg(iPath)
1057 .arg(path.toString()))
1058 .handle(errorHandler);
1059 return false;
1060 }
1061 QString target = cNow.headName();
1062 if (target.isEmpty()) {
1063 myResolveErrors()
1064 .warning(tr("Path with empty lookup at component %1 of %2 will "
1065 "match nothing in %3.")
1066 .arg(iPath)
1067 .arg(path.toString())
1068 .arg(it.canonicalPath().toString()))
1069 .handle(errorHandler);
1070 return true;
1071 }
1072 it.visitLookup(
1073 target,
1074 [&toDos, iPath](const DomItem &subEl) {
1075 toDos.append({ subEl, iPath });
1076 return true;
1077 },
1078 lookupType, opt, errorHandler, &(visited[iPath]), visitedRefs);
1079 branchExhausted = true;
1080 break;
1081 }
1082 }
1083 break;
1084 }
1085 case Path::Kind::Any:
1086 visitTree(
1087 Path(),
1088 [&toDos, iPath](Path, const DomItem &item, bool) {
1089 toDos.append({ item, iPath });
1090 return true;
1091 },
1092 VisitOption::VisitSelf | VisitOption::Recurse | VisitOption::VisitAdopted);
1093 branchExhausted = true;
1094 break;
1095 case Path::Kind::Filter:
1096 if (cNow.headFilter() && !cNow.headFilter()(it))
1097 branchExhausted = true;
1098 break;
1099 }
1100 }
1101 // visit the resolved path
1102 if (!branchExhausted && iPath == path.length() && !visitor(fPath, it))
1103 return false;
1104 }
1105 return true;
1106}
1107
1108DomItem DomItem::path(const Path &p, const ErrorHandler &errorHandler) const
1109{
1110 if (!p)
1111 return *this;
1112 DomItem res;
1113 resolve(p, [&res](const Path &, const DomItem &it) {
1114 res = it;
1115 return false;
1116 }, errorHandler);
1117 return res;
1118}
1119
1120DomItem DomItem::path(const QString &p, const ErrorHandler &errorHandler) const
1121{
1122 return path(Path::fromString(p, errorHandler));
1123}
1124
1125DomItem DomItem::path(QStringView p, const ErrorHandler &errorHandler) const
1126{
1127 return path(Path::fromString(p, errorHandler));
1128}
1129
1130QList<QString> DomItem::fields() const
1131{
1132 return visitEl([this](auto &&el) { return el->fields(*this); });
1133}
1134
1136{
1137 return visitEl([this, name](auto &&el) { return el->field(*this, name); });
1138}
1139
1141{
1142 return visitEl([this](auto &&el) { return el->indexes(*this); });
1143}
1144
1145DomItem DomItem::index(index_type i) const
1146{
1147 return visitEl([this, i](auto &&el) { return el->index(*this, i); });
1148}
1149
1150bool DomItem::visitIndexes(function_ref<bool(const DomItem &)> visitor) const
1151{
1152 // use iterateDirectSubpathsConst instead?
1153 int nIndexes = indexes();
1154 for (int i = 0; i < nIndexes; ++i) {
1155 DomItem v = index(i);
1156 if (!visitor(v))
1157 return false;
1158 }
1159 return true;
1160}
1161
1162QSet<QString> DomItem::keys() const
1163{
1164 return visitEl([this](auto &&el) { return el->keys(*this); });
1165}
1166
1168{
1169 QSet<QString> ks = keys();
1170 QStringList sortedKs(ks.begin(), ks.end());
1171 std::sort(sortedKs.begin(), sortedKs.end());
1172 return sortedKs;
1173}
1174
1175DomItem DomItem::key(const QString &name) const
1176{
1177 return visitEl([this, name](auto &&el) { return el->key(*this, name); });
1178}
1179
1180bool DomItem::visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const
1181{
1182 // use iterateDirectSubpathsConst instead?
1183 const QStringList keys = sortedKeys();
1184 for (const QString &k : keys) {
1185 DomItem v = key(k);
1186 if (!visitor(k, v))
1187 return false;
1188 }
1189 return true;
1190}
1191
1192QList<DomItem> DomItem::values() const
1193{
1194 QList<DomItem> res;
1195 visitEl([this, &res](const auto &el) {
1196 return el->iterateDirectSubpathsConst(
1197 *this, [&res](const PathEls::PathComponent &, function_ref<DomItem()> item) {
1198 res.append(item());
1199 return true;
1200 });
1201 });
1202 return res;
1203}
1204
1205void DomItem::writeOutPre(OutWriter &ow) const
1206{
1207 if (hasAnnotations()) {
1208 DomItem anns = field(Fields::annotations);
1209 for (const auto &ann : anns.values()) {
1210 if (ann.annotations().indexes() == 0) {
1211 ow.ensureNewline();
1212 ann.writeOut(ow);
1213 ow.ensureNewline();
1214 } else {
1215 DomItem annAnn = ann.annotations();
1216 Q_ASSERT_X(annAnn.indexes() == 1 && annAnn.index(0).name() == u"duplicate",
1217 "DomItem::writeOutPre", "Unexpected annotation of annotation");
1218 }
1219 }
1220 }
1221 ow.itemStart(*this);
1222}
1223
1224void DomItem::writeOut(OutWriter &ow) const
1225{
1226 writeOutPre(ow);
1227 visitEl([this, &ow](auto &&el) { el->writeOut(*this, ow); });
1228 writeOutPost(ow);
1229}
1230
1231void DomItem::writeOutPost(OutWriter &ow) const
1232{
1233 ow.itemEnd(*this);
1234}
1235
1236DomItem::WriteOutCheckResult DomItem::performWriteOutChecks(const DomItem &original, const DomItem &reformatted,
1237 OutWriter &ow,
1238 WriteOutChecks extraChecks) const
1239{
1240 QStringList dumped;
1241 auto maybeDump = [&ow, extraChecks, &dumped](const DomItem &obj, QStringView objName) {
1242 QString objDumpPath;
1243 if (extraChecks & WriteOutCheck::DumpOnFailure) {
1244 objDumpPath = QDir(QDir::tempPath())
1245 .filePath(objName.toString()
1246 + QFileInfo(ow.lineWriter.fileName()).baseName()
1247 + QLatin1String(".dump.json"));
1248 obj.dump(objDumpPath);
1249 dumped.append(objDumpPath);
1250 }
1251 return objDumpPath;
1252 };
1253 auto dumpedDumper = [&dumped](const Sink &s) {
1254 if (dumped.isEmpty())
1255 return;
1256 s(u"\ndump: ");
1257 for (const auto &dumpPath : dumped) {
1258 s(u" ");
1259 sinkEscaped(s, dumpPath);
1260 }
1261 };
1262 auto compare = [&maybeDump, &dumpedDumper, this](const DomItem &obj1, QStringView obj1Name,
1263 const DomItem &obj2, QStringView obj2Name,
1264 const FieldFilter &filter) {
1265 const auto diffList = domCompareStrList(obj1, obj2, filter, DomCompareStrList::AllDiffs);
1266 if (!diffList.isEmpty()) {
1267 maybeDump(obj1, obj1Name);
1268 maybeDump(obj2, obj2Name);
1269 qCWarning(writeOutLog).noquote().nospace()
1270 << obj2Name << " writeOut of " << this->canonicalFilePath() << " has changes:\n"
1271 << diffList.join(QString()) << dumpedDumper;
1272 return false;
1273 }
1274 return true;
1275 };
1276 auto checkStability = [&maybeDump, &dumpedDumper, &dumped, &ow,
1277 this](const QString &expected, const DomItem &obj, QStringView objName) {
1278 LineWriter lw2([](QStringView) {}, ow.lineWriter.fileName(), ow.lineWriter.options());
1279 OutWriter ow2(lw2);
1280 ow2.indentNextlines = true;
1281 obj.writeOut(ow2);
1282 ow2.eof();
1283 if (ow2.writtenStr != expected) {
1284 DomItem fObj = this->fileObject();
1285 maybeDump(fObj, u"initial");
1286 maybeDump(obj, objName);
1287 qCWarning(writeOutLog).noquote().nospace()
1288 << objName << " non stable writeOut of " << this->canonicalFilePath() << ":"
1289 << lineDiff(ow2.writtenStr, expected, 2) << dumpedDumper;
1290 dumped.clear();
1291 return false;
1292 }
1293 return true;
1294 };
1295
1296 if ((extraChecks & WriteOutCheck::UpdatedDomCompare)
1297 && !compare(original, u"initial", reformatted, u"reformatted",
1298 FieldFilter::noLocationFilter()))
1299 return WriteOutCheckResult::Failed;
1300
1301 if (extraChecks & WriteOutCheck::UpdatedDomStable) {
1302 checkStability(ow.writtenStr, reformatted, u"reformatted");
1303 }
1304
1305 if (extraChecks
1306 & (WriteOutCheck::Reparse | WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) {
1307 DomItem newEnv = environment().makeCopy().item();
1308 std::shared_ptr<DomEnvironment> newEnvPtr = newEnv.ownerAs<DomEnvironment>();
1309 if (!newEnvPtr)
1310 return WriteOutCheckResult::Failed;
1311
1312 auto newFilePtr = std::make_shared<QmlFile>(canonicalFilePath(), ow.writtenStr);
1313 if (!newFilePtr)
1314 return WriteOutCheckResult::Failed;
1315 newEnvPtr->addQmlFile(newFilePtr, AddOption::Overwrite);
1316
1317 DomItem newFile = newEnv.copy(newFilePtr, Path());
1318 if (newFilePtr->isValid()) {
1319 if (extraChecks & (WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) {
1320 newEnvPtr->populateFromQmlFile(newFile);
1321 if ((extraChecks & WriteOutCheck::ReparseCompare)
1322 && !compare(reformatted, u"reformatted", newFile, u"reparsed",
1323 FieldFilter::compareNoCommentsFilter()))
1324 return WriteOutCheckResult::Failed;
1325 if ((extraChecks & WriteOutCheck::ReparseStable))
1326 checkStability(ow.writtenStr, newFile, u"reparsed");
1327 }
1328 } else {
1329 const auto iterateErrors = [&newFile](const Sink &s) {
1330 newFile.iterateErrors(
1331 [s](const DomItem &, const ErrorMessage &msg) {
1332 s(u"\n ");
1333 msg.dump(s);
1334 return true;
1335 },
1336 true);
1337 s(u"\n"); // extra empty line at the end...
1338 };
1339 qCWarning(writeOutLog).noquote().nospace()
1340 << "writeOut of " << canonicalFilePath()
1341 << " created invalid code:\n----------\n"
1342 << ow.writtenStr << "\n----------" << iterateErrors;
1343 return WriteOutCheckResult::Failed;
1344 }
1345 }
1346 return WriteOutCheckResult::Success;
1347}
1348
1359bool DomItem::writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const
1360{
1361 ow.indentNextlines = true;
1362 writeOut(ow);
1363 ow.eof();
1364
1365 auto currentFileItem = fileObject();
1366 auto writtenFileItem = ow.restoreWrittenFileItem(currentFileItem);
1367 WriteOutCheckResult result = WriteOutCheckResult::Success;
1368 if (extraChecks & WriteOutCheck::All)
1369 result = performWriteOutChecks(currentFileItem, writtenFileItem, ow, extraChecks);
1370 return result == WriteOutCheckResult::Success ? bool(writtenFileItem) : false;
1371}
1372
1373bool DomItem::writeOut(const QString &path, int nBackups, const LineWriterOptions &options,
1374 FileWriter *fw, WriteOutChecks extraChecks) const
1375{
1376 FileWriter localFw;
1377 if (!fw)
1378 fw = &localFw;
1379 auto status = fw->write(
1380 path,
1381 [this, path, &options, extraChecks](QTextStream &ts) {
1382 LineWriter lw([&ts](QStringView s) { ts << s; }, path, options);
1383 OutWriter ow(lw);
1384 return writeOutForFile(ow, extraChecks);
1385 },
1386 nBackups);
1387 switch (status) {
1388 case FileWriter::Status::DidWrite:
1389 case FileWriter::Status::SkippedEqual:
1390 return true;
1391 case FileWriter::Status::ShouldWrite:
1392 case FileWriter::Status::SkippedDueToFailure:
1393 qCWarning(writeOutLog) << "failure reformatting " << path;
1394 return false;
1395 default:
1396 qCWarning(writeOutLog) << "Unknown FileWriter::Status ";
1397 Q_ASSERT(false);
1398 return false;
1399 }
1400}
1401
1402bool DomItem::isCanonicalChild(const DomItem &item) const
1403{
1404 bool isChild = false;
1405 if (item.isOwningItem()) {
1406 isChild = canonicalPath() == item.canonicalPath().dropTail();
1407 } else {
1408 DomItem itemOw = item.owner();
1409 DomItem selfOw = owner();
1410 isChild = itemOw == selfOw && item.pathFromOwner().dropTail() == pathFromOwner();
1411 }
1412 return isChild;
1413}
1414
1415bool DomItem::hasAnnotations() const
1416{
1417 bool hasAnnotations = false;
1418 DomType iKind = internalKind();
1419 switch (iKind) {
1420 case DomType::Id:
1421 if (const Id *myPtr = as<Id>())
1422 hasAnnotations = !myPtr->annotations.isEmpty();
1423 break;
1424 case DomType::PropertyDefinition:
1425 if (const PropertyDefinition *myPtr = as<PropertyDefinition>())
1426 hasAnnotations = !myPtr->annotations.isEmpty();
1427 break;
1428 case DomType::MethodInfo:
1429 if (const MethodInfo *myPtr = as<MethodInfo>())
1430 hasAnnotations = !myPtr->annotations.isEmpty();
1431 break;
1432 case DomType::QmlObject:
1433 if (const QmlObject *myPtr = as<QmlObject>())
1434 hasAnnotations = !myPtr->annotations().isEmpty();
1435 break;
1436 case DomType::Binding:
1437 if (const Binding *myPtr = as<Binding>())
1438 hasAnnotations = !myPtr->annotations().isEmpty();
1439 break;
1440 default:
1441 break;
1442 }
1443 return hasAnnotations;
1444}
1445
1468bool DomItem::visitTree(const Path &basePath, DomItem::ChildrenVisitor visitor,
1469 VisitOptions options, DomItem::ChildrenVisitor openingVisitor,
1470 DomItem::ChildrenVisitor closingVisitor, const FieldFilter &filter) const
1471{
1472 if (!*this)
1473 return true;
1474 if (options & VisitOption::VisitSelf && !visitor(basePath, *this, true))
1475 return false;
1476 if (options & VisitOption::VisitSelf && !openingVisitor(basePath, *this, true))
1477 return true;
1478 auto atEnd = qScopeGuard([closingVisitor, basePath, this, options]() {
1479 if (options & VisitOption::VisitSelf) {
1480 closingVisitor(basePath, *this, true);
1481 }
1482 });
1483 return visitEl([this, basePath, visitor, openingVisitor, closingVisitor, options,
1484 &filter](auto &&el) {
1485 return el->iterateDirectSubpathsConst(
1486 *this,
1487 [this, basePath, visitor, openingVisitor, closingVisitor, options,
1488 &filter](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) {
1489 Path pNow;
1490 if (!(options & VisitOption::NoPath)) {
1491 pNow = basePath;
1492 pNow = pNow.appendComponent(c);
1493 }
1494 if (!filter(*this, c, DomItem{}))
1495 return true;
1496 DomItem item = itemF();
1497 bool directChild = isCanonicalChild(item);
1498 if (!directChild && !(options & VisitOption::VisitAdopted))
1499 return true;
1500 if (!directChild || !(options & VisitOption::Recurse)) {
1501 if (!visitor(pNow, item, directChild))
1502 return false;
1503 // give an option to avoid calling open/close when not recursing?
1504 // calling it always allows close to do the reverse looping (children before
1505 // parent)
1506 if (!openingVisitor(pNow, item, directChild))
1507 return true;
1508 closingVisitor(pNow, item, directChild);
1509 } else {
1510 return item.visitTree(pNow, visitor, options | VisitOption::VisitSelf,
1511 openingVisitor, closingVisitor, filter);
1512 }
1513 return true;
1514 });
1515 });
1516}
1517static bool visitPrototypeIndex(QList<DomItem> &toDo, const DomItem &current,
1518 const DomItem &derivedFromPrototype, const ErrorHandler &h,
1519 QList<Path> *visitedRefs, VisitPrototypesOptions options,
1520 const DomItem &prototype)
1521{
1522 Path elId = prototype.canonicalPath();
1523 if (visitedRefs->contains(elId))
1524 return true;
1525 else
1526 visitedRefs->append(elId);
1527 QList<DomItem> protos = prototype.getAll(h, visitedRefs);
1528 if (protos.isEmpty()) {
1529 if (std::shared_ptr<DomEnvironment> envPtr =
1530 derivedFromPrototype.environment().ownerAs<DomEnvironment>())
1531 if (!(envPtr->options() & DomEnvironment::Option::NoDependencies))
1532 derivedFromPrototype.myErrors()
1533 .warning(derivedFromPrototype.tr("could not resolve prototype %1 (%2)")
1534 .arg(current.canonicalPath().toString(),
1535 prototype.field(Fields::referredObjectPath)
1536 .value()
1537 .toString()))
1538 .withItem(derivedFromPrototype)
1539 .handle(h);
1540 } else {
1541 if (protos.size() > 1) {
1542 QStringList protoPaths;
1543 for (const DomItem &p : protos)
1544 protoPaths.append(p.canonicalPath().toString());
1545 derivedFromPrototype.myErrors()
1546 .warning(derivedFromPrototype
1547 .tr("Multiple definitions found, using first only, resolving "
1548 "prototype %1 (%2): %3")
1549 .arg(current.canonicalPath().toString(),
1550 prototype.field(Fields::referredObjectPath)
1551 .value()
1552 .toString(),
1553 protoPaths.join(QLatin1String(", "))))
1554 .withItem(derivedFromPrototype)
1555 .handle(h);
1556 }
1557 int nProtos = 1; // change to protos.length() to use all prototypes
1558 // (sloppier)
1559 for (int i = nProtos; i != 0;) {
1560 DomItem proto = protos.at(--i);
1561 if (proto.internalKind() == DomType::Export) {
1562 if (!(options & VisitPrototypesOption::ManualProceedToScope))
1563 proto = proto.proceedToScope(h, visitedRefs);
1564 toDo.append(proto);
1565 } else if (proto.internalKind() == DomType::QmlObject
1566 || proto.internalKind() == DomType::QmlComponent) {
1567 toDo.append(proto);
1568 } else {
1569 derivedFromPrototype.myErrors()
1570 .warning(derivedFromPrototype.tr("Unexpected prototype type %1 (%2)")
1571 .arg(current.canonicalPath().toString(),
1572 prototype.field(Fields::referredObjectPath)
1573 .value()
1574 .toString()))
1575 .withItem(derivedFromPrototype)
1576 .handle(h);
1577 }
1578 }
1579 }
1580 return true;
1581}
1582
1583bool DomItem::visitPrototypeChain(function_ref<bool(const DomItem &)> visitor,
1584 VisitPrototypesOptions options, const ErrorHandler &h,
1585 QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1586{
1587 QSet<quintptr> visitedLocal;
1588 if (!visited)
1589 visited = &visitedLocal;
1590 QList<Path> refsLocal;
1591 if (!visitedRefs)
1592 visitedRefs = &refsLocal;
1593 bool shouldVisit = !(options & VisitPrototypesOption::SkipFirst);
1594 DomItem current = qmlObject();
1595 if (!current) {
1596 myErrors().warning(tr("Prototype chain called outside object")).withItem(*this).handle(h);
1597 return true;
1598 }
1599 QList<DomItem> toDo({ current });
1600 while (!toDo.isEmpty()) {
1601 current = toDo.takeLast();
1602 current = current.proceedToScope(h, visitedRefs);
1603 if (visited->contains(current.id())) {
1604 // to warn about circular dependencies a purely local visited trace is required
1605 // as common ancestors of unrelated objects are valid and should be skipped
1606 // so we do not to warn unless requested
1607 if (options & VisitPrototypesOption::RevisitWarn)
1608 myErrors()
1609 .warning(tr("Detected multiple visit of %1 visiting prototypes of %2")
1610 .arg(current.canonicalPath().toString(),
1612 .withItem(*this)
1613 .handle(h);
1614 continue;
1615 }
1616 visited->insert(current.id());
1617 if (shouldVisit && !visitor(current))
1618 return false;
1619 shouldVisit = true;
1620 current.field(Fields::prototypes)
1621 .visitIndexes([&toDo, &current, this, &h, visitedRefs, options](const DomItem &el) {
1622 return visitPrototypeIndex(toDo, current, *this, h, visitedRefs, options, el);
1623 });
1624 }
1625 return true;
1626}
1627
1629 function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
1630 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1631{
1632 // these are the scopes one can access with the . operator from the current location
1633 // but currently not the attached types, which we should
1634 DomType k = internalKind();
1635 if (k == DomType::QmlObject)
1636 return visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1637 if (visited && id() != 0) {
1638 if (visited->contains(id()))
1639 return true;
1640 visited->insert(id());
1641 }
1642 if (k == DomType::Id || k == DomType::Reference || k == DomType::Export) {
1643 // we go to the scope if it is clearly defined
1644 DomItem v = proceedToScope(h, visitedRefs);
1645 if (v.internalKind() == DomType::QmlObject)
1646 return v.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1647 }
1648 if (k == DomType::Binding) {
1649 // from a binding we can get to its value if it is a object
1650 DomItem v = field(Fields::value);
1651 if (v.internalKind() == DomType::QmlObject)
1652 return v.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1653 }
1654 if (k == DomType::PropertyDefinition) {
1655 // from a property definition we go to the type stored in it
1656 DomItem t = field(Fields::type).proceedToScope(h, visitedRefs);
1657 if (t.internalKind() == DomType::QmlObject)
1658 return t.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1659 }
1660 if (!(options & VisitPrototypesOption::SkipFirst) && isScope() && !visitor(*this))
1661 return false;
1662 return true;
1663}
1664
1676 function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
1677 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1678{
1679 QSet<quintptr> visitedLocal;
1680 if (!visited)
1681 visited = &visitedLocal;
1682 DomItem current = qmlObject();
1683 DomItem comp = current.component();
1684 if (comp.field(Fields::isSingleton).value().toBool(false)
1685 && !current.visitPrototypeChain(visitor, options, h, visited, visitedRefs))
1686 return false;
1687 if (DomItem attachedT = current.component().field(Fields::attachedType).field(Fields::get))
1688 if (!attachedT.visitPrototypeChain(
1689 visitor, options & ~VisitPrototypesOptions(VisitPrototypesOption::SkipFirst), h,
1690 visited, visitedRefs))
1691 return false;
1692 return true;
1693}
1694
1698bool DomItem::visitUp(function_ref<bool(const DomItem &)> visitor) const
1699{
1700 Path p = canonicalPath();
1701 while (p.length() > 0) {
1702 DomItem current = top().path(p);
1703 if (!visitor(current))
1704 return false;
1705 p = p.dropTail();
1706 }
1707 return true;
1708}
1709
1714 function_ref<bool(const DomItem &)> visitor, LookupOptions options, const ErrorHandler &h,
1715 QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1716{
1717 QSet<quintptr> visitedLocal;
1718 if (!visited)
1719 visited = &visitedLocal;
1720 QList<Path> visitedRefsLocal;
1721 if (!visitedRefs)
1722 visitedRefs = &visitedRefsLocal;
1723 DomItem current = scope();
1724 if (!current) {
1725 myResolveErrors().warning(tr("Called visitScopeChain outside scopes")).handle(h);
1726 return true;
1727 }
1728 QList<DomItem> toDo { current };
1729 bool visitFirst = !(options & LookupOption::SkipFirstScope);
1730 bool visitCurrent = visitFirst;
1731 bool first = true;
1732 QSet<quintptr> alreadyAddedComponentMaps;
1733 while (!toDo.isEmpty()) {
1734 DomItem current = toDo.takeLast();
1735 if (visited->contains(current.id()))
1736 continue;
1737 visited->insert(current.id());
1738 if (visitCurrent && !visitor(current))
1739 return false;
1740 visitCurrent = true;
1741 switch (current.internalKind()) {
1742 case DomType::QmlObject: {
1743 if (!current.visitPrototypeChain(visitor, VisitPrototypesOption::SkipFirst, h, visited,
1744 visitedRefs))
1745 return false;
1746 DomItem root = current.rootQmlObject();
1747 if (root && root != current) {
1748 first = false;
1749 toDo.append(root);
1750 } else if (DomItem next = current.scope(
1751 FilterUpOptions::ReturnOuterNoSelf)) { // should be the component
1752 toDo.append(next);
1753 }
1754 } break;
1755 case DomType::ScriptExpression: // Js lexical scope
1756 first = false;
1757 if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
1758 toDo.append(next);
1759 break;
1760 case DomType::QmlComponent: { // ids/attached type
1761 if ((options & LookupOption::Strict) == 0) {
1762 if (DomItem comp = current.field(Fields::nextComponent))
1763 toDo.append(comp);
1764 }
1765 if (first && visitFirst && (options & LookupOption::VisitTopClassType)
1766 && *this == current) { // visit attached type if it is the top of the chain
1767 if (DomItem attachedT = current.field(Fields::attachedType).field(Fields::get))
1768 toDo.append(attachedT);
1769 }
1770 if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
1771 toDo.append(next);
1772
1773 DomItem owner = current.owner();
1774 Path pathToComponentMap = current.pathFromOwner().dropTail(2);
1775 DomItem componentMap = owner.path(pathToComponentMap);
1776 if (alreadyAddedComponentMaps.contains(componentMap.id()))
1777 break;
1778 alreadyAddedComponentMaps.insert(componentMap.id());
1779 const auto keys = componentMap.keys();
1780 for (const QString &x : keys) {
1781 DomItem componentList = componentMap.key(x);
1782 for (int i = 0; i < componentList.indexes(); ++i) {
1783 DomItem component = componentList.index(i);
1784 if (component != current && !visited->contains(component.id()))
1785 toDo.append(component);
1786 }
1787 }
1788 first = false;
1789 break;
1790 }
1791 case DomType::QmlFile: // subComponents, imported types
1792 if (DomItem iScope =
1793 current.field(Fields::importScope)) // treat file as a separate scope?
1794 toDo.append(iScope);
1795 first = false;
1796 break;
1797 case DomType::MethodInfo: // method arguments
1798 first = false;
1799 if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
1800 toDo.append(next);
1801 break;
1802 case DomType::ImportScope: // types
1803 first = false;
1804 if (auto globalC = globalScope().field(Fields::rootComponent))
1805 toDo.append(globalC);
1806 break;
1807 case DomType::JsResource:
1808 case DomType::GlobalComponent:
1809 first = false;
1810 if (DomItem next = current.field(Fields::objects).index(0))
1811 toDo.append(next);
1812 break;
1813 case DomType::QmltypesComponent:
1814 first = false;
1815 break;
1816 default:
1817 first = false;
1818 myResolveErrors()
1819 .error(tr("Unexpected non scope object %1 (%2) reached in visitScopeChain")
1820 .arg(domTypeToString(current.internalKind()),
1821 current.canonicalPath().toString()))
1822 .handle(h);
1823 Q_ASSERT(false);
1824 break;
1825 }
1826 }
1827 return true;
1828}
1829
1831 const QString &symbolName, function_ref<bool(const DomItem &)> visitor, LookupOptions opts,
1832 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1833{
1834 return visitScopeChain(
1835 [symbolName, visitor](const DomItem &obj) {
1836 return obj.visitLocalSymbolsNamed(symbolName,
1837 [visitor](const DomItem &el) { return visitor(el); });
1838 },
1839 opts, h, visited, visitedRefs);
1840}
1841
1842class CppTypeInfo
1843{
1844 Q_DECLARE_TR_FUNCTIONS(CppTypeInfo)
1845public:
1846 CppTypeInfo() = default;
1847
1848 static CppTypeInfo fromString(QStringView target, const ErrorHandler &h = nullptr)
1849 {
1850 CppTypeInfo res;
1852 uR"(QList<(?<list>[a-zA-Z_0-9:]+) *(?<listPtr>\*?)>|QMap< *(?<mapKey>[a-zA-Z_0-9:]+) *, *(?<mapValue>[a-zA-Z_0-9:]+) *(?<mapPtr>\*?)>|(?<baseType>[a-zA-Z_0-9:]+) *(?<ptr>\*?))"));
1853
1854 QRegularExpressionMatch m = reTarget.matchView(target);
1855 if (!m.hasMatch()) {
1857 .error(tr("Unexpected complex CppType %1").arg(target))
1858 .handle(h);
1859 }
1860 res.baseType = m.captured(u"baseType");
1861 res.isPointer = !m.captured(u"ptr").isEmpty();
1862 if (!m.captured(u"list").isEmpty()) {
1863 res.isList = true;
1864 res.baseType = m.captured(u"list");
1865 res.isPointer = !m.captured(u"listPtr").isEmpty();
1866 }
1867 if (!m.captured(u"mapValue").isEmpty()) {
1868 res.isMap = true;
1869 if (m.captured(u"mapKey") != u"QString") {
1871 .error(tr("Unexpected complex CppType %1 (map with non QString key)")
1872 .arg(target))
1873 .handle(h);
1874 }
1875 res.baseType = m.captured(u"mapValue");
1876 res.isPointer = !m.captured(u"mapPtr").isEmpty();
1877 }
1878 return res;
1879 }
1880
1881 QString baseType;
1882 bool isPointer = false;
1883 bool isMap = false;
1884 bool isList = false;
1885};
1886
1887static bool visitForLookupType(const DomItem &el, LookupType lookupType,
1888 function_ref<bool(const DomItem &)> visitor)
1889{
1890 bool correctType = false;
1891 DomType iType = el.internalKind();
1892 switch (lookupType) {
1893 case LookupType::Binding:
1894 correctType = (iType == DomType::Binding);
1895 break;
1896 case LookupType::Method:
1897 correctType = (iType == DomType::MethodInfo);
1898 break;
1899 case LookupType::Property:
1900 correctType = (iType == DomType::PropertyDefinition || iType == DomType::Binding);
1901 break;
1902 case LookupType::PropertyDef:
1903 correctType = (iType == DomType::PropertyDefinition);
1904 break;
1905 case LookupType::Type:
1906 correctType = (iType == DomType::Export); // accept direct QmlObject ref?
1907 break;
1908 default:
1909 Q_ASSERT(false);
1910 break;
1911 }
1912 if (correctType)
1913 return visitor(el);
1914 return true;
1915}
1916
1917static bool visitQualifiedNameLookup(
1918 const DomItem &newIt, const QStringList &subpath,
1919 function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
1920 const ErrorHandler &errorHandler, QList<Path> *visitedRefs)
1921{
1922 QVector<ResolveToDo> lookupToDos(
1923 { ResolveToDo{ newIt, 1 } }); // invariant: always increase pathIndex to guarantee
1924 // end even with only partial visited match
1925 QList<QSet<quintptr>> lookupVisited(subpath.size() + 1);
1926 while (!lookupToDos.isEmpty()) {
1927 ResolveToDo tNow = lookupToDos.takeFirst();
1928 auto vNow = qMakePair(tNow.item.id(), tNow.pathIndex);
1929 DomItem subNow = tNow.item;
1930 int iSubPath = tNow.pathIndex;
1931 Q_ASSERT(iSubPath < subpath.size());
1932 QString subPathNow = subpath[iSubPath++];
1933 DomItem scope = subNow.proceedToScope();
1934 if (iSubPath < subpath.size()) {
1935 if (vNow.first != 0) {
1936 if (lookupVisited[vNow.second].contains(vNow.first))
1937 continue;
1938 else
1939 lookupVisited[vNow.second].insert(vNow.first);
1940 }
1941 if (scope.internalKind() == DomType::QmlObject)
1942 scope.visitDirectAccessibleScopes(
1943 [&lookupToDos, &subPathNow, iSubPath](const DomItem &el) {
1944 return el.visitLocalSymbolsNamed(
1945 subPathNow, [&lookupToDos, iSubPath](const DomItem &subEl) {
1946 lookupToDos.append({ subEl, iSubPath });
1947 return true;
1948 });
1949 },
1950 VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
1951 visitedRefs);
1952 } else {
1953 bool cont = scope.visitDirectAccessibleScopes(
1954 [&visitor, &subPathNow, lookupType](const DomItem &el) -> bool {
1955 if (lookupType == LookupType::Symbol)
1956 return el.visitLocalSymbolsNamed(subPathNow, visitor);
1957 else
1958 return el.visitLocalSymbolsNamed(
1959 subPathNow, [lookupType, &visitor](const DomItem &el) -> bool {
1960 return visitForLookupType(el, lookupType, visitor);
1961 });
1962 },
1963 VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
1964 visitedRefs);
1965 if (!cont)
1966 return false;
1967 }
1968 }
1969 return true;
1970}
1971
1973 const QString &target, function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
1974 LookupOptions opts, const ErrorHandler &errorHandler, QSet<quintptr> *visited,
1975 QList<Path> *visitedRefs) const
1976{
1977 if (target.isEmpty())
1978 return true;
1979 switch (lookupType) {
1980 case LookupType::Binding:
1981 case LookupType::Method:
1982 case LookupType::Property:
1983 case LookupType::PropertyDef:
1984 case LookupType::Symbol:
1985 case LookupType::Type: {
1986 QStringList subpath = target.split(QChar::fromLatin1('.'));
1987 if (subpath.size() == 1) {
1988 return visitLookup1(subpath.first(), visitor, opts, errorHandler, visited, visitedRefs);
1989 } else {
1990 return visitLookup1(
1991 subpath.at(0),
1992 [&subpath, visitor, lookupType, &errorHandler,
1993 visitedRefs](const DomItem &newIt) -> bool {
1994 return visitQualifiedNameLookup(newIt, subpath, visitor, lookupType,
1995 errorHandler, visitedRefs);
1996 },
1997 opts, errorHandler, visited, visitedRefs);
1998 }
1999 break;
2000 }
2001 case LookupType::CppType: {
2002 QString baseTarget = CppTypeInfo::fromString(target, errorHandler).baseType;
2003 DomItem localQmltypes = owner();
2004 while (localQmltypes && localQmltypes.internalKind() != DomType::QmltypesFile) {
2005 localQmltypes = localQmltypes.containingObject();
2006 localQmltypes = localQmltypes.owner();
2007 }
2008 if (localQmltypes) {
2009 if (DomItem localTypes = localQmltypes.field(Fields::components).key(baseTarget)) {
2010 bool cont = localTypes.visitIndexes([&visitor](const DomItem &els) {
2011 return els.visitIndexes([&visitor](const DomItem &el) {
2012 if (DomItem obj = el.field(Fields::objects).index(0))
2013 return visitor(obj);
2014 return true;
2015 });
2016 });
2017 if (!cont)
2018 return false;
2019 }
2020 }
2021 DomItem qmltypes = environment().field(Fields::qmltypesFileWithPath);
2022 return qmltypes.visitKeys([baseTarget, &visitor](const QString &, const DomItem &els) {
2023 DomItem comps =
2024 els.field(Fields::currentItem).field(Fields::components).key(baseTarget);
2025 return comps.visitIndexes([&visitor](const DomItem &el) {
2026 if (DomItem obj = el.field(Fields::objects).index(0))
2027 return visitor(obj);
2028 return true;
2029 });
2030 });
2031 break;
2032 }
2033 }
2034 Q_ASSERT(false);
2035 return true;
2036}
2037
2046DomItem DomItem::proceedToScope(const ErrorHandler &h, QList<Path> *visitedRefs) const
2047{
2048 // follow references, resolve exports
2049 DomItem current = *this;
2050 while (current) {
2051 switch (current.internalKind()) {
2052 case DomType::Reference: {
2053 Path currentPath = current.canonicalPath();
2054 current = current.get(h, visitedRefs);
2055 break;
2056 }
2057 case DomType::Export:
2058 current = current.field(Fields::type);
2059 break;
2060 case DomType::Id:
2061 current = current.field(Fields::referredObject);
2062 break;
2063 default:
2064 return current.scope();
2065 break;
2066 }
2067 }
2068 return DomItem();
2069}
2070
2071QList<DomItem> DomItem::lookup(const QString &symbolName, LookupType type, LookupOptions opts,
2072 const ErrorHandler &errorHandler) const
2073{
2074 QList<DomItem> res;
2075 visitLookup(
2076 symbolName,
2077 [&res](const DomItem &el) {
2078 res.append(el);
2079 return true;
2080 },
2081 type, opts, errorHandler);
2082 return res;
2083}
2084
2085DomItem DomItem::lookupFirst(const QString &symbolName, LookupType type, LookupOptions opts,
2086 const ErrorHandler &errorHandler) const
2087{
2088 DomItem res;
2089 visitLookup(
2090 symbolName,
2091 [&res](const DomItem &el) {
2092 res = el;
2093 return false;
2094 },
2095 type, opts, errorHandler);
2096 return res;
2097}
2098
2099quintptr DomItem::id() const
2100{
2101 return visitEl([](auto &&b) { return b->id(); });
2102}
2103
2104Path DomItem::pathFromOwner() const
2105{
2106 return visitEl([this](auto &&e) { return e->pathFromOwner(*this); });
2107}
2108
2110{
2111 return visitEl([this](auto &&e) { return e->canonicalFilePath(*this); });
2112}
2113
2115{
2116 if (DomItem l = field(Fields::fileLocationsTree))
2117 return l;
2118 auto res = FileLocations::findAttachedInfo(*this);
2119 if (res && res.foundTreePath) {
2120 return copy(res.foundTree, res.foundTreePath);
2121 }
2122 return DomItem();
2123}
2124
2126{
2127 return fileLocationsTree().field(Fields::infoItem);
2128}
2129
2130MutableDomItem DomItem::makeCopy(DomItem::CopyOption option) const
2131{
2132 if (m_kind == DomType::Empty)
2133 return MutableDomItem();
2134 DomItem o = owner();
2135 if (option == CopyOption::EnvDisconnected) {
2136 DomItem newItem = std::visit([this, &o](auto &&el) {
2137 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
2138 return DomItem();
2139 } else {
2140 auto copyPtr = el->makeCopy(o);
2141 return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get());
2142 }
2143 }, m_owner);
2144 return MutableDomItem(newItem.path(pathFromOwner()));
2145 }
2146 DomItem env = environment();
2147 std::shared_ptr<DomEnvironment> newEnvPtr;
2148 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
2149 newEnvPtr = std::make_shared<DomEnvironment>(envPtr, envPtr->loadPaths(), envPtr->options(),
2150 envPtr->domCreationOptions());
2151 DomBase *eBase = envPtr.get();
2152 if (std::holds_alternative<const DomEnvironment *>(m_element) && eBase
2153 && std::get<const DomEnvironment *>(m_element) == eBase)
2154 return MutableDomItem(DomItem(newEnvPtr));
2155 } else if (std::shared_ptr<DomUniverse> univPtr = top().ownerAs<DomUniverse>()) {
2156 newEnvPtr = std::make_shared<DomEnvironment>(
2157 QStringList(),
2158 DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies,
2159 DomCreationOption::None, univPtr);
2160 } else {
2161 Q_ASSERT(false);
2162 return {};
2163 }
2164 DomItem newItem = std::visit(
2165 [this, newEnvPtr, &o](auto &&el) {
2166 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
2167 return DomItem();
2168 } else {
2169 auto copyPtr = el->makeCopy(o);
2170 return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get());
2171 }
2172 },
2173 m_owner);
2174
2175 switch (o.internalKind()) {
2176 case DomType::QmlDirectory:
2177 newEnvPtr->addQmlDirectory(newItem.ownerAs<QmlDirectory>(), AddOption::Overwrite);
2178 break;
2179 case DomType::JsFile:
2180 newEnvPtr->addJsFile(newItem.ownerAs<JsFile>(), AddOption::Overwrite);
2181 break;
2182 case DomType::QmlFile:
2183 newEnvPtr->addQmlFile(newItem.ownerAs<QmlFile>(), AddOption::Overwrite);
2184 break;
2185 case DomType::QmltypesFile:
2186 newEnvPtr->addQmltypesFile(newItem.ownerAs<QmltypesFile>(), AddOption::Overwrite);
2187 break;
2188 case DomType::GlobalScope: {
2189 newEnvPtr->addGlobalScope(newItem.ownerAs<GlobalScope>(), AddOption::Overwrite);
2190 } break;
2191 case DomType::ModuleIndex:
2192 case DomType::MockOwner:
2193 case DomType::ScriptExpression:
2194 case DomType::AstComments:
2195 case DomType::LoadInfo:
2196 case DomType::AttachedInfo:
2197 case DomType::DomEnvironment:
2198 case DomType::DomUniverse:
2199 qCWarning(domLog) << "DomItem::makeCopy " << internalKindStr()
2200 << " does not support binding to environment";
2201 Q_ASSERT(false);
2202 return MutableDomItem();
2203 default:
2204 qCWarning(domLog) << "DomItem::makeCopy(" << internalKindStr()
2205 << ") is not an known OwningItem";
2206 Q_ASSERT(o.isOwningItem());
2207 return MutableDomItem();
2208 }
2209 DomItem newEnv(newEnvPtr);
2210 Q_ASSERT(newEnv.path(o.canonicalPath()).m_owner == newItem.m_owner);
2211 return MutableDomItem(newItem.path(pathFromOwner()));
2212}
2213
2214bool DomItem::commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr) const
2215{
2216 DomItem env = environment();
2217 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
2218 return envPtr->commitToBase(env, validEnvPtr);
2219 }
2220 return false;
2221}
2222
2223bool DomItem::visitLocalSymbolsNamed(const QString &name, function_ref<bool(const DomItem &)> visitor) const
2224{
2225 if (name.isEmpty()) // no empty symbol
2226 return true;
2227 // we could avoid discriminating by type and just access all the needed stuff in the correct
2228 // sequence, making sure it is empty if not provided, but for now I find it clearer to check the
2229 // type
2230 DomItem f;
2231 DomItem v;
2232 switch (internalKind()) {
2233 case DomType::QmlObject:
2234 f = field(Fields::propertyDefs);
2235 v = f.key(name);
2236 if (!v.visitIndexes(visitor))
2237 return false;
2238 f = field(Fields::bindings);
2239 v = f.key(name);
2240 if (!v.visitIndexes(visitor))
2241 return false;
2242 f = field(Fields::methods);
2243 v = f.key(name);
2244 if (!v.visitIndexes(visitor))
2245 return false;
2246 break;
2247 case DomType::ScriptExpression:
2248 // to do
2249 break;
2250 case DomType::QmlComponent:
2251 f = field(Fields::ids);
2252 v = f.key(name);
2253 if (!v.visitIndexes(visitor))
2254 return false;
2255 Q_FALLTHROUGH();
2256 case DomType::JsResource:
2257 case DomType::GlobalComponent:
2258 case DomType::QmltypesComponent:
2259 f = field(Fields::enumerations);
2260 v = f.key(name);
2261 if (!v.visitIndexes(visitor))
2262 return false;
2263 break;
2264 case DomType::MethodInfo: {
2265 DomItem params = field(Fields::parameters);
2266 if (!params.visitIndexes([name, visitor](const DomItem &p) {
2267 const MethodParameter *pPtr = p.as<MethodParameter>();
2268 if (pPtr->name == name && !visitor(p))
2269 return false;
2270 return true;
2271 }))
2272 return false;
2273 break;
2274 }
2275 case DomType::QmlFile: {
2276 f = field(Fields::components);
2277 v = f.key(name);
2278 if (!v.visitIndexes(visitor))
2279 return false;
2280 break;
2281 }
2282 case DomType::ImportScope: {
2283 f = field(Fields::imported);
2284 v = f.key(name);
2285 if (!v.visitIndexes(visitor))
2286 return false;
2287 f = field(Fields::qualifiedImports);
2288 v = f.key(name);
2289 if (v && !visitor(v))
2290 return false;
2291 break;
2292 default:
2293 Q_ASSERT(!isScope());
2294 break;
2295 }
2296 }
2297 return true;
2298}
2299
2300DomItem DomItem::operator[](const QString &cName) const
2301{
2302 if (internalKind() == DomType::Map)
2303 return key(cName);
2304 return field(cName);
2305}
2306
2308{
2309 if (internalKind() == DomType::Map)
2310 return key(cName.toString());
2311 return field(cName);
2312}
2313
2314DomItem DomItem::operator[](const Path &p) const
2315{
2316 return path(p);
2317}
2318
2320{
2321 return base()->value();
2322}
2323
2324void DomItem::dumpPtr(const Sink &sink) const
2325{
2326 sink(u"DomItem{ topPtr:");
2327 sink(QString::number((quintptr)topPtr().get(), 16));
2328 sink(u", ownerPtr:");
2329 sink(QString::number((quintptr)owningItemPtr().get(), 16));
2330 sink(u", ownerPath:");
2331 m_ownerPath.dump(sink);
2332 sink(u", elPtr:");
2334 sink(u"}");
2335}
2336
2337void DomItem::dump(
2338 const Sink &s, int indent,
2339 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2340{
2341 visitEl([this, s, indent, filter](auto &&e) { e->dump(*this, s, indent, filter); });
2342}
2343
2344FileWriter::Status
2346 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter,
2347 int nBackups, int indent, FileWriter *fw) const
2348{
2349 FileWriter localFw;
2350 if (!fw)
2351 fw = &localFw;
2352 switch (fw->write(
2353 path,
2354 [this, indent, filter](QTextStream &ts) {
2355 this->dump([&ts](QStringView s) { ts << s; }, indent, filter);
2356 return true;
2357 },
2358 nBackups)) {
2359 case FileWriter::Status::ShouldWrite:
2360 case FileWriter::Status::SkippedDueToFailure:
2361 qWarning() << "Failure dumping " << canonicalPath() << " to " << path;
2362 break;
2363 case FileWriter::Status::DidWrite:
2364 case FileWriter::Status::SkippedEqual:
2365 break;
2366 }
2367 return fw->status;
2368}
2369
2371{
2372 return dumperToString([this](const Sink &s){ dump(s); });
2373}
2374
2375int DomItem::derivedFrom() const
2376{
2377 return std::visit([](auto &&ow) {
2378 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2379 return 0;
2380 else
2381 return ow->derivedFrom();
2382 }, m_owner);
2383}
2384
2385int DomItem::revision() const
2386{
2387 return std::visit([](auto &&ow) {
2388 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2389 return -1;
2390 else
2391 return ow->revision();
2392 }, m_owner);
2393}
2394
2396{
2397 return std::visit([](auto &&ow) {
2398 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2400 else
2401 return ow->createdAt();
2402 }, m_owner);
2403}
2404
2406{
2407 return std::visit([](auto &&ow) {
2408 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2410 else
2411 return ow->frozenAt();
2412 }, m_owner);
2413}
2414
2416{
2417 return std::visit([](auto &&ow) {
2418 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2420 else
2421 return ow->lastDataUpdateAt();
2422 }, m_owner);
2423}
2424
2425void DomItem::addError(ErrorMessage &&msg) const
2426{
2427 std::visit([this, &msg](auto &&ow) {
2428 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2429 defaultErrorHandler(msg.withItem(*this));
2430 else
2431 ow->addError(owner(), std::move(msg.withItem(*this)));
2432 }, m_owner);
2433}
2434
2436{
2437 // We need a copy here. Error handlers may be called when this is gone.
2438 return [self = *this](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
2439}
2440
2441void DomItem::clearErrors(const ErrorGroups &groups, bool iterate) const
2442{
2443 std::visit([&groups](auto &&ow) {
2444 if constexpr (!std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2445 ow->clearErrors(groups);
2446 }, m_owner);
2447
2448 if (iterate) {
2449 iterateSubOwners([groups](const DomItem &i){
2450 i.clearErrors(groups, true);
2451 return true;
2452 });
2453 }
2454}
2455
2457 function_ref<bool(const DomItem &, const ErrorMessage &)> visitor, bool iterate,
2458 Path inPath) const
2459{
2460 if (!std::visit([this, visitor, inPath](auto &&el) {
2461 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2462 return true;
2463 else
2464 return el->iterateErrors(owner(), visitor, inPath);
2465 }, m_owner)) {
2466 return false;
2467 }
2468
2469 if (iterate && !iterateSubOwners([inPath, visitor](const DomItem &i) {
2470 return i.iterateErrors(visitor, true, inPath);
2471 })) {
2472 return false;
2473 }
2474
2475 return true;
2476}
2477
2478bool DomItem::iterateSubOwners(function_ref<bool(const DomItem &)> visitor) const
2479{
2480 return std::visit([this, visitor](auto &&o) {
2481 if constexpr (std::is_same_v<std::decay_t<decltype(o)>, std::monostate>)
2482 return true;
2483 else
2484 return o->iterateSubOwners(owner(), visitor);
2485 }, m_owner);
2486}
2487
2488bool DomItem::iterateDirectSubpaths(DirectVisitor v) const
2489{
2490 return visitEl(
2491 [this, v](auto &&el) { return el->iterateDirectSubpaths(*this, v); });
2492}
2493
2494DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const
2495{
2496 return subListItem(
2497 List::fromQList<Path>(pathFromOwner().appendComponent(c), paths,
2498 [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
2499 return list.subReferenceItem(p, el);
2500 }));
2501}
2502
2503DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const
2504{
2505 if (domTypeIsOwningItem(internalKind())) {
2506 return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, Path(c)));
2507 } else {
2508 return DomItem(m_top, m_owner, m_ownerPath,
2509 Reference(referencedObject, pathFromOwner().appendComponent(c)));
2510 }
2511}
2512
2513shared_ptr<DomTop> DomItem::topPtr() const
2514{
2515 return std::visit([](auto &&el) -> shared_ptr<DomTop> {
2516 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2517 return {};
2518 else
2519 return el;
2520 }, m_top);
2521}
2522
2523shared_ptr<OwningItem> DomItem::owningItemPtr() const
2524{
2525 return std::visit([](auto &&el) -> shared_ptr<OwningItem> {
2526 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2527 return {};
2528 else
2529 return el;
2530 }, m_owner);
2531}
2532
2537const DomBase *DomItem::base() const
2538{
2539 return visitEl([](auto &&el) -> const DomBase * { return el->domBase(); });
2540}
2541
2542DomItem::DomItem(const std::shared_ptr<DomEnvironment> &envPtr):
2543 DomItem(envPtr, envPtr, Path(), envPtr.get())
2544{
2545}
2546
2547DomItem::DomItem(const std::shared_ptr<DomUniverse> &universePtr):
2548 DomItem(universePtr, universePtr, Path(), universePtr.get())
2549{
2550}
2551
2560DomItem DomItem::fromCode(const QString &code, DomType fileType)
2561{
2562 if (code.isEmpty())
2563 return DomItem();
2564 auto env =
2565 DomEnvironment::create(QStringList(),
2568
2569 DomItem tFile;
2570
2571 env->loadFile(
2572 FileToLoad::fromMemory(env, QString(), code),
2573 [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
2574 std::make_optional(fileType));
2575 env->loadPendingDependencies();
2576 return tFile.fileObject();
2577}
2578
2579Empty::Empty()
2580{}
2581
2582Path Empty::pathFromOwner(const DomItem &) const
2583{
2584 return Path();
2585}
2586
2587Path Empty::canonicalPath(const DomItem &) const
2588{
2589 return Path();
2590}
2591
2592bool Empty::iterateDirectSubpaths(const DomItem &, DirectVisitor) const
2593{
2594 return true;
2595}
2596
2597DomItem Empty::containingObject(const DomItem &self) const
2598{
2599 return self;
2600}
2601
2602void Empty::dump(
2603 const DomItem &, const Sink &s, int,
2604 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const
2605{
2606 s(u"null");
2607}
2608
2609Map::Map(const Path &pathFromOwner, const Map::LookupFunction &lookup,
2610 const Keys &keys, const QString &targetType)
2611 : DomElement(pathFromOwner), m_lookup(lookup), m_keys(keys), m_targetType(targetType)
2612{}
2613
2614quintptr Map::id() const
2615{
2616 return quintptr(0);
2617}
2618
2619bool Map::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2620{
2621 QSet<QString> ksSet = keys(self);
2622 QStringList ksList = QStringList(ksSet.begin(), ksSet.end());
2623 std::sort(ksList.begin(), ksList.end());
2624 for (const QString &k : std::as_const(ksList)) {
2625 if (!visitor(PathEls::Key(k), [&self, this, k]() { return key(self, k); }))
2626 return false;
2627 }
2628 return true;
2629}
2630
2631const QSet<QString> Map::keys(const DomItem &self) const
2632{
2633 return m_keys(self);
2634}
2635
2636DomItem Map::key(const DomItem &self, const QString &name) const
2637{
2638 return m_lookup(self, name);
2639}
2640
2641void DomBase::dump(
2642 const DomItem &self, const Sink &sink, int indent,
2643 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2644{
2645 bool comma = false;
2646 DomKind dK = self.domKind();
2647 switch (dK) {
2648 case DomKind::Object:
2649 sink(u"{ \"~type~\":");
2651 comma = true;
2652 break;
2653 case DomKind::Value:
2654 {
2655 QJsonValue v = value().toJsonValue();
2656 if (v.isString())
2657 sinkEscaped(sink, v.toString());
2658 else if (v.isBool())
2659 if (v.toBool())
2660 sink(u"true");
2661 else
2662 sink(u"false");
2663 else if (v.isDouble())
2664 if (value().isInteger())
2665 sink(QString::number(value().toInteger()));
2666 else
2667 sink(QString::number(v.toDouble()));
2668 else {
2669 sink(QString::fromUtf8(QJsonDocument::fromVariant(v.toVariant()).toJson()));
2670 }
2671 break;
2672 }
2673 case DomKind::Empty:
2674 sink(u"null");
2675 break;
2676 case DomKind::List:
2677 sink(u"[");
2678 break;
2679 case DomKind::Map:
2680 sink(u"{");
2681 break;
2682 case DomKind::ScriptElement:
2683 // nothing to print
2684 break;
2685 }
2686 auto closeParens = qScopeGuard(
2687 [dK, sink, indent]{
2688 switch (dK) {
2689 case DomKind::Object:
2690 sinkNewline(sink, indent);
2691 sink(u"}");
2692 break;
2693 case DomKind::Value:
2694 break;
2695 case DomKind::Empty:
2696 break;
2697 case DomKind::List:
2698 sinkNewline(sink, indent);
2699 sink(u"]");
2700 break;
2701 case DomKind::Map:
2702 sinkNewline(sink, indent);
2703 sink(u"}");
2704 break;
2705 case DomKind::ScriptElement:
2706 // nothing to print
2707 break;
2708 }
2709 });
2710 index_type idx = 0;
2711 self.iterateDirectSubpaths(
2712 [&comma, &idx, dK, sink, indent, &self, filter](const PathEls::PathComponent &c,
2713 function_ref<DomItem()> itemF) {
2714 DomItem i = itemF();
2715 if (!filter(self, c, i))
2716 return true;
2717 if (comma)
2718 sink(u",");
2719 else
2720 comma = true;
2721 switch (c.kind()) {
2722 case Path::Kind::Field:
2723 sinkNewline(sink, indent + 2);
2724 if (dK != DomKind::Object)
2725 sink(u"UNEXPECTED ENTRY ERROR:");
2726 sinkEscaped(sink, c.name());
2727 sink(u":");
2728 break;
2729 case Path::Kind::Key:
2730 sinkNewline(sink, indent + 2);
2731 if (dK != DomKind::Map)
2732 sink(u"UNEXPECTED ENTRY ERROR:");
2733 sinkEscaped(sink, c.name());
2734 sink(u":");
2735 break;
2736 case Path::Kind::Index:
2737 sinkNewline(sink, indent + 2);
2738 if (dK != DomKind::List)
2739 sink(u"UNEXPECTED ENTRY ERROR:");
2740 else if (idx++ != c.index())
2741 sink(u"OUT OF ORDER ARRAY:");
2742 break;
2743 default:
2744 sinkNewline(sink, indent + 2);
2745 sink(u"UNEXPECTED PATH KIND ERROR (ignored)");
2746 break;
2747 }
2748 if (self.isCanonicalChild(i)) {
2749 i.dump(sink, indent + 2, filter);
2750 } else {
2751 sink(uR"({ "~type~": "Reference", "immediate": true, "referredObjectPath":")");
2752 i.canonicalPath().dump([sink](QStringView s) {
2753 sinkEscaped(sink, s, EscapeOptions::NoOuterQuotes);
2754 });
2755 sink(u"\"}");
2756 }
2757 return true;
2758 });
2759}
2760
2761List::List(const Path &pathFromOwner, const List::LookupFunction &lookup,
2762 const List::Length &length, const List::IteratorFunction &iterator,
2763 const QString &elType):
2764 DomElement(pathFromOwner), m_lookup(lookup), m_length(length), m_iterator(iterator),
2765 m_elType(elType)
2766{}
2767
2768quintptr List::id() const
2769{
2770 return quintptr(0);
2771}
2772
2773void List::dump(
2774 const DomItem &self, const Sink &sink, int indent,
2775 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2776{
2777 bool first = true;
2778 sink(u"[");
2779 iterateDirectSubpaths(
2780 self,
2781 [&self, indent, &first, sink, filter](const PathEls::PathComponent &c,
2782 function_ref<DomItem()> itemF) {
2783 DomItem item = itemF();
2784 if (!filter(self, c, item))
2785 return true;
2786 if (first)
2787 first = false;
2788 else
2789 sink(u",");
2790 sinkNewline(sink, indent + 2);
2791 item.dump(sink, indent + 2, filter);
2792 return true;
2793 });
2794 sink(u"]");
2795}
2796
2797bool List::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2798{
2799 if (m_iterator) {
2800 return m_iterator(self, [visitor](index_type i, function_ref<DomItem()> itemF) {
2801 return visitor(PathEls::Index(i), itemF);
2802 });
2803 }
2804 index_type len = indexes(self);
2805 for (index_type i = 0; i < len; ++i) {
2806 if (!visitor(PathEls::Index(i), [this, &self, i]() { return index(self, i); }))
2807 return false;
2808 }
2809 return true;
2810}
2811
2812index_type List::indexes(const DomItem &self) const
2813{
2814 return m_length(self);
2815}
2816
2817DomItem List::index(const DomItem &self, index_type index) const
2818{
2819 return m_lookup(self, index);
2820}
2821
2822void List::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
2823{
2824 ow.writeRegion(LeftBracketRegion);
2825 int baseIndent = ow.increaseIndent(1);
2826 bool first = true;
2827 iterateDirectSubpaths(
2828 self,
2829 [&ow, &first, compact](const PathEls::PathComponent &, function_ref<DomItem()> elF) {
2830 if (first)
2831 first = false;
2832 else
2833 ow.write(u", ", LineWriter::TextAddType::Extra);
2834 if (!compact)
2835 ow.ensureNewline(1);
2836 DomItem el = elF();
2837 el.writeOut(ow);
2838 return true;
2839 });
2840 if (!compact && !first)
2841 ow.newline();
2842 ow.decreaseIndent(1, baseIndent);
2843 ow.writeRegion(RightBracketRegion);
2844}
2845
2846DomElement::DomElement(const Path &pathFromOwner) : m_pathFromOwner(pathFromOwner) { }
2847
2848Path DomElement::pathFromOwner(const DomItem &) const
2849{
2850 Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
2851 return m_pathFromOwner;
2852}
2853
2854Path DomElement::canonicalPath(const DomItem &self) const
2855{
2856 Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
2857 return self.owner().canonicalPath().path(m_pathFromOwner);
2858}
2859
2860DomItem DomElement::containingObject(const DomItem &self) const
2861{
2862 Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
2863 return DomBase::containingObject(self);
2864}
2865
2866void DomElement::updatePathFromOwner(const Path &newPath)
2867{
2868 //if (!domTypeCanBeInline(kind()))
2869 m_pathFromOwner = newPath;
2870}
2871
2872bool Reference::shouldCache() const
2873{
2874 for (const Path &p : referredObjectPath) {
2875 switch (p.headKind()) {
2876 case Path::Kind::Current:
2877 switch (p.headCurrent()) {
2878 case PathCurrent::Lookup:
2879 case PathCurrent::LookupDynamic:
2880 case PathCurrent::LookupStrict:
2881 case PathCurrent::ObjChain:
2882 case PathCurrent::ScopeChain:
2883 return true;
2884 default:
2885 break;
2886 }
2887 break;
2888 case Path::Kind::Empty:
2889 case Path::Kind::Any:
2890 case Path::Kind::Filter:
2891 return true;
2892 default:
2893 break;
2894 }
2895 }
2896 return false;
2897}
2898
2899Reference::Reference(const Path &referredObject, const Path &pathFromOwner, const SourceLocation &)
2900 : DomElement(pathFromOwner), referredObjectPath(referredObject)
2901{
2902}
2903
2904quintptr Reference::id() const
2905{
2906 return quintptr(0);
2907}
2908
2909bool Reference::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2910{
2911 bool cont = true;
2912 cont = cont && self.dvValueLazyField(visitor, Fields::referredObjectPath, [this]() {
2913 return referredObjectPath.toString();
2914 });
2915 cont = cont
2916 && self.dvItemField(visitor, Fields::get, [this, &self]() { return this->get(self); });
2917 return cont;
2918}
2919
2920DomItem Reference::field(const DomItem &self, QStringView name) const
2921{
2922 if (Fields::referredObjectPath == name)
2923 return self.subDataItemField(Fields::referredObjectPath, referredObjectPath.toString());
2924 if (Fields::get == name)
2925 return get(self);
2926 return DomItem();
2927}
2928
2929QList<QString> Reference::fields(const DomItem &) const
2930{
2931 return QList<QString>({QString::fromUtf16(Fields::referredObjectPath), QString::fromUtf16(Fields::get)});
2932}
2933
2934DomItem Reference::index(const DomItem &, index_type) const
2935{
2936 return DomItem();
2937}
2938
2939DomItem Reference::key(const DomItem &, const QString &) const
2940{
2941 return DomItem();
2942}
2943
2944DomItem Reference::get(const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
2945{
2946 DomItem res;
2947 if (referredObjectPath) {
2948 DomItem env;
2949 Path selfPath;
2950 Path cachedPath;
2951 if (shouldCache()) {
2952 env = self.environment();
2953 if (env) {
2954 selfPath = self.canonicalPath();
2955 RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
2956 switch (cached.cached) {
2957 case RefCacheEntry::Cached::None:
2958 break;
2959 case RefCacheEntry::Cached::First:
2960 case RefCacheEntry::Cached::All:
2961 if (!cached.canonicalPaths.isEmpty())
2962 cachedPath = cached.canonicalPaths.first();
2963 else
2964 return res;
2965 break;
2966 }
2967 if (cachedPath) {
2968 res = env.path(cachedPath);
2969 if (!res)
2970 qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
2971 << " leads to invalid path " << cachedPath;
2972 else
2973 return res;
2974 }
2975 }
2976 }
2977 QList<Path> visitedRefsLocal;
2978 self.resolve(
2979 referredObjectPath,
2980 [&res](Path, const DomItem &el) {
2981 res = el;
2982 return false;
2983 },
2984 h, ResolveOption::None, referredObjectPath,
2985 (visitedRefs ? visitedRefs : &visitedRefsLocal));
2986 if (env)
2987 RefCacheEntry::addForPath(
2988 env, selfPath, RefCacheEntry { RefCacheEntry::Cached::First, { cachedPath } });
2989 }
2990 return res;
2991}
2992
2993QList<DomItem> Reference::getAll(
2994 const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
2995{
2996 QList<DomItem> res;
2997 if (referredObjectPath) {
2998 DomItem env;
2999 Path selfPath;
3000 QList<Path> cachedPaths;
3001 if (shouldCache()) {
3002 selfPath = canonicalPath(self);
3003 env = self.environment();
3004 RefCacheEntry cached = RefCacheEntry::forPath(env, selfPath);
3005 switch (cached.cached) {
3006 case RefCacheEntry::Cached::None:
3007 case RefCacheEntry::Cached::First:
3008 break;
3009 case RefCacheEntry::Cached::All:
3010 cachedPaths += cached.canonicalPaths;
3011 if (cachedPaths.isEmpty())
3012 return res;
3013 }
3014 }
3015 if (!cachedPaths.isEmpty()) {
3016 bool outdated = false;
3017 for (const Path &p : cachedPaths) {
3018 DomItem newEl = env.path(p);
3019 if (!newEl) {
3020 outdated = true;
3021 qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
3022 << " leads to invalid path " << p;
3023 break;
3024 } else {
3025 res.append(newEl);
3026 }
3027 }
3028 if (outdated) {
3029 res.clear();
3030 } else {
3031 return res;
3032 }
3033 }
3034 self.resolve(
3035 referredObjectPath,
3036 [&res](Path, const DomItem &el) {
3037 res.append(el);
3038 return true;
3039 },
3040 h, ResolveOption::None, referredObjectPath, visitedRefs);
3041 if (env) {
3042 QList<Path> canonicalPaths;
3043 for (const DomItem &i : res) {
3044 if (i)
3045 canonicalPaths.append(i.canonicalPath());
3046 else
3047 qCWarning(refLog)
3048 << "getAll of reference at " << selfPath << " visits empty items.";
3049 }
3050 RefCacheEntry::addForPath(
3051 env, selfPath,
3052 RefCacheEntry { RefCacheEntry::Cached::All, std::move(canonicalPaths) });
3053 }
3054 }
3055 return res;
3056}
3057
3075OwningItem::OwningItem(int derivedFrom)
3076 : m_derivedFrom(derivedFrom),
3077 m_revision(nextRevision()),
3078 m_createdAt(QDateTime::currentDateTimeUtc()),
3079 m_lastDataUpdateAt(m_createdAt),
3080 m_frozenAt(QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
3081{}
3082
3083OwningItem::OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt)
3084 : m_derivedFrom(derivedFrom),
3085 m_revision(nextRevision()),
3086 m_createdAt(QDateTime::currentDateTimeUtc()),
3087 m_lastDataUpdateAt(lastDataUpdateAt),
3088 m_frozenAt(QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
3089{}
3090
3091OwningItem::OwningItem(const OwningItem &o)
3092 : m_derivedFrom(o.revision()),
3093 m_revision(nextRevision()),
3094 m_createdAt(QDateTime::currentDateTimeUtc()),
3095 m_lastDataUpdateAt(o.lastDataUpdateAt()),
3096 m_frozenAt(QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
3097{
3098 QMultiMap<Path, ErrorMessage> my_errors;
3099 {
3100 QMutexLocker l1(o.mutex());
3101 my_errors = o.m_errors;
3102
3103 }
3104 {
3105 QMutexLocker l2(mutex());
3106 m_errors = my_errors;
3107 }
3108}
3109
3110
3111int OwningItem::nextRevision()
3112{
3113 static QAtomicInt nextRev(0);
3114 return ++nextRev;
3115}
3116
3117bool OwningItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
3118{
3119 bool cont = true;
3120 cont = cont && self.dvItemField(visitor, Fields::errors, [&self, this]() {
3121 QMultiMap<Path, ErrorMessage> myErrors = localErrors();
3122 return self.subMapItem(Map(
3123 self.pathFromOwner().field(Fields::errors),
3124 [myErrors](const DomItem &map, const QString &key) {
3125 auto it = myErrors.find(Path::fromString(key));
3126 if (it != myErrors.end())
3127 return map.subDataItem(PathEls::Key(key), it->toCbor(),
3128 ConstantData::Options::FirstMapIsFields);
3129 else
3130 return DomItem();
3131 },
3132 [myErrors](const DomItem &) {
3133 QSet<QString> res;
3134 auto it = myErrors.keyBegin();
3135 auto end = myErrors.keyEnd();
3136 while (it != end)
3137 res.insert(it++->toString());
3138 return res;
3139 },
3140 QLatin1String("ErrorMessages")));
3141 });
3142 return cont;
3143}
3144
3145DomItem OwningItem::containingObject(const DomItem &self) const
3146{
3147 Source s = self.canonicalPath().split();
3148 if (s.pathFromSource) {
3149 if (!s.pathToSource)
3150 return DomItem();
3151 return self.path(s.pathToSource);
3152 }
3153 return DomItem();
3154}
3155
3156int OwningItem::derivedFrom() const
3157{
3158 return m_derivedFrom;
3159}
3160
3161int OwningItem::revision() const
3162{
3163 return m_revision;
3164}
3165
3166bool OwningItem::frozen() const
3167{
3168 return m_frozenAt > m_createdAt;
3169}
3170
3171bool OwningItem::freeze()
3172{
3173 if (!frozen()) {
3174 m_frozenAt = QDateTime::currentDateTimeUtc();
3175 if (m_frozenAt <= m_createdAt)
3176 m_frozenAt = m_createdAt.addSecs(1);
3177 return true;
3178 }
3179 return false;
3180}
3181
3182QDateTime OwningItem::createdAt() const
3183{
3184 return m_createdAt;
3185}
3186
3187QDateTime OwningItem::lastDataUpdateAt() const
3188{
3189 return m_lastDataUpdateAt;
3190}
3191
3192QDateTime OwningItem::frozenAt() const
3193{
3194 return m_frozenAt;
3195}
3196
3197void OwningItem::refreshedDataAt(QDateTime tNew)
3198{
3199 if (m_lastDataUpdateAt < tNew) // remove check?
3200 m_lastDataUpdateAt = tNew;
3201}
3202
3203void OwningItem::addError(const DomItem &, ErrorMessage &&msg)
3204{
3205 addErrorLocal(std::move(msg));
3206}
3207
3208void OwningItem::addErrorLocal(ErrorMessage &&msg)
3209{
3210 QMutexLocker l(mutex());
3211 quint32 &c = m_errorsCounts[msg];
3212 c += 1;
3213 if (c == 1)
3214 m_errors.insert(msg.path, msg);
3215}
3216
3217void OwningItem::clearErrors(const ErrorGroups &groups)
3218{
3219 QMutexLocker l(mutex());
3220 auto it = m_errors.begin();
3221 while (it != m_errors.end()) {
3222 if (it->errorGroups == groups)
3223 it = m_errors.erase(it);
3224 else
3225 ++it;
3226 }
3227}
3228
3229bool OwningItem::iterateErrors(
3230 const DomItem &self, function_ref<bool(const DomItem &, const ErrorMessage &)> visitor,
3231 const Path &inPath)
3232{
3233 QMultiMap<Path, ErrorMessage> myErrors;
3234 {
3235 QMutexLocker l(mutex());
3236 myErrors = m_errors;
3237 }
3238 auto it = myErrors.lowerBound(inPath);
3239 auto end = myErrors.end();
3240 while (it != end && it.key().mid(0, inPath.length()) == inPath) {
3241 if (!visitor(self, *it++))
3242 return false;
3243 }
3244 return true;
3245}
3246
3247bool OwningItem::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor)
3248{
3249 return self.iterateDirectSubpaths(
3250 [&self, visitor](const PathEls::PathComponent &, function_ref<DomItem()> iF) {
3251 DomItem i = iF();
3252 if (i.owningItemPtr() != self.owningItemPtr()) {
3253 DomItem container = i.container();
3254 if (container.id() == self.id())
3255 return visitor(i);
3256 }
3257 return true;
3258 });
3259}
3260
3261bool operator==(const DomItem &o1, const DomItem &o2)
3262{
3263 if (o1.m_kind != o2.m_kind)
3264 return false;
3265 return o1.visitEl([&o1, &o2](auto &&el1) {
3266 auto &&el2 = std::get<std::decay_t<decltype(el1)>>(o2.m_element);
3267 auto id1 = el1->id();
3268 auto id2 = el2->id();
3269 if (id1 != id2)
3270 return false;
3271 if (id1 != quintptr(0))
3272 return true;
3273 if (o1.m_owner != o2.m_owner)
3274 return false;
3275 Path p1 = el1->pathFromOwner(o1);
3276 Path p2 = el2->pathFromOwner(o2);
3277 if (p1 != p2)
3278 return false;
3279 return true;
3280 });
3281}
3282
3283ErrorHandler MutableDomItem::errorHandler()
3284{
3285 MutableDomItem self;
3286 return [&self](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
3287}
3288
3289MutableDomItem MutableDomItem::addPrototypePath(const Path &prototypePath)
3290{
3291 if (QmlObject *el = mutableAs<QmlObject>()) {
3292 return path(el->addPrototypePath(prototypePath));
3293 } else {
3294 Q_ASSERT(false && "setPrototypePath on non qml scope");
3295 return {};
3296 }
3297}
3298
3299MutableDomItem MutableDomItem::setNextScopePath(const Path &nextScopePath)
3300{
3301 if (QmlObject *el = mutableAs<QmlObject>()) {
3302 el->setNextScopePath(nextScopePath);
3303 return field(Fields::nextScope);
3304 } else {
3305 Q_ASSERT(false && "setNextScopePath on non qml scope");
3306 return {};
3307 }
3308}
3309
3310MutableDomItem MutableDomItem::setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs)
3311{
3312 if (QmlObject *el = mutableAs<QmlObject>()) {
3313 el->setPropertyDefs(propertyDefs);
3314 return field(Fields::propertyDefs);
3315 } else {
3316 Q_ASSERT(false && "setPropertyDefs on non qml scope");
3317 return {};
3318 }
3319}
3320
3321MutableDomItem MutableDomItem::setBindings(QMultiMap<QString, Binding> bindings)
3322{
3323 if (QmlObject *el = mutableAs<QmlObject>()) {
3324 el->setBindings(bindings);
3325 return field(Fields::bindings);
3326 } else {
3327 Q_ASSERT(false && "setBindings on non qml scope");
3328 return {};
3329 }
3330}
3331
3332MutableDomItem MutableDomItem::setMethods(QMultiMap<QString, MethodInfo> functionDefs)
3333{
3334 if (QmlObject *el = mutableAs<QmlObject>())
3335 el->setMethods(functionDefs);
3336 else
3337 Q_ASSERT(false && "setMethods on non qml scope");
3338 return {};
3339}
3340
3341MutableDomItem MutableDomItem::setChildren(const QList<QmlObject> &children)
3342{
3343 if (QmlObject *el = mutableAs<QmlObject>()) {
3344 el->setChildren(children);
3345 return field(Fields::children);
3346 } else
3347 Q_ASSERT(false && "setChildren on non qml scope");
3348 return {};
3349}
3350
3351MutableDomItem MutableDomItem::setAnnotations(const QList<QmlObject> &annotations)
3352{
3353 if (QmlObject *el = mutableAs<QmlObject>())
3354 el->setAnnotations(annotations);
3355 else if (Binding *el = mutableAs<Binding>()) {
3356 el->setAnnotations(annotations);
3357 el->updatePathFromOwner(pathFromOwner());
3358 } else if (PropertyDefinition *el = mutableAs<PropertyDefinition>()) {
3359 el->annotations = annotations;
3360 el->updatePathFromOwner(pathFromOwner());
3361 } else if (MethodInfo *el = mutableAs<MethodInfo>()) {
3362 el->annotations = annotations;
3363 el->updatePathFromOwner(pathFromOwner());
3364 } else if (EnumDecl *el = mutableAs<EnumDecl>()) {
3365 el->setAnnotations(annotations);
3366 el->updatePathFromOwner(pathFromOwner());
3367 } else if (!annotations.isEmpty()) {
3368 Q_ASSERT(false && "setAnnotations on element not supporting them");
3369 }
3370 return field(Fields::annotations);
3371}
3372MutableDomItem MutableDomItem::setScript(const std::shared_ptr<ScriptExpression> &exp)
3373{
3374 switch (internalKind()) {
3375 case DomType::Binding:
3376 if (Binding *b = mutableAs<Binding>()) {
3377 b->setValue(std::make_unique<BindingValue>(exp));
3378 return field(Fields::value);
3379 }
3380 break;
3381 case DomType::MethodInfo:
3382 if (MethodInfo *m = mutableAs<MethodInfo>()) {
3383 m->body = exp;
3384 return field(Fields::body);
3385 }
3386 break;
3387 case DomType::MethodParameter:
3388 if (MethodParameter *p = mutableAs<MethodParameter>()) {
3389 if (exp->expressionType() == ScriptExpression::ExpressionType::ArgInitializer) {
3390 p->defaultValue = exp;
3391 return field(Fields::defaultValue);
3392 }
3393 if (exp->expressionType() == ScriptExpression::ExpressionType::ArgumentStructure) {
3394 p->value = exp;
3395 return field(Fields::value);
3396 }
3397 }
3398 break;
3399 case DomType::ScriptExpression:
3400 return container().setScript(exp);
3401 default:
3402 qCWarning(domLog) << "setScript called on" << internalKindStr();
3403 Q_ASSERT_X(false, "setScript",
3404 "setScript supported only on Binding, MethodInfo, MethodParameter, and "
3405 "ScriptExpression contained in them");
3406 }
3407 return MutableDomItem();
3408}
3409
3410MutableDomItem MutableDomItem::setCode(const QString &code)
3411{
3412 DomItem it = item();
3413 switch (it.internalKind()) {
3414 case DomType::Binding:
3415 if (Binding *b = mutableAs<Binding>()) {
3416 auto exp = std::make_shared<ScriptExpression>(
3417 code, ScriptExpression::ExpressionType::BindingExpression);
3418 b->setValue(std::make_unique<BindingValue>(exp));
3419 return field(Fields::value);
3420 }
3421 break;
3422 case DomType::MethodInfo:
3423 if (MethodInfo *m = mutableAs<MethodInfo>()) {
3424 QString pre = m->preCode(it);
3425 QString post = m->preCode(it);
3426 m->body = std::make_shared<ScriptExpression>(
3427 code, ScriptExpression::ExpressionType::FunctionBody, 0, pre, post);
3428 return field(Fields::body);
3429 }
3430 break;
3431 case DomType::MethodParameter:
3432 if (MethodParameter *p = mutableAs<MethodParameter>()) {
3433 p->defaultValue = std::make_shared<ScriptExpression>(
3434 code, ScriptExpression::ExpressionType::ArgInitializer);
3435 return field(Fields::defaultValue);
3436 }
3437 break;
3438 case DomType::ScriptExpression:
3439 if (std::shared_ptr<ScriptExpression> exp = ownerAs<ScriptExpression>()) {
3440 std::shared_ptr<ScriptExpression> newExp = exp->copyWithUpdatedCode(it, code);
3441 return container().setScript(newExp);
3442 }
3443 break;
3444 default:
3445 qCWarning(domLog) << "setCode called on" << internalKindStr();
3446 Q_ASSERT_X(
3447 false, "setCode",
3448 "setCode supported only on Binding, MethodInfo, MethodParameter, ScriptExpression");
3449 }
3450 return MutableDomItem();
3451}
3452
3453MutableDomItem MutableDomItem::addPropertyDef(
3454 const PropertyDefinition &propertyDef, AddOption option)
3455{
3456 if (QmlObject *el = mutableAs<QmlObject>())
3457 return el->addPropertyDef(*this, propertyDef, option);
3458 else
3459 Q_ASSERT(false && "addPropertyDef on non qml scope");
3460 return MutableDomItem();
3461}
3462
3463MutableDomItem MutableDomItem::addBinding(Binding binding, AddOption option)
3464{
3465 if (QmlObject *el = mutableAs<QmlObject>())
3466 return el->addBinding(*this, binding, option);
3467 else
3468 Q_ASSERT(false && "addBinding on non qml scope");
3469 return MutableDomItem();
3470}
3471
3472MutableDomItem MutableDomItem::addMethod(const MethodInfo &functionDef, AddOption option)
3473{
3474 if (QmlObject *el = mutableAs<QmlObject>())
3475 return el->addMethod(*this, functionDef, option);
3476 else
3477 Q_ASSERT(false && "addMethod on non qml scope");
3478 return MutableDomItem();
3479}
3480
3481MutableDomItem MutableDomItem::addChild(QmlObject child)
3482{
3483 if (QmlObject *el = mutableAs<QmlObject>()) {
3484 return el->addChild(*this, child);
3485 } else if (QmlComponent *el = mutableAs<QmlComponent>()) {
3486 Path p = el->addObject(child);
3487 return owner().path(p); // convenience: treat component objects as children
3488 } else {
3489 Q_ASSERT(false && "addChild on non qml scope");
3490 }
3491 return MutableDomItem();
3492}
3493
3494MutableDomItem MutableDomItem::addAnnotation(QmlObject annotation)
3495{
3496 Path res;
3497 switch (internalKind()) {
3498 case DomType::QmlObject: {
3499 QmlObject *el = mutableAs<QmlObject>();
3500 res = el->addAnnotation(annotation);
3501 } break;
3502 case DomType::Binding: {
3503 Binding *el = mutableAs<Binding>();
3504
3505 res = el->addAnnotation(m_pathFromOwner, annotation);
3506 } break;
3507 case DomType::PropertyDefinition: {
3508 PropertyDefinition *el = mutableAs<PropertyDefinition>();
3509 res = el->addAnnotation(m_pathFromOwner, annotation);
3510 } break;
3511 case DomType::MethodInfo: {
3512 MethodInfo *el = mutableAs<MethodInfo>();
3513 res = el->addAnnotation(m_pathFromOwner, annotation);
3514 } break;
3515 case DomType::Id: {
3516 Id *el = mutableAs<Id>();
3517 res = el->addAnnotation(m_pathFromOwner, annotation);
3518 } break;
3519 default:
3520 Q_ASSERT(false && "addAnnotation on element not supporting them");
3521 }
3522 return MutableDomItem(owner().item(), res);
3523}
3524
3525MutableDomItem MutableDomItem::addPreComment(const Comment &comment, FileLocationRegion region)
3526{
3527 index_type idx;
3528 MutableDomItem rC = field(Fields::comments);
3529 if (auto rcPtr = rC.mutableAs<RegionComments>()) {
3530 auto commentedElement = rcPtr->regionComments()[region];
3531 idx = commentedElement.preComments().size();
3532 commentedElement.addComment(comment);
3533 MutableDomItem res = path(Path::Field(Fields::comments)
3534 .field(Fields::regionComments)
3535 .key(fileLocationRegionName(region))
3536 .field(Fields::preComments)
3537 .index(idx));
3538 Q_ASSERT(res);
3539 return res;
3540 }
3541 return MutableDomItem();
3542}
3543
3544MutableDomItem MutableDomItem::addPostComment(const Comment &comment, FileLocationRegion region)
3545{
3546 index_type idx;
3547 MutableDomItem rC = field(Fields::comments);
3548 if (auto rcPtr = rC.mutableAs<RegionComments>()) {
3549 auto commentedElement = rcPtr->regionComments()[region];
3550 idx = commentedElement.postComments().size();
3551 commentedElement.addComment(comment);
3552 MutableDomItem res = path(Path::Field(Fields::comments)
3553 .field(Fields::regionComments)
3554 .key(fileLocationRegionName(region))
3555 .field(Fields::postComments)
3556 .index(idx));
3557 Q_ASSERT(res);
3558 return res;
3559 }
3560 return MutableDomItem();
3561}
3562
3564{
3565 dumperToQDebug([&c](const Sink &s) { c.dump(s); }, debug);
3566 return debug;
3567}
3568
3569QDebug operator<<(QDebug debug, const MutableDomItem &c)
3570{
3571 MutableDomItem cc(c);
3572 return debug.noquote().nospace() << "MutableDomItem(" << domTypeToString(cc.internalKind())
3573 << ", " << cc.canonicalPath().toString() << ")";
3574}
3575
3576bool ListPBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const
3577{
3578 index_type len = index_type(m_pList.size());
3579 for (index_type i = 0; i < len; ++i) {
3580 if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, i); }))
3581 return false;
3582 }
3583 return true;
3584}
3585
3586void ListPBase::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
3587{
3588 ow.writeRegion(LeftBracketRegion);
3589 int baseIndent = ow.increaseIndent(1);
3590 bool first = true;
3591 index_type len = index_type(m_pList.size());
3592 for (index_type i = 0; i < len; ++i) {
3593 if (first)
3594 first = false;
3595 else
3596 ow.write(u", ", LineWriter::TextAddType::Extra);
3597 if (!compact)
3598 ow.ensureNewline(1);
3599 DomItem el = index(self, i);
3600 el.writeOut(ow);
3601 }
3602 if (!compact && !first)
3603 ow.newline();
3604 ow.decreaseIndent(1, baseIndent);
3605 ow.writeRegion(RightBracketRegion);
3606}
3607
3608QQmlJSScope::ConstPtr ScriptElement::semanticScope()
3609{
3610 return m_scope;
3611}
3612void ScriptElement::setSemanticScope(const QQmlJSScope::ConstPtr &scope)
3613{
3614 m_scope = scope;
3615}
3616
3623ScriptElement::PointerType<ScriptElement> ScriptElementVariant::base() const
3624{
3625 if (!m_data)
3626 return nullptr;
3627
3628 return std::visit(
3629 [](auto &&e) {
3630 // std::reinterpret_pointer_cast does not exist on qnx it seems...
3631 return std::shared_ptr<ScriptElement>(
3632 e, static_cast<ScriptElement *>(e.get()));
3633 },
3634 *m_data);
3635}
3636
3637} // end namespace Dom
3638} // end namespace QQmlJS
3639
3641
3642#include "moc_qqmldomitem_p.cpp"
NSData * m_data
DomItem()=default
\inmodule QtCore
Definition qatomic.h:112
\inmodule QtCore\reentrant
Definition qcborarray.h:20
\inmodule QtCore\reentrant
Definition qcbormap.h:21
\inmodule QtCore\reentrant
Definition qcborvalue.h:47
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
static QDateTime currentDateTimeUtc()
\inmodule QtCore
\inmodule QtCore
Definition qdir.h:20
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2133
QString filePath(const QString &fileName) const
Returns the path name of a file in the directory.
Definition qdir.cpp:778
QString baseName() const
Returns the base name of the file without the path.
QString canonicalFilePath() const
Returns the file system entry's canonical path, including the entry's name, that is,...
static QJsonDocument fromVariant(const QVariant &variant)
Creates a QJsonDocument from the QVariant variant.
\inmodule QtCore\reentrant
Definition qjsonvalue.h:25
const_iterator cend() const
Definition qmap.h:605
const_iterator cbegin() const
Definition qmap.h:601
\inmodule QtCore
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
ConstantData(const Path &pathFromOwner, const QCborValue &value, Options options=Options::MapIsMap)
virtual DomType kind() const =0
virtual void writeOut(const DomItem &self, OutWriter &lw) const
virtual DomItem containingObject(const DomItem &self) const
virtual QString canonicalFilePath(const DomItem &self) const
DomItem fileLocations() const
DomItem fileLocationsTree() const
bool visitKeys(function_ref< bool(const QString &, const DomItem &)> visitor) const
static ErrorGroup domErrorGroup
bool isCanonicalChild(const DomItem &child) const
QSet< QString > propertyInfoNames() const
QSet< QString > keys() const
bool visitLookup1(const QString &symbolName, function_ref< bool(const DomItem &)> visitor, LookupOptions=LookupOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem owner() const
int revision() const
static ErrorGroups myResolveErrors()
static ErrorGroups myErrors()
DomItem goUp(int) const
DomItem fileObject(GoTo option=GoTo::Strict) const
DomItem environment() const
DomItem directParent() const
bool commitToBase(const std::shared_ptr< DomEnvironment > &validPtr=nullptr) const
DomItem container() const
index_type indexes() const
DomItem field(QStringView name) const
Path pathFromOwner() const
QDateTime createdAt() const
QStringList sortedKeys() const
bool visitIndexes(function_ref< bool(const DomItem &)> visitor) const
bool visitPrototypeChain(function_ref< bool(const DomItem &)> visitor, VisitPrototypesOptions options=VisitPrototypesOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
bool visitDirectAccessibleScopes(function_ref< bool(const DomItem &)> visitor, VisitPrototypesOptions options=VisitPrototypesOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem containingObject() const
bool writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const
QString toString() const
bool visitLookup(const QString &symbolName, function_ref< bool(const DomItem &)> visitor, LookupType type=LookupType::Symbol, LookupOptions=LookupOption::Normal, const ErrorHandler &errorHandler=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem rootQmlObject(GoTo option=GoTo::Strict) const
DomItem get(const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
void addError(ErrorMessage &&msg) const
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
std::shared_ptr< OwningItem > owningItemPtr() const
function_ref< bool(const Path &, const DomItem &)> Visitor
bool iterateDirectSubpaths(DirectVisitor v) const
quintptr id() const
std::shared_ptr< DomTop > topPtr() const
DomItem lookupFirst(const QString &symbolName, LookupType type=LookupType::Symbol, LookupOptions=LookupOption::Normal, const ErrorHandler &errorHandler=nullptr) const
DomItem operator[](const Path &path) const
DomItem filterUp(function_ref< bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const
DomItem subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const
DomItem containingScriptExpression() const
DomItem top() const
void clearErrors(const ErrorGroups &groups=ErrorGroups({}), bool iterate=true) const
int derivedFrom() const
QQmlJSScope::ConstPtr nearestSemanticScope() const
MutableDomItem makeCopy(CopyOption option=CopyOption::EnvConnected) const
bool visitScopeChain(function_ref< bool(const DomItem &)> visitor, LookupOptions=LookupOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem scope(FilterUpOptions options=FilterUpOptions::ReturnOuter) const
QDateTime lastDataUpdateAt() const
QCborValue value() const
bool visitStaticTypePrototypeChains(function_ref< bool(const DomItem &)> visitor, VisitPrototypesOptions options=VisitPrototypesOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem globalScope() const
QString canonicalFilePath() const
bool visitTree(const Path &basePath, ChildrenVisitor visitor, VisitOptions options=VisitOption::Default, ChildrenVisitor openingVisitor=emptyChildrenVisitor, ChildrenVisitor closingVisitor=emptyChildrenVisitor, const FieldFilter &filter=FieldFilter::noFilter()) const
bool resolve(const Path &path, Visitor visitor, const ErrorHandler &errorHandler, ResolveOptions options=ResolveOption::None, const Path &fullPath=Path(), QList< Path > *visitedRefs=nullptr) const
bool iterateSubOwners(function_ref< bool(const DomItem &owner)> visitor) const
QDateTime frozenAt() const
QList< DomItem > getAll(const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem universe() const
DomItem proceedToScope(const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
QList< DomItem > values() const
ErrorHandler errorHandler() const
DomItem index(index_type) const
Path canonicalPath() const
bool hasAnnotations() const
static DomItem empty
void writeOut(OutWriter &lw) const
bool visitUp(function_ref< bool(const DomItem &)> visitor) const
QList< DomItem > lookup(const QString &symbolName, LookupType type=LookupType::Symbol, LookupOptions=LookupOption::Normal, const ErrorHandler &errorHandler=nullptr) const
static DomItem fromCode(const QString &code, DomType fileType=DomType::QmlFile)
DomItem containingFile() const
void dump(const Sink &, int indent=0, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter=noFilter) const
function_ref< bool(const Path &, const DomItem &, bool)> ChildrenVisitor
QList< QString > fields() const
DomItem goToFile(const QString &filePath) const
DomItem component(GoTo option=GoTo::Strict) const
void writeOutPost(OutWriter &lw) const
void writeOutPre(OutWriter &lw) const
bool iterateErrors(function_ref< bool(const DomItem &, const ErrorMessage &)> visitor, bool iterate, Path inPath=Path()) const
PropertyInfo propertyInfoWithName(const QString &name) const
bool visitLocalSymbolsNamed(const QString &name, function_ref< bool(const DomItem &)> visitor) const
DomItem subReferencesItem(const PathEls::PathComponent &c, const QList< Path > &paths) const
void dumpPtr(const Sink &sink) const
DomItem qmlObject(GoTo option=GoTo::Strict, FilterUpOptions options=FilterUpOptions::ReturnOuter) const
DomItem key(const QString &name) const
QQmlJSScope::ConstPtr semanticScope() const
Class that represent a filter on DomItem, when dumping or comparing.
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
static QString anchoredPattern(const QString &expression)
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
iterator erase(const_iterator i)
Definition qset.h:145
const_iterator cbegin() const noexcept
Definition qset.h:138
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:6045
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
QString & append(QChar c)
Definition qstring.cpp:3252
\inmodule QtCore
\inmodule QtCore
Definition qtimezone.h:26
QJSValue expected
Definition qjsengine.cpp:12
QMap< QString, QString > map
[6]
QPixmap p2
QPixmap p1
[0]
list append(new Employee("Blackpool", "Stephen"))
QSet< QString >::iterator it
QStyleOptionButton opt
short next
Definition keywords.cpp:445
typename C::iterator iterator
constexpr bool domTypeIsOwningItem(DomType)
qint64 index_type
void sinkEscaped(const Sink &sink, QStringView s, EscapeOptions options)
dumps a string as quoted string (escaping things like quotes or newlines)
QMLDOM_EXPORT QString domTypeToString(DomType k)
void defaultErrorHandler(const ErrorMessage &error)
Calls the default error handler (by default errorToQDebug)
QMLDOM_EXPORT bool domTypeIsScope(DomType k)
QMLDOM_EXPORT bool domTypeIsExternalItem(DomType k)
void sinkNewline(const Sink &s, int indent)
sinks a neline and indents by the given amount
QString fileLocationRegionName(FileLocationRegion region)
QString dumperToString(const Dumper &writer)
Converts a dumper to a string.
QStringList domCompareStrList(const DomItem &i1, const DomItem &i2, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const > filter, DomCompareStrList stopAtFirstDiff)
QMLDOM_EXPORT QMap< DomType, QString > domTypeToStringMap()
void dumperToQDebug(const Dumper &dumper, QDebug debug)
function_ref< void(QStringView)> Sink
QMLDOM_EXPORT bool domTypeIsTopItem(DomType k)
std::function< void(const ErrorMessage &)> ErrorHandler
QString lineDiff(QString s1, QString s2, int nContext)
QMLDOM_EXPORT QString domKindToString(DomKind k)
QMLDOM_EXPORT bool domTypeIsContainer(DomType k)
QMLDOM_EXPORT QMap< DomKind, QString > domKindToStringMap()
Combined button and popup list for selecting options.
@ CaseInsensitive
QString self
Definition language.cpp:58
static jboolean copy(JNIEnv *, jobject)
SharedPointerFileDialogOptions m_options
#define Q_FALLTHROUGH()
QList< QString > QStringList
Constructs a string list that contains the given string, str.
#define Q_DECLARE_TR_FUNCTIONS(context)
static QDBusError::ErrorType get(const char *name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
@ QtWarningMsg
Definition qlogging.h:31
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
const char * typeName
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLenum GLsizei GLsizei GLint * values
[15]
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLsizei GLuint * groups
GLuint64 key
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLsizei GLenum GLenum * types
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble GLdouble GLdouble top
GLfloat GLfloat f
GLenum type
GLsizei const GLuint * paths
GLenum target
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint name
GLint first
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
void ** params
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLenum array
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLsizei GLenum GLboolean sink
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLuint GLenum option
GLenum GLsizei len
QT_BEGIN_NAMESPACE constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
static qreal component(const QPointF &point, unsigned int i)
#define NewErrorGroup(name)
static bool fromString(const QMetaObject *mo, QString s, Allocate &&allocate)
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
QQmlJS::Dom::DomItem DomItem
bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
Definition qrandom.cpp:1220
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
static QString dump(const QByteArray &)
SSL_CTX int void DUMMYARG SSL const unsigned char * protos
SSL_CTX int void * arg
static QString canonicalPath(const QString &rootPath)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
static FileType fileType(const QFileInfo &fi)
#define tr(X)
#define Q_UNUSED(x)
static int compare(quint64 a, quint64 b)
unsigned int quint32
Definition qtypes.h:50
size_t quintptr
Definition qtypes.h:167
static const uint base
Definition qurlidna.cpp:20
QList< std::pair< QString, QString > > Map
static double UTC(double t, double localTZA)
QList< int > list
[14]
QFile file
[0]
QStringList keys
QDataStream & operator<<(QDataStream &out, const MyClass &myObj)
[4]
QVariant variant
[1]
QMutex mutex
[2]
QGraphicsItem * item
QLayoutItem * child
[0]
manager post(request, myJson, this, [this](QRestReply &reply) { if(!reply.isSuccess()) { } if(std::optional json=reply.readJson()) { } })
char * toString(const MyType &t)
[31]
QStringView el
\inmodule QtCore \reentrant
Definition qchar.h:18