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