aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorFawzi Mohamed <fawzi.mohamed@qt.io>2021-03-23 11:02:18 +0100
committerFawzi Mohamed <fawzi.mohamed@qt.io>2021-06-05 00:06:52 +0200
commit52d61e705ee606f3b673c757bdf253bdf6134a3b (patch)
treeac4244f978ef8ecc5eee56c03d8b4a452b8b291b /src
parent77874b5afa327a78f69437535a115d53f274eb76 (diff)
qmldom: representation and load of Qml Files
- qqmldomitem: main API for generic access to the Dom - qqmldomtop: represent top level elements: DomEnvironment and Universe - qqmldomelements: definition of the classes representing Qml - qqmldomastcreator: instatiate Dom elements from AST - qqmldomcomments: represent comments in Qml - qqmldomexternalitems: represent files - qqmldommoduleindex: represent types in a module - tst_dom_all: combined test running all dom tests Change-Id: If2320722bc3e6eaab9669ecec6962d5473184f29 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qmldom/CMakeLists.txt5
-rw-r--r--src/qmldom/qqmldom_fwd_p.h77
-rw-r--r--src/qmldom/qqmldomastcreator.cpp988
-rw-r--r--src/qmldom/qqmldomastcreator_p.h73
-rw-r--r--src/qmldom/qqmldomastdumper.cpp73
-rw-r--r--src/qmldom/qqmldomattachedinfo.cpp139
-rw-r--r--src/qmldom/qqmldomattachedinfo_p.h82
-rw-r--r--src/qmldom/qqmldomcomments.cpp714
-rw-r--r--src/qmldom/qqmldomcomments_p.h339
-rw-r--r--src/qmldom/qqmldomconstants_p.h78
-rw-r--r--src/qmldom/qqmldomelements.cpp1263
-rw-r--r--src/qmldom/qqmldomelements_p.h1172
-rw-r--r--src/qmldom/qqmldomerrormessage.cpp12
-rw-r--r--src/qmldom/qqmldomerrormessage_p.h3
-rw-r--r--src/qmldom/qqmldomexternalitems.cpp397
-rw-r--r--src/qmldom/qqmldomexternalitems_p.h369
-rw-r--r--src/qmldom/qqmldomitem.cpp2785
-rw-r--r--src/qmldom/qqmldomitem_p.h2280
-rw-r--r--src/qmldom/qqmldommock.cpp184
-rw-r--r--src/qmldom/qqmldommock_p.h147
-rw-r--r--src/qmldom/qqmldommoduleindex.cpp430
-rw-r--r--src/qmldom/qqmldommoduleindex_p.h174
-rw-r--r--src/qmldom/qqmldompath_p.h6
-rw-r--r--src/qmldom/qqmldomtop.cpp2085
-rw-r--r--src/qmldom/qqmldomtop_p.h559
25 files changed, 12584 insertions, 1850 deletions
diff --git a/src/qmldom/CMakeLists.txt b/src/qmldom/CMakeLists.txt
index 8f78c9b08c..2c36f74b64 100644
--- a/src/qmldom/CMakeLists.txt
+++ b/src/qmldom/CMakeLists.txt
@@ -11,13 +11,18 @@ qt_internal_add_module(QmlDom
SOURCES
qqmldom_fwd_p.h
qqmldom_global.h
+ qqmldomastcreator.cpp qqmldomastcreator_p.h
qqmldomastdumper.cpp qqmldomastdumper_p.h
qqmldomattachedinfo.cpp qqmldomattachedinfo_p.h
+ qqmldomcomments.cpp qqmldomcomments_p.h
qqmldomconstants_p.h
+ qqmldomelements.cpp qqmldomelements_p.h
qqmldomerrormessage.cpp qqmldomerrormessage_p.h
qqmldomexternalitems.cpp qqmldomexternalitems_p.h
qqmldomfunctionref_p.h
qqmldomitem.cpp qqmldomitem_p.h
+ qqmldommock.cpp qqmldommock_p.h
+ qqmldommoduleindex.cpp qqmldommoduleindex_p.h
qqmldompath.cpp qqmldompath_p.h
qqmldomstringdumper.cpp qqmldomstringdumper_p.h
qqmldomtop.cpp qqmldomtop_p.h
diff --git a/src/qmldom/qqmldom_fwd_p.h b/src/qmldom/qqmldom_fwd_p.h
index a8b54b1e51..7b21489eb5 100644
--- a/src/qmldom/qqmldom_fwd_p.h
+++ b/src/qmldom/qqmldom_fwd_p.h
@@ -56,47 +56,62 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-class ExternalItemInfoBase;
-class ExternalItemPairBase;
-class ExternalOwningItem;
-class OwningItem;
+class AstComments;
+class AttachedInfo;
+class Binding;
+class Comment;
+class CommentedElement;
+class ConstantData;
class DomBase;
+class DomEnvironment;
class DomItem;
-class Source;
+class DomTop;
+class DomUniverse;
class Empty;
-class QmlDirectory;
+class EnumDecl;
+class Export;
+class ExternalItemInfoBase;
+class ExternalItemPairBase;
+class ExternalOwningItem;
+class FileLocations;
+class FileWriter;
+class GlobalComponent;
+class GlobalScope;
+class MockObject;
+class MockOwner;
+class Id;
+class Import;
class JsFile;
-class QmlFile;
-class QmltypesFile;
+class JsResource;
+class List;
+class LoadInfo;
+class Map;
+class MethodInfo;
class ModuleIndex;
class ModuleScope;
-class Export;
-class JsResource;
-class QmltypesComponent;
-class QmlComponent;
-class EnumDecl;
-class Import;
+class MutableDomItem;
+class ObserversTrie;
+class OutWriter;
+class OutWriterState;
+class OwningItem;
+class Path;
class Pragma;
-class Id;
+class PropertyDefinition;
+class PropertyInfo;
+class QmlDomAstCreator;
+class QmlComponent;
+class QmlDirectory;
+class QmldirFile;
+class QmlFile;
class QmlObject;
-class ConstantData;
-class ScriptExpression;
+class QmltypesComponent;
+class QmltypesFile;
class Reference;
-class Binding;
-class PropertyDefinition;
-class RequiredProperty;
-class Version;
-class MethodInfo;
-class GenericObject;
-class Map;
-class List;
+class RegionComments;
+class ScriptExpression;
+class Source;
+class TestDomItem;
class Version;
-class DomTop;
-class DomEnvironment;
-class DomUniverse;
-
-class Subpath;
-class ObserversTrie;
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomastcreator.cpp b/src/qmldom/qqmldomastcreator.cpp
new file mode 100644
index 0000000000..98d38024ed
--- /dev/null
+++ b/src/qmldom/qqmldomastcreator.cpp
@@ -0,0 +1,988 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#include "qqmldomastcreator_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldomtop_p.h"
+#include "qqmldomerrormessage_p.h"
+#include "qqmldomastdumper_p.h"
+#include "qqmldomattachedinfo_p.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QScopeGuard>
+#include <QtCore/QLoggingCategory>
+
+#include <memory>
+#include <variant>
+
+static Q_LOGGING_CATEGORY(creatorLog, "qt.qmldom.astcreator", QtWarningMsg);
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+using namespace AST;
+
+template<typename K, typename V>
+V *valueFromMultimap(QMultiMap<K, V> &mmap, const K &key, index_type idx)
+{
+ if (idx < 0)
+ return nullptr;
+ auto it = mmap.find(key);
+ auto end = mmap.end();
+ if (it == end)
+ return nullptr;
+ auto it2 = it;
+ index_type nEl = 0;
+ while (it2 != end && it2.key() == key) {
+ ++it2;
+ ++nEl;
+ }
+ if (nEl <= idx)
+ return nullptr;
+ for (index_type i = idx + 1; i < nEl; ++i)
+ ++it;
+ return &(*it);
+}
+
+static ErrorGroups myParseErrors()
+{
+ static ErrorGroups errs = { { NewErrorGroup("Dom"), NewErrorGroup("QmlFile"),
+ NewErrorGroup("Parsing") } };
+ return errs;
+}
+
+static QString toString(const UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
+{
+ QString result;
+
+ for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
+ if (iter != qualifiedId)
+ result += delimiter;
+
+ result += iter->name;
+ }
+
+ return result;
+}
+
+SourceLocation combineLocations(SourceLocation s1, SourceLocation s2)
+{
+ return combine(s1, s2);
+}
+
+SourceLocation combineLocations(Node *n)
+{
+ return combineLocations(n->firstSourceLocation(), n->lastSourceLocation());
+}
+
+class DomValue
+{
+public:
+ template<typename T>
+ DomValue(const T &obj) : kind(T::kindValue), value(obj)
+ {
+ }
+ DomType kind;
+ std::variant<QmlObject, MethodInfo, QmlComponent, PropertyDefinition, Binding, EnumDecl,
+ EnumItem, ConstantData, Id>
+ value;
+};
+
+class StackEl
+{
+public:
+ Path path;
+ DomValue item;
+ FileLocations::Tree fileLocations;
+};
+
+class QmlDomAstCreator final : public AST::Visitor
+{
+ Q_DECLARE_TR_FUNCTIONS(QmlDomAstCreator)
+
+ static constexpr const auto className = "QmlDomAstCreator";
+
+ MutableDomItem qmlFile;
+ std::shared_ptr<QmlFile> qmlFilePtr;
+ QVector<StackEl> nodeStack;
+ QVector<int> arrayBindingLevels;
+ FileLocations::Tree rootMap;
+
+ template<typename T>
+ StackEl &currentEl(int idx = 0)
+ {
+ Q_ASSERT_X(idx < nodeStack.length() && idx >= 0, "currentQmlObjectOrComponentEl",
+ "Stack does not contain enough elements!");
+ int i = nodeStack.length() - idx;
+ while (i-- > 0) {
+ DomType k = nodeStack.at(i).item.kind;
+ if (k == T::kindValue)
+ return nodeStack[i];
+ }
+ Q_ASSERT_X(false, "currentEl", "Stack does not contan object of type ");
+ return nodeStack.last();
+ }
+
+ template<typename T>
+ T &current(int idx = 0)
+ {
+ return std::get<T>(currentEl<T>(idx).item.value);
+ }
+
+ index_type currentIndex() { return currentNodeEl().path.last().headIndex(); }
+
+ StackEl &currentQmlObjectOrComponentEl(int idx = 0)
+ {
+ Q_ASSERT_X(idx < nodeStack.length() && idx >= 0, "currentQmlObjectOrComponentEl",
+ "Stack does not contain enough elements!");
+ int i = nodeStack.length() - idx;
+ while (i-- > 0) {
+ DomType k = nodeStack.at(i).item.kind;
+ if (k == DomType::QmlObject || k == DomType::QmlComponent)
+ return nodeStack[i];
+ }
+ Q_ASSERT_X(false, "currentQmlObjectEl", "No QmlObject or component in stack");
+ return nodeStack.last();
+ }
+
+ StackEl &currentNodeEl(int i = 0)
+ {
+ Q_ASSERT_X(i < nodeStack.length() && i >= 0, "currentNode",
+ "Stack does not contain element!");
+ return nodeStack[nodeStack.length() - i - 1];
+ }
+
+ DomValue &currentNode(int i = 0)
+ {
+ Q_ASSERT_X(i < nodeStack.length() && i >= 0, "currentNode",
+ "Stack does not contain element!");
+ return nodeStack[nodeStack.length() - i - 1].item;
+ }
+
+ void removeCurrentNode(std::optional<DomType> expectedType)
+ {
+ Q_ASSERT_X(!nodeStack.isEmpty(), className, "popCurrentNode() without any node");
+ if (expectedType)
+ Q_ASSERT(nodeStack.last().item.kind == *expectedType);
+ nodeStack.removeLast();
+ }
+
+ void pushEl(Path p, DomValue it, AST::Node *n)
+ {
+ nodeStack.append({ p, it, createMap(it.kind, p, n) });
+ }
+
+ FileLocations::Tree createMap(FileLocations::Tree base, Path p, AST::Node *n)
+ {
+ FileLocations::Tree res = FileLocations::ensure(base, p, AttachedInfo::PathType::Relative);
+ if (n)
+ FileLocations::addRegion(res, QString(), combineLocations(n));
+ return res;
+ }
+
+ FileLocations::Tree createMap(DomType k, Path p, AST::Node *n)
+ {
+ FileLocations::Tree base;
+ switch (k) {
+ case DomType::QmlObject:
+ switch (currentNode().kind) {
+ case DomType::QmlObject:
+ case DomType::QmlComponent:
+ case DomType::PropertyDefinition:
+ case DomType::Binding:
+ case DomType::Id:
+ case DomType::MethodInfo:
+ break;
+ default:
+ qDebug() << "unexpected type" << domTypeToString(currentNode().kind);
+ Q_ASSERT(false);
+ }
+ base = currentNodeEl().fileLocations;
+ if (p.length() > 2) {
+ Path p2 = p[p.length() - 2];
+ if (p2.headKind() == Path::Kind::Field
+ && (p2.checkHeadName(Fields::children) || p2.checkHeadName(Fields::objects)
+ || p2.checkHeadName(Fields::value) || p2.checkHeadName(Fields::annotations)
+ || p2.checkHeadName(Fields::children)))
+ p = p.mid(p.length() - 2, 2);
+ else if (p.last().checkHeadName(Fields::value)
+ && p.last().headKind() == Path::Kind::Field)
+ p = p.last();
+ else {
+ qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
+ Q_ASSERT(false);
+ }
+ } else {
+ qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
+ Q_ASSERT(false);
+ }
+ break;
+ case DomType::EnumItem:
+ base = currentNodeEl().fileLocations;
+ break;
+ case DomType::QmlComponent:
+ case DomType::Pragma:
+ case DomType::Import:
+ case DomType::Id:
+ case DomType::EnumDecl:
+ base = rootMap;
+ break;
+ case DomType::Binding:
+ case DomType::PropertyDefinition:
+ case DomType::MethodInfo:
+ base = currentEl<QmlObject>().fileLocations;
+ if (p.length() > 3)
+ p = p.mid(p.length() - 3, 3);
+ break;
+ default:
+ qCWarning(domLog) << "Unexpected type in createMap:" << domTypeToString(k);
+ Q_ASSERT(false);
+ break;
+ }
+ return createMap(base, p, n);
+ }
+
+public:
+ QmlDomAstCreator(MutableDomItem qmlFile)
+ : qmlFile(qmlFile),
+ qmlFilePtr(qmlFile.ownerAs<QmlFile>()),
+ rootMap(qmlFilePtr->fileLocationsTree())
+ {
+ }
+
+ bool visit(UiProgram *program) override
+ {
+ QFileInfo fInfo(qmlFile.canonicalFilePath());
+ QString componentName = fInfo.baseName();
+ QmlComponent *cPtr;
+ Path p = qmlFilePtr->addComponent(QmlComponent(componentName), AddOption::KeepExisting,
+ &cPtr);
+ MutableDomItem newC(qmlFile.item(), p);
+ Q_ASSERT_X(newC.item(), className, "could not recover component added with addComponent");
+ // QmlFile region == Component region == program span
+ // we hide the component span because the component s written after the imports
+ FileLocations::addRegion(rootMap, QString(), combineLocations(program));
+ pushEl(p, *cPtr, program);
+ // implicit imports
+ // add implicit directory import
+ Import selfDirImport(QLatin1String("file://") + fInfo.canonicalPath());
+ selfDirImport.implicit = true;
+ qmlFilePtr->addImport(selfDirImport);
+ for (Import i : qmlFile.environment().ownerAs<DomEnvironment>()->implicitImports()) {
+ i.implicit = true;
+ qmlFilePtr->addImport(i);
+ }
+ return true;
+ }
+
+ void endVisit(AST::UiProgram *) override
+ {
+ MutableDomItem newC = qmlFile.path(currentNodeEl().path);
+ QmlComponent &comp = current<QmlComponent>();
+ for (const Pragma &p : qmlFilePtr->pragmas()) {
+ if (p.name.compare(u"singleton", Qt::CaseInsensitive) == 0) {
+ comp.setIsSingleton(true);
+ comp.setIsCreatable(false); // correct?
+ }
+ }
+ *newC.mutableAs<QmlComponent>() = comp;
+ removeCurrentNode(DomType::QmlComponent);
+ Q_ASSERT_X(nodeStack.isEmpty(), className, "ui program did not finish node stack");
+ }
+
+ bool visit(UiPragma *el) override
+ {
+ createMap(DomType::Pragma, qmlFilePtr->addPragma(Pragma(el->name.toString())), el);
+ return true;
+ }
+
+ bool visit(UiImport *el) override
+ {
+ Version v(Version::Latest, Version::Latest);
+ if (el->version && el->version->version.hasMajorVersion())
+ v.majorVersion = el->version->version.majorVersion();
+ if (el->version && el->version->version.hasMinorVersion())
+ v.minorVersion = el->version->version.minorVersion();
+ if (el->importUri != nullptr)
+ createMap(DomType::Import,
+ qmlFilePtr->addImport(Import::fromUriString(toString(el->importUri), v,
+ el->importId.toString())),
+ el);
+ else
+ createMap(DomType::Import,
+ qmlFilePtr->addImport(Import::fromFileString(
+ el->fileName.toString(),
+ QFileInfo(qmlFilePtr->canonicalFilePath()).dir().absolutePath(),
+ el->importId.toString())),
+ el);
+ return true;
+ }
+
+ bool visit(AST::UiPublicMember *el) override
+ {
+ switch (el->type) {
+ case AST::UiPublicMember::Signal: {
+ MethodInfo m;
+ m.name = el->name.toString();
+ m.typeName = toString(el->memberType);
+ m.isReadonly = el->isReadonlyMember;
+ m.access = MethodInfo::Public;
+ m.methodType = MethodInfo::Signal;
+ m.isList = el->typeModifier == QLatin1String("list");
+ MethodInfo *mPtr;
+ Path p = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
+ pushEl(p, *mPtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"signal", el->propertyToken);
+ MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
+ AST::UiParameterList *args = el->parameters;
+ while (args) {
+ MethodParameter param;
+ param.name = args->name.toString();
+ param.typeName = toString(args->type);
+ index_type idx = index_type(mInfo.parameters.size());
+ mInfo.parameters.append(param);
+ auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
+ Path::Field(Fields::parameters).index(idx),
+ AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(argLocs, QString(), combineLocations(args));
+ args = args->next;
+ }
+ break;
+ }
+ case AST::UiPublicMember::Property: {
+ PropertyDefinition p;
+ p.name = el->name.toString();
+ p.typeName = toString(el->memberType);
+ p.isReadonly = el->isReadonlyMember;
+ p.isDefaultMember = el->isDefaultMember;
+ p.isList = el->typeModifier == QLatin1String("list");
+ p.isRequired = el->isRequired;
+ if (!el->typeModifier.isEmpty())
+ p.typeName = el->typeModifier.toString() + QChar(u'<') + p.typeName + QChar(u'>');
+ PropertyDefinition *pPtr;
+ Path pPathFromOwner =
+ current<QmlObject>().addPropertyDef(p, AddOption::KeepExisting, &pPtr);
+ pushEl(pPathFromOwner, *pPtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"property",
+ el->propertyToken);
+ if (p.name == u"id")
+ qmlFile.addError(
+ myParseErrors()
+ .warning(tr("id is a special attribute, that should not be "
+ "used as property name"))
+ .withPath(currentNodeEl().path));
+ if (p.isDefaultMember)
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"default",
+ el->defaultToken);
+ if (p.isRequired)
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"required",
+ el->requiredToken);
+ if (el->statement) {
+ BindingType bType = BindingType::Normal;
+ SourceLocation loc = combineLocations(el->statement);
+ QStringView code = qmlFilePtr->code();
+
+ std::shared_ptr<ScriptExpression> script(new ScriptExpression(
+ code.mid(loc.offset, loc.length), qmlFilePtr->engine(), el->statement,
+ qmlFilePtr->astComments(),
+ ScriptExpression::ExpressionType::BindingExpression, loc));
+ Binding *bPtr;
+ Path bPathFromOwner = current<QmlObject>().addBinding(
+ Binding(p.name, script, bType), AddOption::KeepExisting, &bPtr);
+ FileLocations::Tree bLoc = createMap(DomType::Binding, bPathFromOwner, el);
+ FileLocations::addRegion(bLoc, u"colon", el->colonToken);
+ FileLocations::Tree valueLoc = FileLocations::ensure(
+ bLoc, Path::Field(Fields::value), AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(valueLoc, QString(), combineLocations(el->statement));
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ void endVisit(AST::UiPublicMember *el) override
+ {
+ Node::accept(el->parameters, this);
+ loadAnnotations(el);
+ if ((el->binding || el->statement)
+ && nodeStack.last().item.kind == DomType::PropertyDefinition) {
+ PropertyDefinition &pDef = std::get<PropertyDefinition>(nodeStack.last().item.value);
+ if (!pDef.annotations.isEmpty()) {
+ QmlObject duplicate;
+ duplicate.setName(QLatin1String("duplicate"));
+ QmlObject &obj = current<QmlObject>();
+ auto it = obj.m_bindings.find(pDef.name);
+ if (it != obj.m_bindings.end()) {
+ for (QmlObject ann : pDef.annotations) {
+ ann.addAnnotation(duplicate);
+ it->addAnnotation(
+ currentEl<QmlObject>()
+ .path.field(Fields::bindings)
+ .key(pDef.name)
+ .index(obj.m_bindings.values(pDef.name).length() - 1),
+ ann);
+ }
+ }
+ }
+ }
+ QmlObject &obj = current<QmlObject>();
+ StackEl &sEl = nodeStack.last();
+ switch (sEl.item.kind) {
+ case DomType::PropertyDefinition: {
+ PropertyDefinition pDef = std::get<PropertyDefinition>(sEl.item.value);
+ PropertyDefinition *pDefPtr =
+ valueFromMultimap(obj.m_propertyDefs, pDef.name, sEl.path.last().headIndex());
+ Q_ASSERT(pDefPtr);
+ *pDefPtr = pDef;
+ } break;
+ case DomType::MethodInfo: {
+ MethodInfo m = std::get<MethodInfo>(sEl.item.value);
+ MethodInfo *mPtr =
+ valueFromMultimap(obj.m_methods, m.name, sEl.path.last().headIndex());
+ Q_ASSERT(mPtr);
+ *mPtr = m;
+ } break;
+ default:
+ Q_ASSERT(false);
+ }
+ removeCurrentNode({});
+ }
+
+ bool visit(AST::UiSourceElement *el) override
+ {
+ QStringView code(qmlFilePtr->code());
+ if (FunctionDeclaration *fDef = cast<FunctionDeclaration *>(el->sourceElement)) {
+ MethodInfo m;
+ m.name = fDef->name.toString();
+ if (AST::TypeAnnotation *tAnn = fDef->typeAnnotation) {
+ if (AST::Type *t = tAnn->type) {
+ m.typeName = toString(t->typeId);
+ if (t->typeArguments) {
+ Q_ASSERT_X(false, className,
+ "todo: type argument should be added to the typeName");
+ }
+ }
+ }
+ m.access = MethodInfo::Public;
+ m.methodType = MethodInfo::Method;
+ if (fDef->body) {
+ SourceLocation bodyLoc = combineLocations(fDef->body);
+ SourceLocation methodLoc = combineLocations(el);
+ QStringView preCode =
+ code.mid(methodLoc.begin(), bodyLoc.begin() - methodLoc.begin());
+ QStringView postCode = code.mid(bodyLoc.end(), methodLoc.end() - bodyLoc.end());
+ m.body = std::shared_ptr<ScriptExpression>(new ScriptExpression(
+ code.mid(bodyLoc.offset, bodyLoc.length), qmlFilePtr->engine(), fDef->body,
+ qmlFilePtr->astComments(), ScriptExpression::ExpressionType::FunctionBody,
+ bodyLoc, 0, preCode, postCode));
+ }
+ MethodInfo *mPtr;
+ Path mPathFromOwner = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
+ pushEl(mPathFromOwner, *mPtr,
+ fDef); // add at the start and use the normal recursive visit?
+ loadAnnotations(el);
+ MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
+ AST::FormalParameterList *args = fDef->formals;
+ while (args) {
+ MethodParameter param;
+ param.name = args->element->bindingIdentifier.toString();
+ if (AST::TypeAnnotation *tAnn = args->element->typeAnnotation) {
+ if (AST::Type *t = tAnn->type) {
+ param.typeName = toString(t->typeId);
+ if (t->typeArguments) {
+ Q_ASSERT_X(false, className,
+ "todo: type argument should be added to the typeName");
+ }
+ }
+ }
+ if (args->element->initializer) {
+ SourceLocation loc = combineLocations(args->element->initializer);
+ std::shared_ptr<ScriptExpression> script =
+ std::shared_ptr<ScriptExpression>(new ScriptExpression(
+ code.mid(loc.offset, loc.length), qmlFilePtr->engine(),
+ args->element->initializer, qmlFilePtr->astComments(),
+ ScriptExpression::ExpressionType::ArgInitializer, loc));
+ param.defaultValue = script;
+ }
+ index_type idx = index_type(mInfo.parameters.size());
+ mInfo.parameters.append(param);
+ auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
+ Path::Field(Fields::parameters).index(idx),
+ AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(argLocs, QString(), combineLocations(args));
+ args = args->next;
+ }
+ return false;
+ } else {
+ qCWarning(creatorLog) << "source el:" << static_cast<AST::Node *>(el);
+ Q_ASSERT(false);
+ }
+ return true;
+ }
+
+ void endVisit(AST::UiSourceElement *) override
+ {
+ MethodInfo &m = std::get<MethodInfo>(currentNode().value);
+ QmlObject &obj = current<QmlObject>();
+ MethodInfo *mPtr =
+ valueFromMultimap(obj.m_methods, m.name, nodeStack.last().path.last().headIndex());
+ Q_ASSERT(mPtr);
+ *mPtr = m;
+ removeCurrentNode(DomType::MethodInfo);
+ }
+
+ void loadAnnotations(UiObjectMember *el) { Node::accept(el->annotations, this); }
+
+ bool visit(AST::UiObjectDefinition *el) override
+ {
+ QmlObject scope;
+ scope.setName(toString(el->qualifiedTypeNameId));
+ scope.addPrototypePath(Paths::lookupTypePath(scope.name()));
+ QmlObject *sPtr = nullptr;
+ Path sPathFromOwner;
+ if (!arrayBindingLevels.isEmpty() && nodeStack.length() == arrayBindingLevels.last()) {
+ if (currentNode().kind == DomType::Binding) {
+ QList<QmlObject> *vals = std::get<Binding>(currentNode().value).arrayValue();
+ if (vals) {
+ int idx = vals->length();
+ vals->append(scope);
+ sPathFromOwner = currentNodeEl().path.field(Fields::value).index(idx);
+ sPtr = &((*vals)[idx]);
+ sPtr->updatePathFromOwner(sPathFromOwner);
+ } else {
+ Q_ASSERT_X(false, className,
+ "expected an array binding with a valid QList<QmlScope> as value");
+ }
+ } else {
+ Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
+ }
+ } else {
+ DomValue &containingObject = currentQmlObjectOrComponentEl().item;
+ switch (containingObject.kind) {
+ case DomType::QmlComponent:
+ sPathFromOwner =
+ std::get<QmlComponent>(containingObject.value).addObject(scope, &sPtr);
+ break;
+ case DomType::QmlObject:
+ sPathFromOwner = std::get<QmlObject>(containingObject.value).addChild(scope, &sPtr);
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
+ Q_ASSERT_X(sPtr, className, "could not recover new scope");
+ pushEl(sPathFromOwner, *sPtr, el);
+ loadAnnotations(el);
+ return true;
+ }
+
+ void endVisit(AST::UiObjectDefinition *) override
+ {
+ QmlObject &obj = current<QmlObject>();
+ int idx = currentIndex();
+ if (!arrayBindingLevels.isEmpty() && nodeStack.length() == arrayBindingLevels.last() + 1) {
+ if (currentNode(1).kind == DomType::Binding) {
+ Binding &b = std::get<Binding>(currentNode(1).value);
+ QList<QmlObject> *vals = b.arrayValue();
+ Q_ASSERT_X(vals, className,
+ "expected an array binding with a valid QList<QmlScope> as value");
+ (*vals)[idx] = obj;
+ } else {
+ Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
+ }
+ } else {
+ DomValue &containingObject = currentNodeEl(1).item;
+ Path p = currentNodeEl().path;
+ switch (containingObject.kind) {
+ case DomType::QmlComponent:
+ if (p[p.length() - 2] == Path::Field(Fields::objects))
+ std::get<QmlComponent>(containingObject.value).m_objects[idx] = obj;
+ else
+ Q_ASSERT(false);
+ break;
+ case DomType::QmlObject:
+ if (p[p.length() - 2] == Path::Field(Fields::children))
+ std::get<QmlObject>(containingObject.value).m_children[idx] = obj;
+ else
+ Q_ASSERT(false);
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
+ removeCurrentNode(DomType::QmlObject);
+ }
+
+ bool visit(AST::UiObjectBinding *el) override
+ {
+ BindingType bType = (el->hasOnToken ? BindingType::OnBinding : BindingType::Normal);
+ QmlObject value;
+ value.setName(toString(el->qualifiedTypeNameId));
+ Binding *bPtr;
+ Path bPathFromOwner = current<QmlObject>().addBinding(
+ Binding(toString(el->qualifiedId), value, bType), AddOption::KeepExisting, &bPtr);
+ if (bPtr->name() == u"id")
+ qmlFile.addError(myParseErrors()
+ .error(tr("id attributes should only be a lower case letter "
+ "followed by letters numbers or underscore"))
+ .withPath(bPathFromOwner));
+ pushEl(bPathFromOwner, *bPtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"colon", el->colonToken);
+ loadAnnotations(el);
+ QmlObject *objValue = bPtr->objectValue();
+ Q_ASSERT_X(objValue, className, "could not recover objectValue");
+ pushEl(bPathFromOwner.field(Fields::value), *objValue, el->initializer);
+ return true;
+ }
+
+ void endVisit(AST::UiObjectBinding *) override
+ {
+ QmlObject &objValue = current<QmlObject>();
+ QmlObject &containingObj = current<QmlObject>(1);
+ Binding &b = std::get<Binding>(currentNode(1).value);
+ QmlObject *objPtr = b.objectValue();
+ Q_ASSERT(objPtr);
+ *objPtr = objValue;
+ index_type idx = currentNodeEl(1).path.last().headIndex();
+ Binding *bPtr = valueFromMultimap(containingObj.m_bindings, b.name(), idx);
+ Q_ASSERT(bPtr);
+ *bPtr = b;
+ removeCurrentNode(DomType::QmlObject);
+ removeCurrentNode(DomType::Binding);
+ }
+
+ bool visit(AST::UiScriptBinding *el) override
+ {
+ QStringView code = qmlFilePtr->code();
+ SourceLocation loc = combineLocations(el->statement);
+ std::shared_ptr<ScriptExpression> script(
+ new ScriptExpression(code.mid(loc.offset, loc.length), qmlFilePtr->engine(),
+ el->statement, qmlFilePtr->astComments(),
+ ScriptExpression::ExpressionType::BindingExpression, loc));
+ Binding bindingV(toString(el->qualifiedId), script, BindingType::Normal);
+ Binding *bindingPtr = nullptr;
+ Id *idPtr = nullptr;
+ Path pathFromOwner;
+ if (bindingV.name() == u"id") {
+ Node *exp = script->ast();
+ if (ExpressionStatement *eStat = cast<ExpressionStatement *>(script->ast()))
+ exp = eStat->expression;
+ if (IdentifierExpression *iExp = cast<IdentifierExpression *>(exp)) {
+ StackEl &containingObjectEl = currentEl<QmlObject>();
+ QmlObject &containingObject = std::get<QmlObject>(containingObjectEl.item.value);
+ QString idName = iExp->name.toString();
+ Id idVal(idName, qmlFile.canonicalPath().path(containingObject.pathFromOwner()));
+ containingObject.setIdStr(idName);
+ FileLocations::addRegion(containingObjectEl.fileLocations, u"idToken",
+ combineLocations(el->qualifiedId));
+ FileLocations::addRegion(containingObjectEl.fileLocations, u"idColon",
+ el->colonToken);
+ FileLocations::addRegion(containingObjectEl.fileLocations, u"id",
+ combineLocations(el->statement));
+ QmlComponent &comp = current<QmlComponent>();
+ pathFromOwner = comp.addId(idVal, AddOption::KeepExisting, &idPtr);
+ QRegularExpression idRe(QRegularExpression::anchoredPattern(
+ QStringLiteral(uR"([[:lower:]][[:lower:][:upper:]0-9_]*)")));
+ auto m = idRe.match(iExp->name);
+ if (!m.hasMatch()) {
+ qmlFile.addError(
+ myParseErrors()
+ .error(tr("id attributes should only be a lower case letter "
+ "followed by letters numbers or underscore, not %1")
+ .arg(iExp->name))
+ .withPath(pathFromOwner));
+ }
+ } else {
+ pathFromOwner = current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting,
+ &bindingPtr);
+ Q_ASSERT_X(bindingPtr, className, "binding could not be retrived");
+ qmlFile.addError(
+ myParseErrors()
+ .error(tr("id attributes should only be a lower case letter "
+ "followed by letters numbers or underscore, not %1 %2")
+ .arg(script->code(), script->astRelocatableDump()))
+ .withPath(pathFromOwner)
+ .handle());
+ }
+ } else {
+ pathFromOwner =
+ current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
+ Q_ASSERT_X(bindingPtr, className, "binding could not be retrived");
+ }
+ if (bindingPtr)
+ pushEl(pathFromOwner, *bindingPtr, el);
+ else if (idPtr)
+ pushEl(pathFromOwner, *idPtr, el);
+ else
+ Q_ASSERT(false);
+ loadAnnotations(el);
+ // avoid duplicate colon location for id?
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"colon", el->colonToken);
+ return false;
+ }
+
+ void endVisit(AST::UiScriptBinding *) override
+ {
+ DomValue &lastEl = currentNode();
+ index_type idx = currentIndex();
+ if (lastEl.kind == DomType::Binding) {
+ Binding &b = std::get<Binding>(lastEl.value);
+ QmlObject &containingObject = current<QmlObject>();
+ Binding *bPtr = valueFromMultimap(containingObject.m_bindings, b.name(), idx);
+ Q_ASSERT(bPtr);
+ *bPtr = b;
+ } else if (lastEl.kind == DomType::Id) {
+ Id &id = std::get<Id>(lastEl.value);
+ QmlComponent &comp = current<QmlComponent>();
+ Id *idPtr = valueFromMultimap(comp.m_ids, id.name, idx);
+ *idPtr = id;
+ } else {
+ Q_ASSERT(false);
+ }
+ removeCurrentNode({});
+ }
+
+ bool visit(AST::UiArrayBinding *el) override
+ {
+ QList<QmlObject> value;
+ Binding bindingV(toString(el->qualifiedId), value, BindingType::Normal);
+ Binding *bindingPtr;
+ Path bindingPathFromOwner =
+ current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
+ if (bindingV.name() == u"id")
+ qmlFile.addError(
+ myParseErrors()
+ .error(tr("id attributes should have only simple strings as values"))
+ .withPath(bindingPathFromOwner));
+ pushEl(bindingPathFromOwner, *bindingPtr, el);
+ FileLocations::addRegion(currentNodeEl().fileLocations, u"colon", el->colonToken);
+ loadAnnotations(el);
+ FileLocations::Tree arrayList =
+ createMap(currentNodeEl().fileLocations, Path::Field(Fields::value), nullptr);
+ FileLocations::addRegion(arrayList, u"leftSquareBrace", el->lbracketToken);
+ FileLocations::addRegion(arrayList, u"rightSquareBrace", el->lbracketToken);
+ arrayBindingLevels.append(nodeStack.length());
+ return true;
+ }
+
+ void endVisit(AST::UiArrayBinding *) override
+ {
+ index_type idx = currentIndex();
+ Binding &b = std::get<Binding>(currentNode().value);
+ Binding *bPtr = valueFromMultimap(current<QmlObject>().m_bindings, b.name(), idx);
+ *bPtr = b;
+ arrayBindingLevels.removeLast();
+ removeCurrentNode(DomType::Binding);
+ }
+
+ bool visit(AST::UiParameterList *el) override
+ { // currently not used...
+ MethodParameter p {
+ el->name.toString(), toString(el->type), false, false, false, {}, {}, {}
+ };
+ return true;
+ }
+ void endVisit(AST::UiParameterList *el) override
+ {
+ Node::accept(el->next, this); // put other args at the same level as this one...
+ }
+
+ bool visit(AST::UiQualifiedId *) override { return false; }
+
+ bool visit(AST::UiEnumDeclaration *el) override
+ {
+ EnumDecl eDecl;
+ eDecl.setName(el->name.toString());
+ EnumDecl *ePtr;
+ Path enumPathFromOwner =
+ current<QmlComponent>().addEnumeration(eDecl, AddOption::KeepExisting, &ePtr);
+ pushEl(enumPathFromOwner, *ePtr, el);
+ loadAnnotations(el);
+ return true;
+ }
+
+ void endVisit(AST::UiEnumDeclaration *) override
+ {
+ EnumDecl &e = std::get<EnumDecl>(currentNode().value);
+ EnumDecl *ePtr =
+ valueFromMultimap(current<QmlComponent>().m_enumerations, e.name(), currentIndex());
+ Q_ASSERT(ePtr);
+ *ePtr = e;
+ removeCurrentNode(DomType::EnumDecl);
+ }
+
+ bool visit(AST::UiEnumMemberList *el) override
+ {
+ EnumItem it(el->member.toString(), el->value);
+ EnumDecl &eDecl = std::get<EnumDecl>(currentNode().value);
+ Path itPathFromDecl = eDecl.addValue(it);
+ FileLocations::addRegion(createMap(DomType::EnumItem, itPathFromDecl, nullptr), QString(),
+ combine(el->memberToken, el->valueToken));
+ return true;
+ }
+
+ void endVisit(AST::UiEnumMemberList *el) override
+ {
+ Node::accept(el->next, this); // put other enum members at the same level as this one...
+ }
+
+ bool visit(AST::UiInlineComponent *el) override
+ {
+ QStringList els = current<QmlComponent>().name().split(QLatin1Char('.'));
+ els.append(el->name.toString());
+ QString cName = els.join(QLatin1Char('.'));
+ QmlComponent *compPtr;
+ Path p = qmlFilePtr->addComponent(QmlComponent(cName), AddOption::KeepExisting, &compPtr);
+ pushEl(p, *compPtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"component", el->componentToken);
+ loadAnnotations(el);
+ return true;
+ }
+
+ void endVisit(AST::UiInlineComponent *) override
+ {
+ QmlComponent &component = std::get<QmlComponent>(currentNode().value);
+ QStringList nameEls = component.name().split(QChar::fromLatin1('.'));
+ QString key = nameEls.mid(1).join(QChar::fromLatin1('.'));
+ QmlComponent *cPtr = valueFromMultimap(qmlFilePtr->m_components, key, currentIndex());
+ Q_ASSERT(cPtr);
+ *cPtr = component;
+ removeCurrentNode(DomType::QmlComponent);
+ }
+
+ bool visit(UiRequired *el) override
+ {
+ PropertyDefinition pDef;
+ pDef.name = el->name.toString();
+ pDef.isRequired = true;
+ PropertyDefinition *pDefPtr;
+ Path pathFromOwner =
+ current<QmlObject>().addPropertyDef(pDef, AddOption::KeepExisting, &pDefPtr);
+ createMap(DomType::PropertyDefinition, pathFromOwner, el);
+ return false;
+ }
+
+ bool visit(AST::UiAnnotation *el) override
+ {
+ QmlObject a;
+ a.setName(QStringLiteral(u"@") + toString(el->qualifiedTypeNameId));
+ // add annotation prototype?
+ DomValue &containingElement = currentNode();
+ Path pathFromOwner;
+ QmlObject *aPtr = nullptr;
+ switch (containingElement.kind) {
+ case DomType::QmlObject:
+ pathFromOwner = std::get<QmlObject>(containingElement.value).addAnnotation(a, &aPtr);
+ break;
+ case DomType::Binding:
+ pathFromOwner = std::get<Binding>(containingElement.value)
+ .addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ case DomType::Id:
+ pathFromOwner = std::get<Id>(containingElement.value)
+ .addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ case DomType::PropertyDefinition:
+ pathFromOwner = std::get<PropertyDefinition>(containingElement.value)
+ .addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ case DomType::MethodInfo:
+ pathFromOwner = std::get<MethodInfo>(containingElement.value)
+ .addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ default:
+ qCWarning(domLog) << "Unexpected container object for annotation:"
+ << domTypeToString(containingElement.kind);
+ Q_ASSERT(false);
+ }
+ pushEl(pathFromOwner, *aPtr, el);
+ return true;
+ }
+
+ void endVisit(AST::UiAnnotation *) override
+ {
+ DomValue &containingElement = currentNode(1);
+ Path pathFromOwner;
+ QmlObject &a = std::get<QmlObject>(currentNode().value);
+ switch (containingElement.kind) {
+ case DomType::QmlObject:
+ std::get<QmlObject>(containingElement.value).m_annotations[currentIndex()] = a;
+ break;
+ case DomType::Binding:
+ std::get<Binding>(containingElement.value).m_annotations[currentIndex()] = a;
+ break;
+ case DomType::Id:
+ std::get<Id>(containingElement.value).annotations[currentIndex()] = a;
+ break;
+ case DomType::PropertyDefinition:
+ std::get<PropertyDefinition>(containingElement.value).annotations[currentIndex()] = a;
+ break;
+ case DomType::MethodInfo:
+ std::get<MethodInfo>(containingElement.value).annotations[currentIndex()] = a;
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ removeCurrentNode(DomType::QmlObject);
+ }
+
+ void throwRecursionDepthError() override
+ {
+ qmlFile.addError(myParseErrors().error(
+ tr("Maximum statement or expression depth exceeded in QmlDomAstCreator")));
+ }
+};
+
+void createDom(MutableDomItem qmlFile)
+{
+ if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
+ QmlDomAstCreator componentCreator(qmlFile);
+ AST::Node::accept(qmlFilePtr->ast(), &componentCreator);
+ AstComments::collectComments(qmlFile);
+ } else {
+ qCWarning(creatorLog) << "createDom called on non qmlFile";
+ }
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomastcreator_p.h b/src/qmldom/qqmldomastcreator_p.h
new file mode 100644
index 0000000000..b37e2cfa36
--- /dev/null
+++ b/src/qmldom/qqmldomastcreator_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#ifndef QQMLDOMASTCREATOR_P_H
+#define QQMLDOMASTCREATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldom_global.h"
+#include "qqmldomitem_p.h"
+#include "qqmldomastcreator_p.h"
+#include "qqmldomcomments_p.h"
+
+#include <QtQml/private/qqmljsastvisitor_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+SourceLocation combineLocations(SourceLocation s1, SourceLocation s2);
+SourceLocation combineLocations(AST::Node *n);
+
+void createDom(MutableDomItem qmlFile);
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
+#endif // QQMLDOMASTCREATOR_P_H
diff --git a/src/qmldom/qqmldomastdumper.cpp b/src/qmldom/qqmldomastdumper.cpp
index a20c405a00..f1a4abe8e4 100644
--- a/src/qmldom/qqmldomastdumper.cpp
+++ b/src/qmldom/qqmldomastdumper.cpp
@@ -36,6 +36,7 @@
** $QT_END_LICENSE$
**/
#include "qqmldomastdumper_p.h"
+#include "qqmldomerrormessage_p.h"
#include <QtQml/private/qqmljsast_p.h>
#include <QtCore/QDebug>
#include <QtCore/QString>
@@ -139,14 +140,15 @@ private:
.replace(QLatin1String("\""),QLatin1String("\\\""));
if (trim)
tokenStr = tokenStr.trimmed();
- if (noLocations() || !s.isValid())
+ if (noLocations() || s == SourceLocation())
return QLatin1String("\"%1\"").arg(tokenStr);
else {
return QLatin1String("\"off:%1 len:%2 l:%3 c:%4 %5\"").arg(QString::number(s.offset), QString::number(s.length), QString::number(s.startLine), QString::number(s.startColumn), tokenStr);
}
}
- QString semicolonToken(const SourceLocation &s) {
+ QString semicolonToken(const SourceLocation &s)
+ {
if (options & AstDumperOption::SloppyCompare)
return QString();
return QLatin1String(" semicolonToken=") + loc(s);
@@ -166,14 +168,18 @@ public:
bool visit(UiPragma *el) override {
start(QLatin1String("UiPragma name=%1 pragmaToken=%2%3")
- .arg(quotedString(el->name), loc(el->pragmaToken), semicolonToken(el->semicolonToken)));
+ .arg(quotedString(el->name), loc(el->pragmaToken),
+ semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::UiPragma *) override { stop(u"UiPragma"); }
bool visit(UiImport *el) override {
- start(QLatin1String("UiImport fileName=%1 importId=%2 importToken=%3 fileNameToken=%4 asToken=%5 importIdToken=%6%7")
- .arg(quotedString(el->fileName), quotedString(el->importId), loc(el->importToken), loc(el->fileNameToken), loc(el->asToken), loc(el->importIdToken), semicolonToken(el->semicolonToken)));
+ start(QLatin1String("UiImport fileName=%1 importId=%2 importToken=%3 fileNameToken=%4 "
+ "asToken=%5 importIdToken=%6%7")
+ .arg(quotedString(el->fileName), quotedString(el->importId),
+ loc(el->importToken), loc(el->fileNameToken), loc(el->asToken),
+ loc(el->importIdToken), semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::UiImport *el) override {
@@ -184,15 +190,18 @@ public:
bool visit(UiPublicMember *el) override {
QString typeStr = ((el->type == UiPublicMember::Signal) ? QLatin1String("Signal") :
(el->type == UiPublicMember::Property) ? QLatin1String("Property") : QLatin1String("Unexpected(%1)").arg(QString::number(el->type)));
- start(QLatin1String("UiPublicMember type=%1 typeModifier=%2 name=%3 isDefaultMember=%4 isReadonlyMember=%5 isRequired=%6 "
- "defaultToken=%7 readonlyToken=%8 propertyToken=%9 requiredToken=%10 typeModifierToken=%11 typeToken=%12 "
+ start(QLatin1String("UiPublicMember type=%1 typeModifier=%2 name=%3 isDefaultMember=%4 "
+ "isReadonlyMember=%5 isRequired=%6 "
+ "defaultToken=%7 readonlyToken=%8 propertyToken=%9 requiredToken=%10 "
+ "typeModifierToken=%11 typeToken=%12 "
"identifierToken=%13 colonToken=%14%15")
- .arg(quotedString(typeStr), quotedString(el->typeModifier), quotedString(el->name),
- boolStr(el->isDefaultMember), boolStr(el->isReadonlyMember), boolStr(el->isRequired),
- loc(el->defaultToken), loc(el->readonlyToken), loc(el->propertyToken),
- loc(el->requiredToken), loc(el->typeModifierToken), loc(el->typeToken),
- loc(el->identifierToken), loc(el->colonToken), semicolonToken(el->semicolonToken)
- ));
+ .arg(quotedString(typeStr), quotedString(el->typeModifier),
+ quotedString(el->name), boolStr(el->isDefaultMember),
+ boolStr(el->isReadonlyMember), boolStr(el->isRequired),
+ loc(el->defaultToken), loc(el->readonlyToken), loc(el->propertyToken),
+ loc(el->requiredToken), loc(el->typeModifierToken), loc(el->typeToken),
+ loc(el->identifierToken), loc(el->colonToken),
+ semicolonToken(el->semicolonToken)));
if (!noAnnotations()) // put annotations inside the node they refer to
Node::accept(el->annotations, this);
Node::accept(el->memberType, this);
@@ -321,7 +330,8 @@ public:
bool visit(UiRequired *el) override {
start(QLatin1String("UiRequired name=%1 requiredToken=%2%3")
- .arg(quotedString(el->name), loc(el->requiredToken), semicolonToken(el->semicolonToken)));
+ .arg(quotedString(el->name), loc(el->requiredToken),
+ semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(UiRequired *) override { stop(u"UiRequired"); }
@@ -688,8 +698,7 @@ public:
void endVisit(AST::VariableDeclarationList *) override { stop(u"VariableDeclarationList"); }
bool visit(AST::EmptyStatement *el) override {
- start(QLatin1String("EmptyStatement%1")
- .arg(semicolonToken(el->semicolonToken)));
+ start(QLatin1String("EmptyStatement%1").arg(semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::EmptyStatement *) override { stop(u"EmptyStatement"); }
@@ -698,8 +707,7 @@ public:
if (options & AstDumperOption::SloppyCompare)
start(u"ExpressionStatement");
else
- start(QLatin1String("ExpressionStatement%1")
- .arg(semicolonToken(el->semicolonToken)));
+ start(QLatin1String("ExpressionStatement%1").arg(semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::ExpressionStatement *) override { stop(u"ExpressionStatement"); }
@@ -712,8 +720,10 @@ public:
void endVisit(AST::IfStatement *) override { stop(u"IfStatement"); }
bool visit(AST::DoWhileStatement *el) override {
- start(QLatin1String("DoWhileStatement doToken=%1 whileToken=%2 lparenToken=%3 rparenToken=%4%5")
- .arg(loc(el->doToken), loc(el->whileToken), loc(el->lparenToken), loc(el->rparenToken), semicolonToken(el->semicolonToken)));
+ start(QLatin1String(
+ "DoWhileStatement doToken=%1 whileToken=%2 lparenToken=%3 rparenToken=%4%5")
+ .arg(loc(el->doToken), loc(el->whileToken), loc(el->lparenToken),
+ loc(el->rparenToken), semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::DoWhileStatement *) override { stop(u"DoWhileStatement"); }
@@ -728,10 +738,13 @@ public:
bool visit(AST::ForStatement *el) override {
if (options & AstDumperOption::SloppyCompare)
start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 rparenToken=%5")
- .arg(loc(el->forToken), loc(el->lparenToken), loc(el->rparenToken)));
+ .arg(loc(el->forToken), loc(el->lparenToken), loc(el->rparenToken)));
else
- start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 firstSemicolonToken=%3 secondSemicolonToken=%4 rparenToken=%5")
- .arg(loc(el->forToken), loc(el->lparenToken), loc(el->firstSemicolonToken), loc(el->secondSemicolonToken), loc(el->rparenToken)));
+ start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 firstSemicolonToken=%3 "
+ "secondSemicolonToken=%4 rparenToken=%5")
+ .arg(loc(el->forToken), loc(el->lparenToken),
+ loc(el->firstSemicolonToken), loc(el->secondSemicolonToken),
+ loc(el->rparenToken)));
return true;
}
void endVisit(AST::ForStatement *) override { stop(u"ForStatement"); }
@@ -745,21 +758,23 @@ public:
bool visit(AST::ContinueStatement *el) override {
start(QLatin1String("ContinueStatement label=%1 continueToken=%2 identifierToken=%3%4")
- .arg(quotedString(el->label), loc(el->continueToken), loc(el->identifierToken), semicolonToken(el->semicolonToken)));
+ .arg(quotedString(el->label), loc(el->continueToken),
+ loc(el->identifierToken), semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::ContinueStatement *) override { stop(u"ContinueStatement"); }
bool visit(AST::BreakStatement *el) override {
start(QLatin1String("BreakStatement label=%1 breakToken=%2 identifierToken=%3%4")
- .arg(quotedString(el->label), loc(el->breakToken), loc(el->identifierToken), semicolonToken(el->semicolonToken)));
+ .arg(quotedString(el->label), loc(el->breakToken), loc(el->identifierToken),
+ semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::BreakStatement *) override { stop(u"BreakStatement"); }
bool visit(AST::ReturnStatement *el) override {
start(QLatin1String("ReturnStatement returnToken=%1%2")
- .arg(loc(el->returnToken), semicolonToken(el->semicolonToken)));
+ .arg(loc(el->returnToken), semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::ReturnStatement *) override { stop(u"ReturnStatement"); }
@@ -821,7 +836,7 @@ public:
bool visit(AST::ThrowStatement *el) override {
start(QLatin1String("ThrowStatement throwToken=%1%2")
- .arg(loc(el->throwToken), semicolonToken(el->semicolonToken)));
+ .arg(loc(el->throwToken), semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::ThrowStatement *) override { stop(u"ThrowStatement"); }
@@ -986,7 +1001,7 @@ public:
bool visit(AST::DebuggerStatement *el) override {
start(QLatin1String("DebuggerStatement debuggerToken=%1%2")
- .arg(loc(el->debuggerToken), semicolonToken(el->semicolonToken)));
+ .arg(loc(el->debuggerToken), semicolonToken(el->semicolonToken)));
return true;
}
void endVisit(AST::DebuggerStatement *) override { stop(u"DebuggerStatement"); }
@@ -1011,7 +1026,7 @@ public:
void endVisit(AST::TypeAnnotation *) override { stop(u"TypeAnnotation"); }
void throwRecursionDepthError() override {
- qDebug() << "Maximum statement or expression depth exceeded in AstDumper";
+ qCWarning(domLog) << "Maximum statement or expression depth exceeded in AstDumper";
}
private:
diff --git a/src/qmldom/qqmldomattachedinfo.cpp b/src/qmldom/qqmldomattachedinfo.cpp
index be01fd05c5..c86279cff6 100644
--- a/src/qmldom/qqmldomattachedinfo.cpp
+++ b/src/qmldom/qqmldomattachedinfo.cpp
@@ -60,39 +60,60 @@ Attributes:
\sa QQmlJs::Dom::AttachedInfo
*/
-bool FileLocations::iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)> visitor)
+bool FileLocations::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
bool cont = true;
+#ifdef QmlDomAddCodeStr
+ bool hasCode = false;
QString codeStr = self.fileObject().field(Fields::code).value().toString();
- auto loc2str = [codeStr](SourceLocation loc) {
- return QStringView(codeStr).mid(loc.offset, loc.length);
+ auto loc2str = [&self, &codeStr](SourceLocation loc) {
+ if (loc.offset < codeStr.length() && loc.end() <= codeStr.length())
+ return QStringView(codeStr).mid(loc.offset, loc.length);
+ return QStringView();
};
- cont = cont && self.subDataField(Fields::fullRegion, locationToData(fullRegion)).visit(visitor);
- cont = cont && self.subMap(
- Map::fromMapRef<SourceLocation>(
- self.pathFromOwner().field(Fields::regions), regions, [&loc2str](const DomItem &map, Path key, SourceLocation &el){
- return map.subDataPath(key, locationToData(el, loc2str(el))).item;
- })).visit(visitor);
- cont = cont && self.subMap(
- Map::fromMapRef<QList<SourceLocation>>(
- self.pathFromOwner().field(Fields::preCommentLocations), preCommentLocations,
- [&loc2str](const DomItem &map, Path key, QList<SourceLocation> &el){
- return map.subList(List::fromQListRef<SourceLocation>(
- map.pathFromOwner().path(key), el,
- [&loc2str](const DomItem &list, Path idx, SourceLocation &el){
- return list.subDataPath(idx, locationToData(el, loc2str(el))).item;
- })).item;
- })).visit(visitor);
- cont = cont && self.subMap(
- Map::fromMapRef<QList<SourceLocation>>(
- self.pathFromOwner().field(Fields::postCommentLocations), postCommentLocations,
- [&loc2str](const DomItem &map, Path key, QList<SourceLocation> &el){
- return map.subList(List::fromQListRef<SourceLocation>(
- map.pathFromOwner().path(key), el,
- [&loc2str](const DomItem &list, Path idx, SourceLocation &el){
- return list.subDataPath(idx, locationToData(el, loc2str(el))).item;
- })).item;
- })).visit(visitor);
+#else
+ auto loc2str = [](SourceLocation) { return QStringView(); };
+#endif
+ cont = cont && self.dvValueLazyField(visitor, Fields::fullRegion, [this]() {
+ return locationToData(fullRegion);
+ });
+ cont = cont && self.dvItemField(visitor, Fields::regions, [this, &self, &loc2str]() {
+ return self.subMapItem(Map::fromMapRef<SourceLocation>(
+ self.pathFromOwner().field(Fields::regions), regions,
+ [&loc2str](DomItem &map, const PathEls::PathComponent &key, SourceLocation &el) {
+ return map.subLocationItem(key, el, loc2str(el));
+ }));
+ });
+ cont = cont
+ && self.dvItemField(visitor, Fields::preCommentLocations, [this, &self, &loc2str]() {
+ return self.subMapItem(Map::fromMapRef<QList<SourceLocation>>(
+ self.pathFromOwner().field(Fields::preCommentLocations),
+ preCommentLocations,
+ [&loc2str](DomItem &map, const PathEls::PathComponent &key,
+ QList<SourceLocation> &el) {
+ return map.subListItem(List::fromQListRef<SourceLocation>(
+ map.pathFromOwner().appendComponent(key), el,
+ [&loc2str](DomItem &list, const PathEls::PathComponent &idx,
+ SourceLocation &el) {
+ return list.subLocationItem(idx, el, loc2str(el));
+ }));
+ }));
+ });
+ cont = cont
+ && self.dvItemField(visitor, Fields::postCommentLocations, [this, &self, &loc2str]() {
+ return self.subMapItem(Map::fromMapRef<QList<SourceLocation>>(
+ self.pathFromOwner().field(Fields::postCommentLocations),
+ postCommentLocations,
+ [&loc2str](DomItem &map, const PathEls::PathComponent &key,
+ QList<SourceLocation> &el) {
+ return map.subListItem(List::fromQListRef<SourceLocation>(
+ map.pathFromOwner().appendComponent(key), el,
+ [&loc2str](DomItem &list, const PathEls::PathComponent &idx,
+ SourceLocation &el) {
+ return list.subLocationItem(idx, el, loc2str(el));
+ }));
+ }));
+ });
return cont;
}
@@ -113,17 +134,17 @@ FileLocations::Tree FileLocations::ensure(FileLocations::Tree base, Path basePat
}
AttachedInfoLookupResult<FileLocations::Tree>
-FileLocations::findAttachedInfo(const DomItem &item, AttachedInfo::FindOptions options)
+FileLocations::findAttachedInfo(DomItem &item, AttachedInfo::FindOptions options)
{
return AttachedInfoT<FileLocations>::findAttachedInfo(item, Fields::fileLocationsTree, options);
}
-FileLocations::Tree FileLocations::treePtr(const DomItem &item)
+FileLocations::Tree FileLocations::treePtr(DomItem &item)
{
return AttachedInfoT<FileLocations>::treePtr(item, Fields::fileLocationsTree);
}
-const FileLocations *FileLocations::fileLocationsPtr(const DomItem &item)
+const FileLocations *FileLocations::fileLocationsPtr(DomItem &item)
{
if (FileLocations::Tree t = treePtr(item))
return &(t->info());
@@ -173,24 +194,33 @@ Attributes:
\sa QQmlJs::Dom::AttachedInfo
*/
-bool AttachedInfo::iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)> visitor)
+bool AttachedInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
bool cont = true;
if (Ptr p = parent())
- cont = cont && Subpath{Path::Field(Fields::parent), DomItem(self.m_top, p, self.m_ownerPath.dropTail(2), p.get())}.visit(visitor);
- cont = cont && self.subDataField(Fields::path, path().toString()).visit(visitor);
- cont = cont && self.subMap(
- Map(Path::Field(Fields::subItems),
- [this](const DomItem &map, QString key){
- Path p = Path::fromString(key);
- return map.copy(m_subItems.value(p), map.canonicalPath().key(key));
- },[this](const DomItem &) {
- QSet<QString> res;
- for (auto p : m_subItems.keys())
- res.insert(p.toString());
- return res;
- }, QLatin1String("AttachedInfo"))).visit(visitor);
- cont = cont && Subpath{Path::Field(Fields::infoItem), infoItem(self)}.visit(visitor);
+ cont = cont && self.dvItemField(visitor, Fields::parent, [&self, p]() {
+ return self.copy(p, self.m_ownerPath.dropTail(2), p.get());
+ });
+ cont = cont
+ && self.dvValueLazyField(visitor, Fields::path, [this]() { return path().toString(); });
+ cont = cont && self.dvItemField(visitor, Fields::subItems, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::subItems),
+ [this](DomItem &map, QString key) {
+ Path p = Path::fromString(key);
+ return map.copy(m_subItems.value(p), map.canonicalPath().key(key));
+ },
+ [this](DomItem &) {
+ QSet<QString> res;
+ for (auto p : m_subItems.keys())
+ res.insert(p.toString());
+ return res;
+ },
+ QLatin1String("AttachedInfo")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::infoItem, [&self, this]() {
+ return infoItem(self);
+ });
return cont;
}
@@ -252,7 +282,7 @@ AttachedInfo::Ptr AttachedInfo::find(AttachedInfo::Ptr self, Path p, AttachedInf
}
AttachedInfoLookupResult<AttachedInfo::Ptr>
-AttachedInfo::findAttachedInfo(const DomItem &item, QStringView fieldName,
+AttachedInfo::findAttachedInfo(DomItem &item, QStringView fieldName,
AttachedInfo::FindOptions options)
{
Path p;
@@ -262,7 +292,7 @@ AttachedInfo::findAttachedInfo(const DomItem &item, QStringView fieldName,
// canonical path, and PathType::Canonical instead...
DomItem o = item.owner();
p = item.pathFromOwner();
- DomItem fLoc = o.field(fieldName);
+ fLoc = o.field(fieldName);
while (!fLoc && o) {
DomItem c = o.container();
p = c.pathFromOwner().path(o.canonicalPath().last()).path(p);
@@ -276,8 +306,17 @@ AttachedInfo::findAttachedInfo(const DomItem &item, QStringView fieldName,
if (AttachedInfo::Ptr foundTree =
AttachedInfo::find(fLocPtr, p, AttachedInfo::PathType::Relative))
res.foundTree = foundTree;
- if (options & FindOption::SetRootTreePath)
+ if (options & (FindOption::SetRootTreePath | FindOption::SetFoundTreePath))
res.rootTreePath = fLoc.canonicalPath();
+ if (options & FindOption::SetFoundTreePath) {
+ Path foundTreePath = res.rootTreePath.value();
+ if (res.lookupPath) {
+ foundTreePath = foundTreePath.key(res.lookupPath.head().toString());
+ for (Path pEl : res.lookupPath.mid(1))
+ foundTreePath = foundTreePath.field(Fields::subItems).key(pEl.toString());
+ }
+ res.foundTreePath = foundTreePath;
+ }
return res;
}
diff --git a/src/qmldom/qqmldomattachedinfo_p.h b/src/qmldom/qqmldomattachedinfo_p.h
index d7cf9909e7..ea080f3fbc 100644
--- a/src/qmldom/qqmldomattachedinfo_p.h
+++ b/src/qmldom/qqmldomattachedinfo_p.h
@@ -66,6 +66,7 @@ public:
TreePtr foundTree;
Path lookupPath; // relative path used to reach result
std::optional<Path> rootTreePath; // path of the root TreePath
+ std::optional<Path> foundTreePath;
operator bool() { return bool(foundTree); }
template<typename T>
AttachedInfoLookupResult<std::shared_ptr<T>> as() const
@@ -86,7 +87,12 @@ public:
Canonical
};
Q_ENUM(PathType)
- enum class FindOption { None = 0, SetRootTreePath = 0x1, Default = SetRootTreePath };
+ enum class FindOption {
+ None = 0,
+ SetRootTreePath = 0x1,
+ SetFoundTreePath = 0x2,
+ Default = 0x3
+ };
Q_DECLARE_FLAGS(FindOptions, FindOption)
Q_FLAG(FindOptions)
@@ -94,12 +100,11 @@ public:
using Ptr = std::shared_ptr<AttachedInfo>;
DomType kind() const override { return kindValue; }
- Path canonicalPath(const DomItem &self) const override {
- return self.m_ownerPath;
- }
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)> visitor) override;
+ Path canonicalPath(DomItem &self) const override { return self.m_ownerPath; }
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
- AttachedInfo::Ptr makeCopy(const DomItem &self) const {
+ AttachedInfo::Ptr makeCopy(DomItem &self) const
+ {
return std::static_pointer_cast<AttachedInfo>(doCopy(self));
}
@@ -113,13 +118,15 @@ public:
static Ptr ensure(Ptr self, Path path, PathType pType = PathType::Relative);
static Ptr find(Ptr self, Path p, PathType pType = PathType::Relative);
static AttachedInfoLookupResult<Ptr>
- findAttachedInfo(const DomItem &item, QStringView treeFieldName,
+ findAttachedInfo(DomItem &item, QStringView treeFieldName,
FindOptions options = AttachedInfo::FindOption::None);
- static Ptr treePtr(const DomItem &item, QStringView fieldName) {
+ static Ptr treePtr(DomItem &item, QStringView fieldName)
+ {
return findAttachedInfo(item, fieldName, FindOption::None).foundTree;
}
- DomItem itemAtPath(const DomItem &self, Path p, PathType pType = PathType::Relative) const {
+ DomItem itemAtPath(DomItem &self, Path p, PathType pType = PathType::Relative) const
+ {
if (Ptr resPtr = find(self.ownerAs<AttachedInfo>(), p, pType)) {
if (pType == PathType::Canonical)
p = p.mid(m_path.length());
@@ -132,11 +139,14 @@ public:
return DomItem();
}
- DomItem infoAtPath(const DomItem &self, Path p, PathType pType = PathType::Relative) const {
+ DomItem infoAtPath(DomItem &self, Path p, PathType pType = PathType::Relative) const
+ {
return itemAtPath(self, p, pType).field(Fields::infoItem);
}
- MutableDomItem ensureItemAtPath(const MutableDomItem &self, Path p, PathType pType = PathType::Relative) {
+ MutableDomItem ensureItemAtPath(MutableDomItem &self, Path p,
+ PathType pType = PathType::Relative)
+ {
if (Ptr resPtr = ensure(self.ownerAs<AttachedInfo>(), p, pType)) {
if (pType == PathType::Canonical)
p = p.mid(m_path.length());
@@ -144,18 +154,21 @@ public:
for (Path pEl : p) {
resPath = resPath.field(Fields::subItems).key(pEl.toString());
}
- return MutableDomItem(self.base().copy(resPtr, resPath));
+ return MutableDomItem(self.item().copy(resPtr, resPath));
}
return MutableDomItem();
}
- MutableDomItem ensureInfoAtPath(const MutableDomItem &self, Path p, PathType pType = PathType::Relative) {
+ MutableDomItem ensureInfoAtPath(MutableDomItem &self, Path p,
+ PathType pType = PathType::Relative)
+ {
return ensureItemAtPath(self, p, pType).field(Fields::infoItem);
}
virtual AttachedInfo::Ptr instantiate(AttachedInfo::Ptr parent, Path p = Path()) const = 0;
- virtual DomItem infoItem(const DomItem &self) = 0;
- DomItem infoItem(const DomItem &self) const {
+ virtual DomItem infoItem(DomItem &self) = 0;
+ DomItem infoItem(DomItem &self) const
+ {
return const_cast<AttachedInfo *>(this)->infoItem(self);
}
QMap<Path, Ptr> subItems() const {
@@ -169,10 +182,13 @@ protected:
std::weak_ptr<AttachedInfo> m_parent;
QMap<Path, Ptr> m_subItems;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(AttachedInfo::FindOptions)
-template <typename Info>
-class QMLDOM_EXPORT AttachedInfoT : public AttachedInfo {
+template<typename Info>
+class QMLDOM_EXPORT AttachedInfoT final : public AttachedInfo
+{
public:
+ constexpr static DomType kindValue = DomType::AttachedInfo;
using Ptr = std::shared_ptr<AttachedInfoT>;
using InfoType = Info;
@@ -201,13 +217,14 @@ public:
return std::static_pointer_cast<AttachedInfoT>(AttachedInfo::find(self, p, pType));
}
- static AttachedInfoLookupResult<Ptr>
- findAttachedInfo(const DomItem &item, QStringView fieldName, AttachedInfo::FindOptions options)
+ static AttachedInfoLookupResult<Ptr> findAttachedInfo(DomItem &item, QStringView fieldName,
+ AttachedInfo::FindOptions options)
{
return AttachedInfo::findAttachedInfo(item, fieldName, options)
.template as<AttachedInfoT>();
}
- static Ptr treePtr(const DomItem &item, QStringView fieldName) {
+ static Ptr treePtr(DomItem &item, QStringView fieldName)
+ {
return std::static_pointer_cast<AttachedInfoT>(AttachedInfo::treePtr(item, fieldName));
}
static bool visitTree(Ptr base, function_ref<bool(Path, Ptr)>visitor, Path basePath = Path()) {
@@ -231,11 +248,10 @@ public:
AttachedInfo::Ptr instantiate(AttachedInfo::Ptr parent, Path p = Path()) const override {
return Ptr(new AttachedInfoT(std::static_pointer_cast<AttachedInfoT>(parent), p));
}
- DomItem infoItem(const DomItem &self) override {
- return self.subWrapField(Fields::infoItem, m_info).item;
- }
+ DomItem infoItem(DomItem &self) override { return self.wrapField(Fields::infoItem, m_info); }
- Ptr makeCopy(const DomItem &self) const {
+ Ptr makeCopy(DomItem &self) const
+ {
return std::static_pointer_cast<AttachedInfoT>(doCopy(self));
}
@@ -244,9 +260,11 @@ public:
const Info &info() const { return m_info; }
Info &info() { return m_info; }
protected:
- std::shared_ptr<OwningItem> doCopy(const DomItem &) const override {
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
return Ptr(new AttachedInfoT(*this));
}
+
private:
Info m_info;
};
@@ -256,21 +274,25 @@ public:
using Tree = std::shared_ptr<AttachedInfoT<FileLocations>>;
constexpr static DomType kindValue = DomType::FileLocations;
DomType kind() const { return kindValue; }
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>);
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
void ensureCommentLocations(QList<QString> keys);
static Tree createTree(Path basePath);
static Tree ensure(Tree base, Path basePath, AttachedInfo::PathType pType);
+ static Tree find(Tree self, Path p,
+ AttachedInfo::PathType pType = AttachedInfo::PathType::Relative)
+ {
+ return AttachedInfoT<FileLocations>::find(self, p, pType);
+ }
// returns the path looked up and the found tree when looking for the info attached to item
static AttachedInfoLookupResult<Tree>
- findAttachedInfo(const DomItem &item,
+ findAttachedInfo(DomItem &item,
AttachedInfo::FindOptions options = AttachedInfo::FindOption::Default);
// convenience: find FileLocations::Tree attached to the given item
- static FileLocations::Tree treePtr(const DomItem &);
+ static FileLocations::Tree treePtr(DomItem &);
// convenience: find FileLocations* attached to the given item (if there is one)
- static const FileLocations *fileLocationsPtr(const DomItem &);
-
+ static const FileLocations *fileLocationsPtr(DomItem &);
static void updateFullLocation(Tree fLoc, SourceLocation loc);
static void addRegion(Tree fLoc, QString locName, SourceLocation loc);
diff --git a/src/qmldom/qqmldomcomments.cpp b/src/qmldom/qqmldomcomments.cpp
new file mode 100644
index 0000000000..d1f386c6be
--- /dev/null
+++ b/src/qmldom/qqmldomcomments.cpp
@@ -0,0 +1,714 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmldomcomments_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldomexternalitems_p.h"
+#include "qqmldomastdumper_p.h"
+#include "qqmldomattachedinfo_p.h"
+
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+
+#include <QtCore/QSet>
+
+#include <variant>
+
+static Q_LOGGING_CATEGORY(commentsLog, "qt.qmldom.comments", QtWarningMsg);
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+/*!
+\internal
+\class QQmlJS::Dom::AstComments
+
+\brief Associates comments with AST::Node *
+
+Comments are associated to the largest closest node with the
+following algorithm:
+\list
+\li comments of a node can either be preComments or postComments (before
+or after the element)
+\li define start and end for each element, if two elements start (or end)
+ at the same place the first (larger) wins.
+\li associate the comments either with the element just before or
+just after unless the comments is *inside* an element (meaning that
+going back there is a start before finding an end, or going forward an
+end is met before a start).
+\li to choose between the element before or after, we look at the start
+of the comment, if it is on a new line then associating it as
+preComment to the element after is preferred, otherwise post comment
+of the previous element (inline element).
+This is the only space dependent choice, making comment assignment
+quite robust
+\li if the comment is intrinsically inside all elements then it is moved
+to before the smallest element.
+This is the largest reorganization performed, and it is still quite
+small and difficult to trigger.
+\li the comments are stored with the whitespace surrounding them, from
+the preceding newline (and recording if a newline is required before
+it) until the newline after.
+This allows a better reproduction of the comments.
+\endlist
+*/
+/*!
+\class QQmlJS::Dom::CommentInfo
+
+\brief Extracts various pieces and information out of a rawComment string
+
+Comments store a string (rawComment) with comment characters (//,..) and spaces.
+Sometime one wants just the comment, the commentcharacters, the space before the comment,....
+CommentInfo gets such a raw comment string and makes the various pieces available
+*/
+CommentInfo::CommentInfo(QStringView rawComment) : rawComment(rawComment)
+{
+ commentBegin = 0;
+ while (commentBegin < quint32(rawComment.length()) && rawComment.at(commentBegin).isSpace()) {
+ if (rawComment.at(commentBegin) == QLatin1Char('\n'))
+ hasStartNewline = true;
+ ++commentBegin;
+ }
+ if (commentBegin < quint32(rawComment.length())) {
+ QString expectedEnd;
+ switch (rawComment.at(commentBegin).unicode()) {
+ case '/':
+ commentStartStr = rawComment.mid(commentBegin, 2);
+ if (commentStartStr == u"/*") {
+ expectedEnd = QStringLiteral(u"*/");
+ } else {
+ if (commentStartStr == u"//") {
+ expectedEnd = QStringLiteral(u"\n");
+ } else {
+ warnings.append(tr("Unexpected comment start %1").arg(commentStartStr));
+ }
+ }
+ break;
+ case '#':
+ commentStartStr = rawComment.mid(commentBegin, 1);
+ expectedEnd = QStringLiteral(u"\n");
+ break;
+ default:
+ commentStartStr = rawComment.mid(commentBegin, 1);
+ warnings.append(tr("Unexpected comment start %1").arg(commentStartStr));
+ break;
+ }
+ commentEnd = commentBegin + commentStartStr.size();
+ quint32 rawEnd = quint32(rawComment.length());
+ while (commentEnd < rawEnd && rawComment.at(commentEnd).isSpace())
+ ++commentEnd;
+ commentContentEnd = commentContentBegin = commentEnd;
+ QChar e1 = ((expectedEnd.isEmpty()) ? QChar::fromLatin1(0) : expectedEnd.at(0));
+ while (commentEnd < rawEnd) {
+ QChar c = rawComment.at(commentEnd);
+ if (c == e1) {
+ if (expectedEnd.length() > 1) {
+ if (++commentEnd < rawEnd && rawComment.at(commentEnd) == expectedEnd.at(1)) {
+ Q_ASSERT(expectedEnd.length() == 2);
+ commentEndStr = rawComment.mid(++commentEnd - 2, 2);
+ break;
+ } else {
+ commentContentEnd = commentEnd;
+ }
+ } else {
+ commentEndStr = rawComment.mid(++commentEnd - 1, 1);
+ break;
+ }
+ } else if (!c.isSpace()) {
+ commentContentEnd = commentEnd;
+ } else if (c == QLatin1Char('\n')) {
+ ++nContentNewlines;
+ } else if (c == QLatin1Char('\r')) {
+ if (expectedEnd == QStringLiteral(u"\n")) {
+ if (commentEnd + 1 < rawEnd
+ && rawComment.at(commentEnd + 1) == QLatin1Char('\n')) {
+ ++commentEnd;
+ commentEndStr = rawComment.mid(++commentEnd - 2, 2);
+ } else {
+ commentEndStr = rawComment.mid(++commentEnd - 1, 1);
+ }
+ break;
+ } else if (commentEnd + 1 == rawEnd
+ || rawComment.at(commentEnd + 1) != QLatin1Char('\n')) {
+ ++nContentNewlines;
+ }
+ }
+ ++commentEnd;
+ }
+
+ if (commentEnd > 0
+ && (rawComment.at(commentEnd - 1) == QLatin1Char('\n')
+ || rawComment.at(commentEnd - 1) == QLatin1Char('\r')))
+ hasEndNewline = true;
+ quint32 i = commentEnd;
+ while (i < rawEnd && rawComment.at(i).isSpace()) {
+ if (rawComment.at(i) == QLatin1Char('\n') || rawComment.at(i) == QLatin1Char('\r'))
+ hasEndNewline = true;
+ ++i;
+ }
+ if (i < rawEnd) {
+ warnings.append(tr("Non whitespace char %1 after comment end at %2")
+ .arg(rawComment.at(i))
+ .arg(i));
+ }
+ }
+}
+
+/*!
+\class QQmlJS::Dom::Comment
+
+\brief Represents a comment
+
+Comments are not needed for execute the program, so they are aimed to the programmer,
+and have few functions: explaining code, adding extra info/context (who did write,
+when licensing,...) or disabling code.
+Being for the programmer and being non functional it is difficult to treat them properly.
+So preserving them as much as possible is the best course of action.
+
+To acheive this comment is represented by
+\list
+\li newlinesBefore: the number of newlines before the comment, to preserve spacing between
+comments (the extraction routines limit this to 2 at most, i.e. a single empty line) \li
+rawComment: a string with the actual comment including whitespace before and after and the
+comment characters (whitespace before is limited to spaces/tabs to preserve indentation or
+spacing just before starting the comment) \endlist The rawComment is a bit annoying if one wants
+to change the comment, or extract information from it. For this reason info gives access to the
+various elements of it: the comment characters #, // or /
+*, the space before it, and the actual comment content.
+
+the comments are stored with the whitespace surrounding them, from
+the preceding newline (and recording if a newline is required before
+it) until the newline after.
+
+A comment has methods to write it out again (write) and expose it to the Dom
+(iterateDirectSubpaths).
+*/
+
+/*!
+\brief Expose attributes to the Dom
+*/
+bool Comment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::rawComment, rawComment());
+ cont = cont && self.dvValueField(visitor, Fields::newlinesBefore, newlinesBefore());
+ return cont;
+}
+
+/*!
+\class QQmlJS::Dom::CommentedElement
+\brief Keeps the comment associated with an element
+
+A comment can be attached to an element (that is always a range of the file with a start and
+end) only in two ways: it can precede the region (preComments), or follow it (postComments).
+*/
+
+/*!
+\class QQmlJS::Dom::RegionComments
+\brief Keeps the comments associated with a DomItem
+
+A DomItem can be more complex that just a start/end, it can have multiple regions, for example
+a return or a function token might define a region.
+The empty string is the region that represents the whole element.
+
+Every region has a name, and should be written out using the OutWriter.writeRegion (or
+startRegion/ EndRegion). Region comments keeps a mapping containing them.
+*/
+
+bool CommentedElement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvWrapField(visitor, Fields::preComments, preComments);
+ cont = cont && self.dvWrapField(visitor, Fields::postComments, postComments);
+ return cont;
+}
+
+/*!
+\brief Given the SourceLocation of the current element returns the comments associated with the
+start and end of item
+
+The map uses an index that is based on 2*the location. Thus for every location l it is possible
+to have two indexes: 2*l (just before) and 2*l+1 (just after).
+This allows to attach comments to indexes representing either just
+*/
+QMultiMap<quint32, const QList<Comment> *>
+CommentedElement::commentGroups(SourceLocation elLocation) const
+{
+ return QMultiMap<quint32, const QList<Comment> *>(
+ { { elLocation.begin() * 2, &preComments },
+ { elLocation.end() * 2 + 1, &postComments } });
+}
+
+using namespace QQmlJS::AST;
+
+class RegionRef
+{
+public:
+ Path path; // store the MutableDomItem instead?
+ QString regionName;
+};
+
+// internal class to keep a reference either to an AST::Node* or a region of a DomItem and the
+// size of that region
+class ElementRef
+{
+public:
+ ElementRef(AST::Node *node, quint32 size) : element(node), size(size) { }
+ ElementRef(Path path, QString region, quint32 size)
+ : element(RegionRef { path, region }), size(size)
+ {
+ }
+ operator bool() const
+ {
+ return (element.index() == 0 && std::get<0>(element)) || element.index() == 1 || size != 0;
+ }
+ ElementRef() = default;
+
+ std::variant<AST::Node *, RegionRef> element;
+ quint32 size = 0;
+};
+
+/*!
+\class QQmlJS::Dom::VisitAll
+\brief A vistor that visits all the AST:Node
+
+The default visitor does not necessarily visit all nodes, because some part
+of the AST are typically handled manually. This visitor visits *all* AST
+elements contained.
+
+Note: Subclasses should take care to call the parent (i.e. this) visit/endVisit
+methods when overriding them, to guarantee that all element are really visited
+*/
+
+/*!
+returns a set with all Ui* Nodes (i.e. the top level non javascript Qml)
+*/
+QSet<int> VisitAll::uiKinds()
+{
+ static QSet<int> res({ AST::Node::Kind_UiObjectMemberList, AST::Node::Kind_UiArrayMemberList,
+ AST::Node::Kind_UiParameterList, AST::Node::Kind_UiHeaderItemList,
+ AST::Node::Kind_UiEnumMemberList, AST::Node::Kind_UiAnnotationList,
+
+ AST::Node::Kind_UiArrayBinding, AST::Node::Kind_UiImport,
+ AST::Node::Kind_UiObjectBinding, AST::Node::Kind_UiObjectDefinition,
+ AST::Node::Kind_UiInlineComponent, AST::Node::Kind_UiObjectInitializer,
+ AST::Node::Kind_UiPragma, AST::Node::Kind_UiProgram,
+ AST::Node::Kind_UiPublicMember, AST::Node::Kind_UiQualifiedId,
+ AST::Node::Kind_UiScriptBinding, AST::Node::Kind_UiSourceElement,
+ AST::Node::Kind_UiEnumDeclaration, AST::Node::Kind_UiVersionSpecifier,
+ AST::Node::Kind_UiRequired, AST::Node::Kind_UiAnnotation });
+ return res;
+}
+
+// internal private class to set all the starts/ends of the nodes/regions
+class AstRangesVisitor final : protected VisitAll
+{
+public:
+ AstRangesVisitor() = default;
+
+ void addNodeRanges(AST::Node *rootNode);
+ void addItemRanges(DomItem item, FileLocations::Tree itemLocations, Path currentP);
+
+ void throwRecursionDepthError() override { }
+
+ static const QSet<int> kindsToSkip();
+
+ bool preVisit(Node *n) override
+ {
+ if (!kindsToSkip().contains(n->kind)) {
+ quint32 start = n->firstSourceLocation().begin();
+ quint32 end = n->lastSourceLocation().end();
+ if (!starts.contains(start))
+ starts.insert(start, { n, end - start });
+ if (!ends.contains(end))
+ ends.insert(end, { n, end - start });
+ }
+ return true;
+ }
+
+ QQmlJS::Engine *engine;
+ FileLocations::Tree rootItemLocations;
+ QMap<quint32, ElementRef> starts;
+ QMap<quint32, ElementRef> ends;
+};
+
+void AstRangesVisitor::addNodeRanges(AST::Node *rootNode)
+{
+ AST::Node::accept(rootNode, this);
+}
+
+void AstRangesVisitor::addItemRanges(DomItem item, FileLocations::Tree itemLocations, Path currentP)
+{
+ if (!itemLocations) {
+ if (item)
+ qCWarning(commentsLog) << "reached item" << item.canonicalPath() << "without locations";
+ return;
+ }
+ DomItem comments = item.field(Fields::comments);
+ if (comments) {
+ auto regs = itemLocations->info().regions;
+ for (auto it = regs.cbegin(), end = regs.cend(); it != end; ++it) {
+ quint32 startI = it.value().begin();
+ quint32 endI = it.value().end();
+ if (!starts.contains(startI))
+ starts.insert(startI, { currentP, it.key(), quint32(endI - startI) });
+ if (!ends.contains(endI))
+ ends.insert(endI, { currentP, it.key(), endI - startI });
+ }
+ }
+ {
+ auto subMaps = itemLocations->subItems();
+ for (auto it = subMaps.begin(), end = subMaps.end(); it != end; ++it) {
+ addItemRanges(item.path(it.key()),
+ std::static_pointer_cast<AttachedInfoT<FileLocations>>(it.value()),
+ currentP.path(it.key()));
+ }
+ }
+}
+
+const QSet<int> AstRangesVisitor::kindsToSkip()
+{
+ static QSet<int> res = QSet<int>({
+ AST::Node::Kind_ArgumentList,
+ AST::Node::Kind_ElementList,
+ AST::Node::Kind_FormalParameterList,
+ AST::Node::Kind_ImportsList,
+ AST::Node::Kind_ExportsList,
+ AST::Node::Kind_PropertyDefinitionList,
+ AST::Node::Kind_StatementList,
+ AST::Node::Kind_VariableDeclarationList,
+ AST::Node::Kind_ClassElementList,
+ AST::Node::Kind_PatternElementList,
+ AST::Node::Kind_PatternPropertyList,
+ AST::Node::Kind_TypeArgumentList,
+ })
+ .unite(VisitAll::uiKinds());
+ return res;
+}
+
+/*!
+\class QQmlJS::Dom::AstComments
+\brief Stores the comments associated with javascript AST::Node pointers
+*/
+
+bool AstComments::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = self.dvItemField(visitor, Fields::commentedElements, [this, &self]() {
+ return self.subMapItem(Map(
+ self.pathFromOwner().field(Fields::commentedElements),
+ [this](DomItem &map, QString key) {
+ bool ok;
+ // we expose the comments as map just for debugging purposes,
+ // as key we use the address hex value as key (keys must be strings)
+ quintptr v = key.split(QLatin1Char('_')).last().toULong(&ok, 16);
+ // recover the actual key, and check if it is in the map
+ AST::Node *n = reinterpret_cast<AST::Node *>(v);
+ if (ok && m_commentedElements.contains(n))
+ return map.wrap(PathEls::Key(key), m_commentedElements[n]);
+ return DomItem();
+ },
+ [this](DomItem &) {
+ QSet<QString> res;
+ for (AST::Node *n : m_commentedElements.keys()) {
+ QString name;
+ if (n)
+ name = QString::number(n->kind); // we should add mapping to
+ // string for this
+ res.insert(name + QStringLiteral(u"_") + QString::number(quintptr(n), 16));
+ }
+ return res;
+ },
+ QLatin1String("CommentedElements")));
+ });
+ return cont;
+}
+
+void AstComments::collectComments(MutableDomItem &item)
+{
+ if (std::shared_ptr<ScriptExpression> scriptPtr = item.ownerAs<ScriptExpression>()) {
+ DomItem itemItem = item.item();
+ return collectComments(scriptPtr->engine(), scriptPtr->ast(), scriptPtr->astComments(),
+ item, FileLocations::treePtr(itemItem));
+ } else if (std::shared_ptr<QmlFile> qmlFilePtr = item.ownerAs<QmlFile>()) {
+ return collectComments(qmlFilePtr->engine(), qmlFilePtr->ast(), qmlFilePtr->astComments(),
+ item, qmlFilePtr->fileLocationsTree());
+ } else {
+ qCWarning(commentsLog)
+ << "collectComments works with QmlFile and ScriptExpression, not with"
+ << item.internalKindStr();
+ }
+}
+
+/*!
+\brief
+Collects and associates comments with javascript AST::Node pointers and MutableDomItem in
+rootItem
+*/
+void AstComments::collectComments(std::shared_ptr<Engine> engine, AST::Node *n,
+ std::shared_ptr<AstComments> ccomm, MutableDomItem rootItem,
+ FileLocations::Tree rootItemLocations)
+{
+ if (!n)
+ return;
+ AstRangesVisitor ranges;
+ ranges.addItemRanges(rootItem.item(), rootItemLocations, Path());
+ ranges.addNodeRanges(n);
+ QStringView code = engine->code();
+ QHash<AST::Node *, CommentedElement> &commentedElements = ccomm->m_commentedElements;
+ quint32 lastPostCommentPostEnd = 0;
+ for (SourceLocation cLoc : engine->comments()) {
+ // collect whitespace before and after cLoc -> iPre..iPost contains whitespace,
+ // do not add newline before, but add the one after
+ quint32 iPre = cLoc.begin();
+ int preNewline = 0;
+ QStringView commentStartStr;
+ while (iPre > 0) {
+ QChar c = code.at(iPre - 1);
+ if (!c.isSpace()) {
+ if (commentStartStr.isEmpty() && (c == QLatin1Char('*') || c == QLatin1Char('/'))
+ && iPre - 1 > 0 && code.at(iPre - 2) == QLatin1Char('/')) {
+ commentStartStr = code.mid(iPre - 2, 2);
+ --iPre;
+ } else {
+ break;
+ }
+ } else if (c == QLatin1Char('\n') || c == QLatin1Char('\r')) {
+ preNewline = 1;
+ // possibly add an empty line if it was there (but never more than one)
+ int i = iPre - 1;
+ if (c == QLatin1Char('\n') && i > 0 && code.at(i - 1) == QLatin1Char('\r'))
+ --i;
+ while (i > 0 && code.at(--i).isSpace()) {
+ c = code.at(i);
+ if (c == QLatin1Char('\n') || c == QLatin1Char('\r')) {
+ ++preNewline;
+ break;
+ }
+ }
+ break;
+ }
+ --iPre;
+ }
+ if (iPre == 0)
+ preNewline = 1;
+ quint32 iPost = cLoc.end();
+ while (iPost < code.size()) {
+ QChar c = code.at(iPost);
+ if (!c.isSpace()) {
+ if (!commentStartStr.isEmpty() && commentStartStr.at(1) == QLatin1Char('*')
+ && c == QLatin1Char('*') && iPost + 1 < code.size()
+ && code.at(iPost + 1) == QLatin1Char('/')) {
+ commentStartStr = QStringView();
+ ++iPost;
+ } else {
+ break;
+ }
+ }
+ ++iPost;
+ if (c == QLatin1Char('\n'))
+ break;
+ if (c == QLatin1Char('\r')) {
+ if (iPost < code.size() && code.at(iPost) == QLatin1Char('\n'))
+ ++iPost;
+ break;
+ }
+ }
+ ElementRef commentEl;
+ bool pre = true;
+ auto iStart = ranges.starts.lowerBound(cLoc.begin());
+ auto iEnd = ranges.ends.lowerBound(cLoc.begin());
+ Q_ASSERT(!ranges.ends.isEmpty() && !ranges.starts.isEmpty());
+
+ auto checkElementBefore = [&]() {
+ if (commentEl)
+ return;
+ // prefer post comment attached to preceding element
+ auto preEnd = iEnd;
+ auto preStart = iStart;
+ if (preEnd != ranges.ends.begin()) {
+ --preEnd;
+ if (iStart == ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
+ // iStart == begin should never happen
+ // check that we do not have operators (or in general other things) between
+ // preEnd and this because inserting a newline too ealy might invalidate the
+ // expression (think a + //comment\n b ==> a // comment\n + b), in this
+ // case attaching as preComment of iStart (b in the example) should be
+ // preferred as it is safe
+ quint32 i = iPre;
+ while (i != 0 && code.at(--i).isSpace())
+ ;
+ if (i <= preEnd.key() || i < lastPostCommentPostEnd
+ || iEnd == ranges.ends.end()) {
+ commentEl = preEnd.value();
+ pre = false;
+ lastPostCommentPostEnd = iPost + 1; // ensure the previous check works
+ // with multiple post comments
+ }
+ }
+ }
+ };
+ auto checkElementAfter = [&]() {
+ if (commentEl)
+ return;
+ if (iStart != ranges.starts.end()) {
+ // try to add a pre comment of following element
+ if (iEnd == ranges.ends.end() || iEnd.key() > iStart.key()) {
+ // there is no end of element before iStart begins
+ // associate the comment as preComment of iStart
+ // (btw iEnd == end should never happen here)
+ commentEl = iStart.value();
+ return;
+ }
+ }
+ if (iStart == ranges.starts.begin()) {
+ Q_ASSERT(iStart != ranges.starts.end());
+ // we are before the first node (should be handled already by previous case)
+ commentEl = iStart.value();
+ }
+ };
+ auto checkInsideEl = [&]() {
+ if (commentEl)
+ return;
+ auto preIStart = iStart;
+ if (iStart == ranges.starts.begin()) {
+ commentEl = iStart.value(); // checkElementAfter should have handled this
+ return;
+ } else {
+ --preIStart;
+ }
+ // we are inside a node, actually inside both n1 and n2 (which might be the same)
+ // add to pre of the smallest between n1 and n2.
+ // This is needed because if there are multiple nodes starting/ending at the same
+ // place we store only the first (i.e. largest)
+ ElementRef n1 = preIStart.value();
+ ElementRef n2 = iEnd.value();
+ if (n1.size > n2.size)
+ commentEl = n2;
+ else
+ commentEl = n1;
+ };
+ if (!preNewline) {
+ checkElementBefore();
+ checkElementAfter();
+ } else {
+ checkElementAfter();
+ checkElementBefore();
+ }
+ if (!commentEl)
+ checkInsideEl();
+ if (!commentEl) {
+ qCWarning(commentsLog) << "Could not assign comment at" << locationToData(cLoc)
+ << "adding before root node";
+ if (rootItem && (rootItemLocations || !n)) {
+ commentEl.element = RegionRef { Path(), QString() };
+ commentEl.size =
+ rootItemLocations->info()
+ .regions.value(QString(), rootItemLocations->info().fullRegion)
+ .length;
+ // attach to rootItem
+ } else if (n) {
+ commentEl.element = n;
+ commentEl.size = n->lastSourceLocation().end() - n->firstSourceLocation().begin();
+ }
+ }
+ Comment comment(code.mid(iPre, iPost - iPre), preNewline);
+ if (commentEl.element.index() == 0 && std::get<0>(commentEl.element)) {
+ CommentedElement &cEl = commentedElements[std::get<0>(commentEl.element)];
+ if (pre)
+ cEl.preComments.append(comment);
+ else
+ cEl.postComments.append(comment);
+ } else if (commentEl.element.index() == 1) {
+ DomItem rComments = rootItem.item()
+ .path(std::get<1>(commentEl.element).path)
+ .field(Fields::comments);
+ if (RegionComments *rCommentsPtr = rComments.mutableAs<RegionComments>()) {
+ if (pre)
+ rCommentsPtr->addPreComment(comment, std::get<1>(commentEl.element).regionName);
+ else
+ rCommentsPtr->addPostComment(comment,
+ std::get<1>(commentEl.element).regionName);
+ } else {
+ Q_ASSERT(false);
+ }
+ } else {
+ qCWarning(commentsLog)
+ << "Failed: no item or node to attach comment" << comment.rawComment();
+ }
+ }
+}
+
+// internal class to collect all comments in a node or its subnodes
+class CommentCollectorVisitor : protected VisitAll
+{
+public:
+ CommentCollectorVisitor(AstComments *comments, AST::Node *n) : comments(comments)
+ {
+ AST::Node::accept(n, this);
+ }
+
+ void throwRecursionDepthError() override { }
+
+ bool preVisit(Node *n) override
+ {
+ auto &cEls = comments->commentedElements();
+ if (cEls.contains(n))
+ nodeComments += cEls[n].commentGroups(
+ combine(n->firstSourceLocation(), n->lastSourceLocation()));
+ return true;
+ }
+
+ AstComments *comments;
+ QMultiMap<quint32, const QList<Comment> *> nodeComments;
+};
+
+/*!
+\brief low level method returns all comments in a node (including its subnodes)
+
+The comments are roughly ordered in the order they appear in the file.
+Multiple values are in reverse order if the index is even.
+*/
+QMultiMap<quint32, const QList<Comment> *> AstComments::allCommentsInNode(AST::Node *n)
+{
+ CommentCollectorVisitor v(this, n);
+ return v.nodeComments;
+}
+
+bool RegionComments::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ if (!regionComments.isEmpty())
+ cont = cont && self.dvWrapField(visitor, Fields::regionComments, regionComments);
+ return cont;
+}
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomcomments_p.h b/src/qmldom/qqmldomcomments_p.h
new file mode 100644
index 0000000000..0dd605a9ad
--- /dev/null
+++ b/src/qmldom/qqmldomcomments_p.h
@@ -0,0 +1,339 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDOMCOMMENTS_P_H
+#define QQMLDOMCOMMENTS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldom_fwd_p.h"
+#include "qqmldomconstants_p.h"
+#include "qqmldomfunctionref_p.h"
+#include "qqmldomitem_p.h"
+#include "qqmldomattachedinfo_p.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+
+#include <QtCore/QMultiMap>
+#include <QtCore/QHash>
+#include <QtCore/QStack>
+#include <QtCore/QCoreApplication>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+class QMLDOM_EXPORT CommentInfo
+{
+ Q_DECLARE_TR_FUNCTIONS(CommentInfo)
+public:
+ CommentInfo(QStringView);
+
+ QStringView preWhitespace() const { return rawComment.mid(0, commentBegin); }
+
+ QStringView comment() const { return rawComment.mid(commentBegin, commentEnd - commentBegin); }
+
+ QStringView commentContent() const
+ {
+ return rawComment.mid(commentContentBegin, commentContentEnd - commentContentEnd);
+ }
+
+ QStringView postWhitespace() const
+ {
+ return rawComment.mid(commentEnd, rawComment.size() - commentEnd);
+ }
+
+ quint32 commentBegin;
+ quint32 commentEnd;
+ quint32 commentContentBegin;
+ quint32 commentContentEnd;
+ QStringView commentStartStr;
+ QStringView commentEndStr;
+ bool hasStartNewline = false;
+ bool hasEndNewline = false;
+ int nContentNewlines;
+ QStringView rawComment;
+ QStringList warnings;
+};
+
+class QMLDOM_EXPORT Comment
+{
+public:
+ constexpr static DomType kindValue = DomType::Comment;
+ DomType kind() const { return kindValue; }
+
+ Comment(QString c, int newlinesBefore = 1)
+ : m_commentStr(c), m_comment(m_commentStr), m_newlinesBefore(newlinesBefore)
+ {
+ }
+ Comment(QStringView c, int newlinesBefore = 1) : m_comment(c), m_newlinesBefore(newlinesBefore)
+ {
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ int newlinesBefore() const { return m_newlinesBefore; }
+ void setNewlinesBefore(int n) { m_newlinesBefore = n; }
+ QStringView rawComment() const { return m_comment; }
+ CommentInfo info() const { return CommentInfo(m_comment); }
+ void write(OutWriter &lw, SourceLocation *commentLocation = nullptr) const;
+
+ friend bool operator==(const Comment &c1, const Comment &c2)
+ {
+ return c1.m_newlinesBefore == c2.m_newlinesBefore && c1.m_comment == c2.m_comment;
+ }
+ friend bool operator!=(const Comment &c1, const Comment &c2) { return !(c1 == c2); }
+
+private:
+ QString m_commentStr;
+ QStringView m_comment;
+ int m_newlinesBefore;
+};
+
+class QMLDOM_EXPORT CommentedElement
+{
+public:
+ constexpr static DomType kindValue = DomType::CommentedElement;
+ DomType kind() const { return kindValue; }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ void writePre(OutWriter &lw, QList<SourceLocation> *locations = nullptr) const;
+ void writePost(OutWriter &lw, QList<SourceLocation> *locations = nullptr) const;
+ QMultiMap<quint32, const QList<Comment> *> commentGroups(SourceLocation elLocation) const;
+
+ friend bool operator==(const CommentedElement &c1, const CommentedElement &c2)
+ {
+ return c1.preComments == c2.preComments && c1.postComments == c2.postComments;
+ }
+ friend bool operator!=(const CommentedElement &c1, const CommentedElement &c2)
+ {
+ return !(c1 == c2);
+ }
+
+ QList<Comment> preComments;
+ QList<Comment> postComments;
+};
+
+class QMLDOM_EXPORT RegionComments
+{
+public:
+ constexpr static DomType kindValue = DomType::RegionComments;
+ DomType kind() const { return kindValue; }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+
+ friend bool operator==(const RegionComments &c1, const RegionComments &c2)
+ {
+ return c1.regionComments == c2.regionComments;
+ }
+ friend bool operator!=(const RegionComments &c1, const RegionComments &c2)
+ {
+ return !(c1 == c2);
+ }
+
+ Path addPreComment(const Comment &comment, QString regionName)
+ {
+ auto &preList = regionComments[regionName].preComments;
+ index_type idx = preList.length();
+ preList.append(comment);
+ return Path::Field(Fields::regionComments)
+ .key(regionName)
+ .field(Fields::preComments)
+ .index(idx);
+ }
+
+ Path addPostComment(const Comment &comment, QString regionName)
+ {
+ auto &postList = regionComments[regionName].postComments;
+ index_type idx = postList.length();
+ postList.append(comment);
+ return Path::Field(Fields::regionComments)
+ .key(regionName)
+ .field(Fields::postComments)
+ .index(idx);
+ }
+
+ QMap<QString, CommentedElement> regionComments;
+};
+
+class QMLDOM_EXPORT AstComments final : public OwningItem
+{
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ return std::shared_ptr<OwningItem>(new AstComments(*this));
+ }
+
+public:
+ constexpr static DomType kindValue = DomType::AstComments;
+ DomType kind() const override { return kindValue; }
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ std::shared_ptr<AstComments> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<AstComments>(doCopy(self));
+ }
+
+ Path canonicalPath(DomItem &self) const override { return self.m_ownerPath; }
+ static void collectComments(MutableDomItem &item);
+ static void collectComments(std::shared_ptr<Engine> engine, AST::Node *n,
+ std::shared_ptr<AstComments> collectComments,
+ MutableDomItem rootItem, FileLocations::Tree rootItemLocations);
+ AstComments(std::shared_ptr<Engine> e) : m_engine(e) { }
+ AstComments(const AstComments &o)
+ : OwningItem(o), m_engine(o.m_engine), m_commentedElements(o.m_commentedElements)
+ {
+ }
+
+ const QHash<AST::Node *, CommentedElement> &commentedElements() const
+ {
+ return m_commentedElements;
+ }
+ CommentedElement *commentForNode(AST::Node *n)
+ {
+ if (m_commentedElements.contains(n))
+ return &(m_commentedElements[n]);
+ return nullptr;
+ }
+ QMultiMap<quint32, const QList<Comment> *> allCommentsInNode(AST::Node *n);
+
+private:
+ std::shared_ptr<Engine> m_engine;
+ QHash<AST::Node *, CommentedElement> m_commentedElements;
+};
+
+class VisitAll : public AST::Visitor
+{
+public:
+ VisitAll() = default;
+
+ static QSet<int> uiKinds();
+
+ void throwRecursionDepthError() override { }
+
+ bool visit(AST::UiPublicMember *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ AST::Node::accept(el->memberType, this);
+ return true;
+ }
+
+ bool visit(AST::UiSourceElement *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ return true;
+ }
+
+ bool visit(AST::UiObjectDefinition *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ return true;
+ }
+
+ bool visit(AST::UiObjectBinding *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ return true;
+ }
+
+ bool visit(AST::UiScriptBinding *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ return true;
+ }
+
+ bool visit(AST::UiArrayBinding *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ return true;
+ }
+
+ bool visit(AST::UiParameterList *el) override
+ {
+ AST::Node::accept(el->type, this);
+ return true;
+ }
+
+ bool visit(AST::UiQualifiedId *el) override
+ {
+ AST::Node::accept(el->next, this);
+ return true;
+ }
+
+ bool visit(AST::UiEnumDeclaration *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ return true;
+ }
+
+ bool visit(AST::UiInlineComponent *el) override
+ {
+ AST::Node::accept(el->annotations, this);
+ return true;
+ }
+
+ void endVisit(AST::UiImport *el) override { AST::Node::accept(el->version, this); }
+ void endVisit(AST::UiPublicMember *el) override { AST::Node::accept(el->parameters, this); }
+
+ void endVisit(AST::UiParameterList *el) override
+ {
+ AST::Node::accept(el->next, this); // put other args at the same level as this one...
+ }
+
+ void endVisit(AST::UiEnumMemberList *el) override
+ {
+ AST::Node::accept(el->next,
+ this); // put other enum members at the same level as this one...
+ }
+
+ bool visit(AST::TemplateLiteral *el) override
+ {
+ AST::Node::accept(el->expression, this);
+ return true;
+ }
+
+ void endVisit(AST::Elision *el) override
+ {
+ AST::Node::accept(el->next, this); // emit other elisions at the same level
+ }
+};
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#endif // QQMLDOMCOMMENTS_P_H
diff --git a/src/qmldom/qqmldomconstants_p.h b/src/qmldom/qqmldomconstants_p.h
index 1faa840065..e9a2b8c753 100644
--- a/src/qmldom/qqmldomconstants_p.h
+++ b/src/qmldom/qqmldomconstants_p.h
@@ -86,6 +86,10 @@ enum class PathCurrent {
Lookup
};
Q_ENUM_NS(PathCurrent)
+
+enum class Language { QmlQuick1, QmlQuick2, QmlQuick3, QmlCompiled, QmlAnnotation, Qbs };
+Q_ENUM_NS(Language)
+
enum class ResolveOption{
None=0,
TraceVisit=0x1 // call the function along all elements of the path, not just for the target (the function might be called even if the target is never reached)
@@ -94,16 +98,42 @@ Q_ENUM_NS(ResolveOption)
Q_DECLARE_FLAGS(ResolveOptions, ResolveOption)
Q_DECLARE_OPERATORS_FOR_FLAGS(ResolveOptions)
-enum class VisitOption{
- None=0,
- VisitAdopted=0x1, // Visit adopted types (but never recurses)
- Recurse=0x2, // recurse non adopted types
- NoPath=0x4 // does not generate path consistent with visit
+enum class VisitOption {
+ None = 0,
+ VisitSelf = 0x1, // Visit the start item
+ VisitAdopted = 0x2, // Visit adopted types (but never recurses them)
+ Recurse = 0x4, // recurse non adopted types
+ NoPath = 0x8, // does not generate path consistent with visit
+ Default = VisitOption::VisitSelf | VisitOption::VisitAdopted | VisitOption::Recurse
};
Q_ENUM_NS(VisitOption)
Q_DECLARE_FLAGS(VisitOptions, VisitOption)
Q_DECLARE_OPERATORS_FOR_FLAGS(VisitOptions)
+enum class LookupOption {
+ Normal = 0,
+ Strict = 0x1,
+ VisitTopClassType = 0x2, // static lookup of class (singleton) or attached type, the default is
+ // visiting instance methods
+ SkipFirstScope = 0x4
+};
+Q_ENUM_NS(LookupOption)
+Q_DECLARE_FLAGS(LookupOptions, LookupOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(LookupOptions)
+
+enum class LookupType { PropertyDef, Binding, Property, Method, Type, CppType, Symbol };
+Q_ENUM_NS(LookupType)
+
+enum class VisitPrototypesOption {
+ Normal = 0,
+ SkipFirst = 0x1,
+ RevisitWarn = 0x2,
+ ManualProceedToScope = 0x4
+};
+Q_ENUM_NS(VisitPrototypesOption)
+Q_DECLARE_FLAGS(VisitPrototypesOptions, VisitPrototypesOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(VisitPrototypesOptions)
+
enum class DomKind {
Empty,
Object,
@@ -137,6 +167,7 @@ enum class DomType {
ModuleAutoExport, // dependent imports to automatically load when a module is imported
ModuleIndex, // index for all the imports of a major version
ModuleScope, // a specific import with full version
+ ImportScope, // the scope including the types coming from one or more imports
Export, // An exported type
// header stuff
@@ -150,21 +181,29 @@ enum class DomType {
SimpleObjectWrap,
ScriptExpression,
Reference,
- Binding,
PropertyDefinition,
- RequiredProperty,
+ Binding,
MethodParameter,
MethodInfo,
Version, // wrapped
+ Comment,
+ CommentedElement,
+ RegionComments,
+ AstComments,
FileLocations,
+ UpdatedScriptExpression,
- // generic objects, mainly for debug support
- GenericObject,
- GenericOwner,
+ // convenience collecting types
+ PropertyInfo,
+
+ // Moc objects, mainly for testing
+ MockObject,
+ MockOwner,
// containers
Map,
List,
+ ListP,
// supporting objects
LoadInfo, // owning
@@ -177,6 +216,17 @@ enum class DomType {
};
Q_ENUM_NS(DomType)
+enum class SimpleWrapOption { None = 0, ValueType = 1 };
+Q_ENUM_NS(SimpleWrapOption)
+Q_DECLARE_FLAGS(SimpleWrapOptions, SimpleWrapOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleWrapOptions)
+
+enum class BindingValueKind { Object, ScriptExpression, Array, Empty };
+Q_ENUM_NS(BindingValueKind)
+
+enum class BindingType { Normal, OnBinding };
+Q_ENUM_NS(BindingType)
+
enum class ListOptions {
Normal,
Reverse
@@ -188,7 +238,8 @@ enum class LoadOption {
ForceLoad = 0x1,
};
Q_ENUM_NS(LoadOption)
-Q_DECLARE_FLAGS(LoadOptions, LoadOption);
+Q_DECLARE_FLAGS(LoadOptions, LoadOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(LoadOptions)
enum class EscapeOptions{
OuterQuotes,
@@ -222,6 +273,11 @@ enum class GoTo {
};
Q_ENUM_NS(GoTo)
+enum class AddOption { KeepExisting, Overwrite };
+Q_ENUM_NS(AddOption)
+
+enum class FilterUpOptions { ReturnOuter, ReturnOuterNoSelf, ReturnInner };
+Q_ENUM_NS(FilterUpOptions)
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomelements.cpp b/src/qmldom/qqmldomelements.cpp
new file mode 100644
index 0000000000..2600035512
--- /dev/null
+++ b/src/qmldom/qqmldomelements.cpp
@@ -0,0 +1,1263 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#include "qqmldomelements_p.h"
+#include "qqmldomcomments_p.h"
+#include "qqmldomastdumper_p.h"
+#include "qqmldommock_p.h"
+#include "qqmldomtop_p.h"
+#include "qqmldomexternalitems_p.h"
+
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+
+#include <QtCore/QScopeGuard>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QDir>
+#include <QtCore/QBasicMutex>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+namespace Paths {
+
+Path moduleIndexPath(QString uri, int majorVersion, ErrorHandler errorHandler)
+{
+ QString version = QString::number(majorVersion);
+ if (majorVersion == Version::Latest)
+ version = QLatin1String("Latest");
+ else if (majorVersion == Version::Undefined)
+ version = QString();
+ if (uri.startsWith(u"file://") || uri.startsWith(u"http://") || uri.startsWith(u"https://")) {
+ if (majorVersion != Version::Undefined)
+ Path::myErrors()
+ .error(Path::tr("The module directory import %1 cannot have a version")
+ .arg(uri))
+ .handle(errorHandler);
+ version = QString();
+ } else {
+ QRegularExpression moduleRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
+ auto m = moduleRe.match(uri);
+ if (!m.isValid())
+ Path::myErrors()
+ .error(Path::tr("Invalid module name in import %1").arg(uri))
+ .handle(errorHandler);
+ }
+ return Path::Root(PathRoot::Env).field(Fields::moduleIndexWithUri).key(uri).key(version);
+}
+
+Path moduleScopePath(QString uri, Version version, ErrorHandler errorHandler)
+{
+ if (uri.startsWith(u"file://") || uri.startsWith(u"http://") || uri.startsWith(u"https://")) {
+ if (version.isValid())
+ Path::myErrors()
+ .error(Path::tr("The module directory import %1 cannot have a version")
+ .arg(uri))
+ .handle(errorHandler);
+ version = {};
+ } else {
+ QRegularExpression moduleRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
+ auto m = moduleRe.match(uri);
+ if (!m.isValid())
+ Path::myErrors()
+ .error(Path::tr("Invalid module name in import %1").arg(uri))
+ .handle(errorHandler);
+ }
+ return Path::Root(PathRoot::Env)
+ .field(Fields::moduleIndexWithUri)
+ .key(uri)
+ .key(version.majorSymbolicString())
+ .field(Fields::moduleScope)
+ .key(version.minorString());
+}
+
+Path moduleScopePath(QString uri, QString version, ErrorHandler errorHandler)
+{
+ Version v = Version::fromString(version);
+ if (!version.isEmpty() && !(v.isValid() || v.isLatest()))
+ Path::myErrors().error(Path::tr("Invalid Version %1").arg(version)).handle(errorHandler);
+ return moduleScopePath(uri, v, errorHandler);
+}
+
+} // end namespace Paths
+
+static ErrorGroups domParsingErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Parsing") } };
+ return res;
+}
+
+bool CommentableDomElement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
+ return cont;
+}
+
+void Component::updatePathFromOwner(Path newPath)
+{
+ DomElement::updatePathFromOwner(newPath);
+ updatePathFromOwnerMultiMap(m_enumerations, newPath.field(Fields::enumerations));
+ updatePathFromOwnerQList(m_objects, newPath.field(Fields::objects));
+}
+
+Component::Component(QString name) : CommentableDomElement(Path()), m_name(name) { }
+
+Component::Component(Path pathFromOwner) : CommentableDomElement(pathFromOwner) { }
+
+bool Component::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::name, name());
+ cont = cont && self.dvWrapField(visitor, Fields::enumerations, m_enumerations);
+ cont = cont && self.dvWrapField(visitor, Fields::objects, m_objects);
+ cont = cont && self.dvValueField(visitor, Fields::isSingleton, isSingleton());
+ cont = cont && self.dvValueField(visitor, Fields::isCreatable, isCreatable());
+ cont = cont && self.dvValueField(visitor, Fields::isComposite, isComposite());
+ cont = cont && self.dvValueField(visitor, Fields::attachedTypeName, attachedTypeName());
+ cont = cont && self.dvReferenceField(visitor, Fields::attachedType, attachedTypePath(self));
+ return cont;
+}
+
+DomItem Component::field(DomItem &self, QStringView name)
+{
+ switch (name.length()) {
+ case 4:
+ if (name == Fields::name)
+ return self.wrapField(Fields::name, m_name);
+ break;
+ case 7:
+ if (name == Fields::objects)
+ return self.wrapField(Fields::objects, m_objects);
+ break;
+ default:
+ break;
+ }
+ return DomBase::field(self, name);
+}
+
+Path Component::addObject(const QmlObject &object, QmlObject **oPtr)
+{
+ return appendUpdatableElementInQList(pathFromOwner().field(Fields::objects), m_objects, object,
+ oPtr);
+}
+
+bool QmlComponent::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = Component::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::ids, m_ids);
+ cont = cont && self.dvValueLazyField(visitor, Fields::subComponents, [this, &self]() {
+ return this->subComponents(self);
+ });
+ return cont;
+}
+
+void QmlComponent::updatePathFromOwner(Path newPath)
+{
+ Component::updatePathFromOwner(newPath);
+ updatePathFromOwnerMultiMap(m_ids, newPath.field(Fields::annotations));
+}
+
+QList<QString> QmlComponent::subComponentsNames(DomItem &self) const
+{
+ DomItem components = self.owner().field(Fields::components);
+ QSet<QString> cNames = components.keys();
+ QString myNameDot = self.pathFromOwner()[1].headName();
+ if (!myNameDot.isEmpty())
+ myNameDot += QLatin1Char('.');
+ QList<QString> subNames;
+ for (QString cName : cNames)
+ if (cName.startsWith(myNameDot)
+ && !QStringView(cName).mid(myNameDot.length()).contains(QLatin1Char('.'))
+ && !cName.isEmpty())
+ subNames.append(cName);
+ std::sort(subNames.begin(), subNames.end());
+ return subNames;
+}
+
+QList<DomItem> QmlComponent::subComponents(DomItem &self) const
+{
+ DomItem components = self.owner().field(Fields::components);
+ QList<DomItem> res;
+ for (QString cName : subComponentsNames(self))
+ for (DomItem comp : components.key(cName).values())
+ res.append(comp);
+ return res;
+}
+
+Version Version::fromString(QStringView v)
+{
+ if (v.isEmpty())
+ return Version(Latest, Latest);
+ QRegularExpression r(
+ QRegularExpression::anchoredPattern(QStringLiteral(uR"(([0-9]*)(?:\.([0-9]*))?)")));
+ auto m = r.match(v.toString());
+ if (m.hasMatch()) {
+ bool ok;
+ int majorV = m.captured(1).toInt(&ok);
+ if (!ok)
+ majorV = Version::Undefined;
+ int minorV = m.captured(2).toInt(&ok);
+ if (!ok)
+ minorV = Version::Undefined;
+ return Version(majorV, minorV);
+ }
+ return {};
+}
+
+Version::Version(qint32 majorV, qint32 minorV) : majorVersion(majorV), minorVersion(minorV) { }
+
+bool Version::isLatest() const
+{
+ return majorVersion == Latest && minorVersion == Latest;
+}
+
+bool Version::isValid() const
+{
+ return majorVersion >= 0 && minorVersion >= 0;
+}
+
+QString Version::stringValue() const
+{
+ if (isLatest())
+ return QString();
+ if (minorVersion < 0) {
+ if (majorVersion < 0)
+ return QLatin1String(".");
+ else
+ return QString::number(majorVersion);
+ }
+ if (majorVersion < 0)
+ return QLatin1String(".") + QString::number(minorVersion);
+ return QString::number(majorVersion) + QChar::fromLatin1('.') + QString::number(minorVersion);
+}
+
+bool Version::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvWrapField(visitor, Fields::majorVersion, majorVersion);
+ cont = cont && self.dvWrapField(visitor, Fields::minorVersion, minorVersion);
+ cont = cont && self.dvValueField(visitor, Fields::isLatest, isLatest());
+ cont = cont && self.dvValueField(visitor, Fields::isValid, isValid());
+ cont = cont && self.dvValueLazyField(visitor, Fields::stringValue, [this]() {
+ return this->stringValue();
+ });
+ return cont;
+}
+
+QRegularExpression Import::importRe()
+{
+ static QRegularExpression res(QRegularExpression::anchoredPattern(QStringLiteral(
+ uR"((?<uri>\w+(?:\.\w+)*)(?:\W+(?<version>[0-9]+(?:\.[0-9]*)?))?(?:\W+as\W+(?<id>\w+))?$)")));
+ return res;
+}
+
+Import Import::fromUriString(QString importStr, Version v, QString importId, ErrorHandler handler)
+{
+ if (importStr.startsWith(u"http://") || importStr.startsWith(u"https://")
+ || importStr.startsWith(u"file://")) {
+ return Import(importStr, v, importId);
+ } else {
+ auto m = importRe().match(importStr);
+ if (m.hasMatch()) {
+ if (v.majorVersion == Version::Undefined && v.minorVersion == Version::Undefined)
+ v = Version::fromString(m.captured(2));
+ else if (!m.captured(u"version").isEmpty())
+ domParsingErrors()
+ .warning(tr("Version %1 in import string '%2' overridden by explicit "
+ "version %3")
+ .arg(m.captured(2), importStr, v.stringValue()))
+ .handle(handler);
+ if (importId.isEmpty())
+ importId = m.captured(u"importId");
+ else if (!m.captured(u"importId").isEmpty())
+ domParsingErrors()
+ .warning(tr("namespace %1 in import string '%2' overridden by explicit "
+ "importId %3")
+ .arg(m.captured(u"importId"), importStr, importId))
+ .handle(handler);
+ return Import(m.captured(u"uri").trimmed(), v, importId);
+ }
+ domParsingErrors()
+ .error(tr("Unexpected uri format in import '%1'").arg(importStr))
+ .handle(handler);
+ return Import();
+ }
+}
+
+Import Import::fromFileString(QString importStr, QString baseDir, QString importId,
+ ErrorHandler handler)
+{
+ Version v;
+ if (importStr.startsWith(u"http://") || importStr.startsWith(u"https://")
+ || importStr.startsWith(u"file://"))
+ return Import(importStr, v, importId);
+ QFileInfo p(importStr);
+ if (p.isRelative())
+ p = QFileInfo(QDir(baseDir).filePath(importStr));
+ QString path = p.canonicalFilePath();
+ if (path.isEmpty()) {
+ domParsingErrors()
+ .warning(tr("Non existing directory or file referred in uri of import '%1'")
+ .arg(importStr))
+ .handle(handler);
+ path = p.filePath();
+ }
+ return Import(QLatin1String("file://") + path, v, importId);
+}
+
+bool Import::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::uri, uri);
+ cont = cont && self.dvWrapField(visitor, Fields::version, version);
+ if (!importId.isEmpty())
+ cont = cont && self.dvValueField(visitor, Fields::importId, importId);
+ if (implicit)
+ cont = cont && self.dvValueField(visitor, Fields::implicit, implicit);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
+ return cont;
+}
+
+Id::Id(QString idName, Path referredObject) : name(idName), referredObjectPath(referredObject) { }
+
+bool Id::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::name, name);
+ cont = cont && self.dvReferenceField(visitor, Fields::referredObject, referredObjectPath);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
+ cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
+ return cont;
+}
+
+void Id::updatePathFromOwner(Path newPath)
+{
+ updatePathFromOwnerQList(annotations, newPath.field(Fields::annotations));
+}
+
+Path Id::addAnnotation(Path selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
+{
+ return appendUpdatableElementInQList(selfPathFromOwner.field(Fields::annotations), annotations,
+ annotation, aPtr);
+}
+
+QmlObject::QmlObject(Path pathFromOwner) : CommentableDomElement(pathFromOwner) { }
+
+bool QmlObject::iterateBaseDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
+ if (!idStr().isEmpty())
+ cont = cont && self.dvValueField(visitor, Fields::idStr, idStr());
+ cont = cont && self.dvValueField(visitor, Fields::name, name());
+ if (!prototypePaths().isEmpty())
+ cont = cont && self.dvReferencesField(visitor, Fields::prototypes, m_prototypePaths);
+ if (nextScopePath())
+ cont = cont && self.dvReferenceField(visitor, Fields::nextScope, nextScopePath());
+ cont = cont && self.dvWrapField(visitor, Fields::propertyDefs, m_propertyDefs);
+ cont = cont && self.dvWrapField(visitor, Fields::bindings, m_bindings);
+ cont = cont && self.dvWrapField(visitor, Fields::methods, m_methods);
+ cont = cont && self.dvWrapField(visitor, Fields::children, m_children);
+ cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
+ cont = cont && self.dvItemField(visitor, Fields::propertyInfos, [this, &self]() {
+ return self.subMapItem(Map(
+ pathFromOwner().field(Fields::propertyInfos),
+ [&self](DomItem &map, QString k) {
+ auto pInfo = self.propertyInfoWithName(k);
+ return map.wrap(PathEls::Key(k), pInfo);
+ },
+ [&self](DomItem &) { return self.propertyInfoNames(); },
+ QLatin1String("PropertyInfo")));
+ });
+ return cont;
+}
+
+QList<QString> QmlObject::fields() const
+{
+ static QList<QString> myFields(
+ { QString::fromUtf16(Fields::comments), QString::fromUtf16(Fields::idStr),
+ QString::fromUtf16(Fields::name), QString::fromUtf16(Fields::prototypes),
+ QString::fromUtf16(Fields::nextScope), QString::fromUtf16(Fields::propertyDefs),
+ QString::fromUtf16(Fields::bindings), QString::fromUtf16(Fields::methods),
+ QString::fromUtf16(Fields::children), QString::fromUtf16(Fields::annotations),
+ QString::fromUtf16(Fields::propertyInfos) });
+ return myFields;
+}
+
+bool QmlObject::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = iterateBaseDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueLazyField(visitor, Fields::defaultPropertyName, [this, &self]() {
+ return defaultPropertyName(self);
+ });
+ return cont;
+}
+
+DomItem QmlObject::field(DomItem &self, QStringView name)
+{
+ switch (name.size()) {
+ case 4:
+ if (name == Fields::name)
+ return self.subDataItem(PathEls::Field(Fields::name), this->name());
+ break;
+ case 5:
+ if (name == Fields::idStr) {
+ if (idStr().isEmpty())
+ return DomItem();
+ return self.subDataItem(PathEls::Field(Fields::idStr), idStr());
+ }
+ break;
+ case 7:
+ if (name == Fields::methods)
+ return self.wrapField(Fields::methods, m_methods);
+ break;
+ case 8:
+ switch (name.at(1).unicode()) {
+ case u'i':
+ if (name == Fields::bindings)
+ return self.wrapField(Fields::bindings, m_bindings);
+ break;
+ case u'o':
+ if (name == Fields::comments)
+ return CommentableDomElement::field(self, name);
+ break;
+ case u'h':
+ if (name == Fields::children)
+ return self.wrapField(Fields::children, m_children);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 9:
+ if (name == Fields::nextScope) {
+ if (nextScopePath())
+ return self.subReferenceItem(PathEls::Field(Fields::nextScope), nextScopePath());
+ else
+ return DomItem();
+ }
+ break;
+ case 10:
+ if (name == Fields::prototypes) {
+ if (prototypePaths().isEmpty())
+ return DomItem();
+ return self.subReferencesItem(PathEls::Field(Fields::prototypes), m_prototypePaths);
+ }
+ break;
+ case 11:
+ if (name == Fields::annotations)
+ return self.wrapField(Fields::annotations, m_annotations);
+ break;
+ case 12:
+ return self.wrapField(Fields::propertyDefs, m_propertyDefs);
+ break;
+ case 13:
+ if (name == Fields::propertyInfos)
+ return self.subMapItem(Map(
+ pathFromOwner().field(Fields::propertyInfos),
+ [self](DomItem &map, QString k) mutable {
+ auto pInfo = self.propertyInfoWithName(k);
+ return map.wrap(PathEls::Key(k), pInfo);
+ },
+ [self](DomItem &) mutable { return self.propertyInfoNames(); },
+ QLatin1String("PropertyInfo")));
+ break;
+ case 19:
+ if (name == Fields::defaultPropertyName)
+ return self.subDataItem(PathEls::Field(Fields::defaultPropertyName),
+ defaultPropertyName(self));
+ break;
+ default:
+ break;
+ }
+ static QStringList knownLookups({ QString::fromUtf16(Fields::fileLocationsTree) });
+ if (!knownLookups.contains(name))
+ qCWarning(domLog()) << "Asked non existing field " << name << " in QmlObject "
+ << pathFromOwner();
+ return DomItem();
+}
+
+void QmlObject::updatePathFromOwner(Path newPath)
+{
+ DomElement::updatePathFromOwner(newPath);
+ updatePathFromOwnerMultiMap(m_propertyDefs, newPath.field(Fields::propertyDefs));
+ updatePathFromOwnerMultiMap(m_bindings, newPath.field(Fields::bindings));
+ updatePathFromOwnerMultiMap(m_methods, newPath.field(Fields::methods));
+ updatePathFromOwnerQList(m_children, newPath.field(Fields::children));
+ updatePathFromOwnerQList(m_annotations, newPath.field(Fields::annotations));
+}
+
+QString QmlObject::localDefaultPropertyName() const
+{
+ if (!m_defaultPropertyName.isEmpty())
+ return m_defaultPropertyName;
+ for (const PropertyDefinition &pDef : m_propertyDefs)
+ if (pDef.isDefaultMember)
+ return pDef.name;
+ return QString();
+}
+
+QString QmlObject::defaultPropertyName(DomItem &self) const
+{
+ QString dProp = localDefaultPropertyName();
+ if (!dProp.isEmpty())
+ return dProp;
+ QString res = QStringLiteral(u"data");
+ self.visitPrototypeChain(
+ [&res](DomItem &obj) {
+ if (const QmlObject *objPtr = obj.as<QmlObject>()) {
+ QString dProp = objPtr->localDefaultPropertyName();
+ if (!dProp.isEmpty()) {
+ res = dProp;
+ return false;
+ }
+ }
+ return true;
+ },
+ VisitPrototypesOption::SkipFirst);
+ return res;
+}
+
+bool QmlObject::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &)> visitor) const
+{
+ bool cont = self.field(Fields::bindings).visitKeys([visitor](QString, DomItem &bs) {
+ return bs.visitIndexes([visitor](DomItem &b) {
+ DomItem v = b.field(Fields::value);
+ if (std::shared_ptr<ScriptExpression> vPtr = v.ownerAs<ScriptExpression>()) {
+ if (!visitor(v))
+ return false;
+ return v.iterateSubOwners(visitor); // currently not needed, avoid?
+ }
+ return true;
+ });
+ });
+ cont = cont && self.field(Fields::children).visitIndexes([visitor](DomItem &qmlObj) {
+ if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>()) {
+ return qmlObjPtr->iterateSubOwners(qmlObj, visitor);
+ }
+ Q_ASSERT(false);
+ return true;
+ });
+ return cont;
+}
+
+MutableDomItem QmlObject::addPropertyDef(MutableDomItem &self, PropertyDefinition propertyDef,
+ AddOption option)
+{
+ Path p = addPropertyDef(propertyDef, option);
+ if (p.last().headIndex(0) > 1)
+ self.owningItemPtr()->addErrorLocal(domParsingErrors().error(
+ tr("Repeated PropertyDefinition with name %1").arg(propertyDef.name)));
+ return self.owner().path(p);
+}
+
+MutableDomItem QmlObject::addBinding(MutableDomItem &self, Binding binding, AddOption option)
+{
+ Path p = addBinding(binding, option);
+ if (p && p.last().headIndex(0) > 1)
+ self.owningItemPtr()->addErrorLocal(
+ domParsingErrors().error(tr("Repeated binding with name %1").arg(binding.name())));
+ return self.owner().path(p);
+}
+
+MutableDomItem QmlObject::addMethod(MutableDomItem &self, MethodInfo functionDef, AddOption option)
+{
+ Path p = addMethod(functionDef, option);
+ if (p.last().headIndex(0) > 1)
+ self.owningItemPtr()->addErrorLocal(
+ domParsingErrors().error(tr("Repeated Method with name %1").arg(functionDef.name)));
+ return self.owner().path(p);
+}
+
+Binding::Binding(QString name, std::unique_ptr<BindingValue> value, BindingType bindingType)
+ : m_bindingType(bindingType), m_name(name), m_value(std::move(value))
+{
+}
+
+Binding::Binding(QString name, std::shared_ptr<ScriptExpression> value, BindingType bindingType)
+ : Binding(name, std::make_unique<BindingValue>(value), bindingType)
+{
+}
+
+Binding::Binding(QString name, QString scriptCode, BindingType bindingType)
+ : Binding(name,
+ std::make_unique<BindingValue>(std::shared_ptr<ScriptExpression>(new ScriptExpression(
+ scriptCode, ScriptExpression::ExpressionType::BindingExpression))),
+ bindingType)
+{
+}
+
+Binding::Binding(QString name, QmlObject value, BindingType bindingType)
+ : Binding(name, std::make_unique<BindingValue>(value), bindingType)
+{
+}
+
+Binding::Binding(QString name, QList<QmlObject> value, BindingType bindingType)
+ : Binding(name, std::make_unique<BindingValue>(value), bindingType)
+{
+}
+
+Binding::Binding(const Binding &o)
+ : m_bindingType(o.m_bindingType),
+ m_name(o.m_name),
+ m_annotations(o.m_annotations),
+ m_comments(o.m_comments)
+{
+ if (o.m_value) {
+ m_value = std::make_unique<BindingValue>(*o.m_value);
+ }
+}
+
+Binding::~Binding() { }
+
+Binding &Binding::operator=(const Binding &o)
+{
+ m_name = o.m_name;
+ m_bindingType = o.m_bindingType;
+ m_annotations = o.m_annotations;
+ m_comments = o.m_comments;
+ if (o.m_value) {
+ if (!m_value)
+ m_value = std::make_unique<BindingValue>(*o.m_value);
+ else
+ *m_value = *o.m_value;
+ } else {
+ m_value.reset();
+ }
+ return *this;
+}
+
+bool Binding::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::name, m_name);
+ cont = cont && self.dvValueField(visitor, Fields::isSignalHandler, isSignalHandler());
+ if (!m_value)
+ cont = cont && visitor(PathEls::Field(Fields::value), []() { return DomItem(); });
+ else
+ cont = cont && self.dvItemField(visitor, Fields::value, [this, &self]() {
+ return m_value->value(self);
+ });
+ cont = cont && self.dvValueField(visitor, Fields::bindingType, int(m_bindingType));
+ cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
+ cont = cont && self.dvValueLazyField(visitor, Fields::preCode, [this]() {
+ return this->preCode();
+ });
+ cont = cont && self.dvValueLazyField(visitor, Fields::postCode, [this]() {
+ return this->postCode();
+ });
+ cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
+ return cont;
+}
+
+DomItem Binding::valueItem(DomItem &self) const
+{
+ if (!m_value)
+ return DomItem();
+ return m_value->value(self);
+}
+
+BindingValueKind Binding::valueKind() const
+{
+ if (!m_value)
+ return BindingValueKind::Empty;
+ return m_value->kind;
+}
+
+QmlObject const *Binding::objectValue() const
+{
+ if (valueKind() == BindingValueKind::Object)
+ return &(m_value->object);
+ return nullptr;
+}
+
+QmlObject *Binding::objectValue()
+{
+ if (valueKind() == BindingValueKind::Object)
+ return &(m_value->object);
+ return nullptr;
+}
+
+QList<QmlObject> const *Binding::arrayValue() const
+{
+ if (valueKind() == BindingValueKind::Array)
+ return &(m_value->array);
+ return nullptr;
+}
+
+QList<QmlObject> *Binding::arrayValue()
+{
+ if (valueKind() == BindingValueKind::Array)
+ return &(m_value->array);
+ return nullptr;
+}
+
+std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue() const
+{
+ if (valueKind() == BindingValueKind::ScriptExpression)
+ return m_value->scriptExpression;
+ return nullptr;
+}
+
+std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue()
+{
+ if (valueKind() == BindingValueKind::ScriptExpression)
+ return m_value->scriptExpression;
+ return nullptr;
+}
+
+Path Binding::addAnnotation(Path selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
+{
+ return appendUpdatableElementInQList(selfPathFromOwner.field(Fields::annotations),
+ m_annotations, annotation, aPtr);
+}
+
+void Binding::updatePathFromOwner(Path newPath)
+{
+ Path base = newPath.field(Fields::annotations);
+ if (m_value)
+ m_value->updatePathFromOwner(newPath.field(Fields::value));
+ updatePathFromOwnerQList(m_annotations, newPath.field(Fields::annotations));
+}
+
+bool QmltypesComponent::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = Component::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
+ cont = cont && self.dvValueField(visitor, Fields::metaRevisions, m_metaRevisions);
+ if (!fileName().isEmpty())
+ cont = cont && self.dvValueField(visitor, Fields::fileName, fileName()); // remove?
+ return cont;
+}
+
+Export Export::fromString(Path source, QStringView exp, Path typePath, ErrorHandler h)
+{
+ Export res;
+ res.exportSourcePath = source;
+ res.typePath = typePath;
+ int slashIdx = exp.indexOf(QLatin1Char('/'));
+ int spaceIdx = exp.indexOf(QLatin1Char(' '));
+ if (spaceIdx == -1)
+ spaceIdx = exp.length();
+ else
+ res.version = Version::fromString(exp.mid(spaceIdx + 1));
+ if (!res.version.isValid())
+ domParsingErrors()
+ .error(tr("Expected string literal to contain 'Package/Name major.minor' "
+ "or 'Name major.minor' not '%1'.")
+ .arg(exp))
+ .handle(h);
+ QString package;
+ if (slashIdx != -1)
+ res.uri = exp.left(slashIdx).toString();
+ res.typeName = exp.mid(slashIdx + 1, spaceIdx - (slashIdx + 1)).toString();
+ return res;
+}
+
+bool AttributeInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::name, name);
+ cont = cont && self.dvValueField(visitor, Fields::access, int(access));
+ cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
+ cont = cont && self.dvValueField(visitor, Fields::isReadonly, isReadonly);
+ cont = cont && self.dvValueField(visitor, Fields::isList, isList);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
+ cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
+ return cont;
+}
+
+Path AttributeInfo::addAnnotation(Path selfPathFromOwner, const QmlObject &annotation,
+ QmlObject **aPtr)
+{
+ return appendUpdatableElementInQList(selfPathFromOwner.field(Fields::annotations), annotations,
+ annotation, aPtr);
+}
+
+void AttributeInfo::updatePathFromOwner(Path newPath)
+{
+ Path base = newPath.field(Fields::annotations);
+ updatePathFromOwnerQList(annotations, newPath.field(Fields::annotations));
+}
+
+bool EnumDecl::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::name, name());
+ cont = cont && self.dvWrapField(visitor, Fields::values, m_values);
+ cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
+ return cont;
+}
+
+void EnumDecl::updatePathFromOwner(Path newPath)
+{
+ DomElement::updatePathFromOwner(newPath);
+ updatePathFromOwnerQList(m_annotations, newPath.field(Fields::annotations));
+}
+
+QList<QmlObject> EnumDecl::annotations() const
+{
+ return m_annotations;
+}
+
+void EnumDecl::setAnnotations(QList<QmlObject> annotations)
+{
+ m_annotations = annotations;
+}
+
+Path EnumDecl::addAnnotation(const QmlObject &annotation, QmlObject **aPtr)
+{
+ return appendUpdatableElementInQList(pathFromOwner().field(Fields::annotations), m_annotations,
+ annotation, aPtr);
+}
+
+QList<Path> ImportScope::allSources(DomItem &self) const
+{
+ DomItem env = self.environment();
+ Path selfPath = self.canonicalPath().field(Fields::allSources);
+ RefCacheEntry cached = RefCacheEntry::forPath(env, selfPath);
+ if (cached.cached == RefCacheEntry::Cached::All)
+ return cached.canonicalPaths;
+ QList<Path> res;
+ QSet<Path> knownPaths;
+ QList<Path> toDo(m_importSourcePaths.rbegin(), m_importSourcePaths.rend());
+ while (!toDo.isEmpty()) {
+ Path pNow = toDo.takeLast();
+ if (knownPaths.contains(pNow))
+ continue;
+ knownPaths.insert(pNow);
+ res.append(pNow);
+ DomItem sourceBase = env.path(pNow);
+ for (DomItem autoExp : sourceBase.field(Fields::autoExports).values()) {
+ if (const ModuleAutoExport *autoExpPtr = autoExp.as<ModuleAutoExport>()) {
+ Path newSource;
+ if (autoExpPtr->inheritVersion) {
+ Version v = autoExpPtr->import.version;
+ DomItem sourceVersion = sourceBase.field(Fields::version);
+ if (const Version *sourceVersionPtr = sourceVersion.as<Version>()) {
+ if (v.majorVersion < 0)
+ v.majorVersion = sourceVersionPtr->majorVersion;
+ if (v.minorVersion < 0)
+ v.minorVersion = sourceVersionPtr->minorVersion;
+ } else {
+ qWarning() << "autoExport with inherited version " << autoExp
+ << " but missing version in source" << pNow;
+ }
+ Import toImport(autoExpPtr->import.uri, v);
+ newSource = toImport.importedPath();
+ } else {
+ newSource = autoExpPtr->import.importedPath();
+ }
+ if (newSource && !knownPaths.contains(newSource))
+ toDo.append(newSource);
+ } else {
+ qWarning() << "expected ModuleAutoExport not " << autoExp.internalKindStr()
+ << "looking up autoExports of" << sourceBase;
+ Q_ASSERT(false);
+ }
+ }
+ }
+ RefCacheEntry::addForPath(env, selfPath, RefCacheEntry { RefCacheEntry::Cached::All, res });
+ return res;
+}
+
+bool ImportScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvReferencesField(visitor, Fields::importSources, m_importSourcePaths);
+ cont = cont && self.dvItemField(visitor, Fields::allSources, [this, &self]() -> DomItem {
+ return self.subListItem(List::fromQList<Path>(
+ self.pathFromOwner().field(Fields::allSources), allSources(self),
+ [](DomItem &list, const PathEls::PathComponent &p, const Path &el) {
+ return list.subDataItem(p, el.toString());
+ }));
+ });
+ cont = cont && self.dvWrapField(visitor, Fields::qualifiedImports, m_subImports);
+ cont = cont && self.dvItemField(visitor, Fields::imported, [this, &self]() -> DomItem {
+ return self.subMapItem(Map(
+ self.pathFromOwner().field(Fields::imported),
+ [this, &self](DomItem &map, QString key) {
+ return map.subListItem(List::fromQList<DomItem>(
+ map.pathFromOwner().key(key), importedItemsWithName(self, key),
+ [](DomItem &, const PathEls::PathComponent &, DomItem &el) {
+ return el;
+ }));
+ },
+ [this, &self](DomItem &) { return this->importedNames(self); },
+ QLatin1String("List<Export>")));
+ });
+ return cont;
+}
+
+bool PropertyInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::propertyDefs, propertyDefs);
+ cont = cont && self.dvValueField(visitor, Fields::bindings, bindings);
+ return cont;
+}
+
+BindingValue::BindingValue() : kind(BindingValueKind::Empty) { }
+
+BindingValue::BindingValue(const QmlObject &o) : kind(BindingValueKind::Object)
+{
+ new (&object) QmlObject(o);
+}
+
+BindingValue::BindingValue(std::shared_ptr<ScriptExpression> o)
+ : kind(BindingValueKind::ScriptExpression)
+{
+ new (&scriptExpression) std::shared_ptr<ScriptExpression>(o);
+}
+
+BindingValue::BindingValue(const QList<QmlObject> &l) : kind(BindingValueKind::Array)
+{
+ new (&array) QList<QmlObject>(l);
+}
+
+BindingValue::~BindingValue()
+{
+ clearValue();
+}
+
+BindingValue::BindingValue(const BindingValue &o) : kind(o.kind)
+{
+ switch (o.kind) {
+ case BindingValueKind::Empty:
+ break;
+ case BindingValueKind::Object:
+ new (&object) QmlObject(o.object);
+ break;
+ case BindingValueKind::ScriptExpression:
+ new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
+ break;
+ case BindingValueKind::Array:
+ new (&array) QList<QmlObject>(o.array);
+ }
+}
+
+BindingValue &BindingValue::operator=(const BindingValue &o)
+{
+ clearValue();
+ kind = o.kind;
+ switch (o.kind) {
+ case BindingValueKind::Empty:
+ break;
+ case BindingValueKind::Object:
+ new (&object) QmlObject(o.object);
+ break;
+ case BindingValueKind::ScriptExpression:
+ new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
+ break;
+ case BindingValueKind::Array:
+ new (&array) QList<QmlObject>(o.array);
+ }
+ return *this;
+}
+
+DomItem BindingValue::value(DomItem &binding)
+{
+ switch (kind) {
+ case BindingValueKind::Empty:
+ break;
+ case BindingValueKind::Object:
+ return binding.copy(&object);
+ case BindingValueKind::ScriptExpression:
+ return binding.subOwnerItem(PathEls::Field(Fields::value), scriptExpression);
+ case BindingValueKind::Array:
+ return binding.subListItem(List::fromQListRef<QmlObject>(
+ binding.pathFromOwner().field(u"value"), array,
+ [binding](DomItem &self, const PathEls::PathComponent &, QmlObject &obj) {
+ return self.copy(&obj);
+ }));
+ }
+ return DomItem();
+}
+
+void BindingValue::updatePathFromOwner(Path newPath)
+{
+ switch (kind) {
+ case BindingValueKind::Empty:
+ break;
+ case BindingValueKind::Object:
+ object.updatePathFromOwner(newPath);
+ break;
+ case BindingValueKind::ScriptExpression:
+ break;
+ case BindingValueKind::Array:
+ updatePathFromOwnerQList(array, newPath);
+ break;
+ }
+}
+
+void BindingValue::clearValue()
+{
+ switch (kind) {
+ case BindingValueKind::Empty:
+ break;
+ case BindingValueKind::Object:
+ object.~QmlObject();
+ break;
+ case BindingValueKind::ScriptExpression:
+ scriptExpression.~shared_ptr();
+ break;
+ case BindingValueKind::Array:
+ array.~QList<QmlObject>();
+ break;
+ }
+ kind = BindingValueKind::Empty;
+}
+
+ScriptExpression::ScriptExpression(const ScriptExpression &e) : OwningItem(e)
+{
+ QMutexLocker l(mutex());
+ m_expressionType = e.m_expressionType;
+ m_engine = e.m_engine;
+ m_ast = e.m_ast;
+ if (m_codeStr.isEmpty()) {
+ m_code = e.m_code;
+ } else {
+ m_codeStr = e.m_codeStr;
+ m_code = m_codeStr;
+ }
+ m_localOffset = e.m_localOffset;
+ m_astComments = e.m_astComments;
+}
+
+std::shared_ptr<ScriptExpression> ScriptExpression::copyWithUpdatedCode(DomItem &self,
+ QString code) const
+{
+ std::shared_ptr<ScriptExpression> copy = makeCopy(self);
+ DomItem container = self.containingObject();
+ QString preCodeStr = container.field(Fields::preCode).value().toString(m_preCode.toString());
+ QString postCodeStr = container.field(Fields::postCode).value().toString(m_postCode.toString());
+ copy->setCode(code, preCodeStr, postCodeStr);
+ return copy;
+}
+
+bool ScriptExpression::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::code, code());
+ if (!preCode().isEmpty())
+ cont = cont
+ && self.dvValueField(visitor, Fields::preCode, preCode(),
+ ConstantData::Options::MapIsMap);
+ if (!postCode().isEmpty())
+ cont = cont
+ && self.dvValueField(visitor, Fields::postCode, postCode(),
+ ConstantData::Options::MapIsMap);
+ cont = cont
+ && self.dvValueLazyField(
+ visitor, Fields::localOffset,
+ [this]() { return locationToData(localOffset()); },
+ ConstantData::Options::MapIsMap);
+ cont = cont && self.dvValueLazyField(visitor, Fields::astRelocatableDump, [this]() {
+ return astRelocatableDump();
+ });
+ cont = cont && self.dvValueField(visitor, Fields::expressionType, int(expressionType()));
+ return cont;
+}
+
+class FirstNodeVisitor : public VisitAll
+{
+public:
+ quint32 minStart = 0;
+ quint32 maxEnd = ~quint32(0);
+ AST::Node *firstNodeInRange = nullptr;
+
+ FirstNodeVisitor(quint32 minStart = 0, quint32 maxEnd = ~quint32(0))
+ : minStart(minStart), maxEnd(maxEnd)
+ {
+ }
+
+ bool preVisit(AST::Node *n) override
+ {
+ if (!VisitAll::uiKinds().contains(n->kind)) {
+ quint32 start = n->firstSourceLocation().begin();
+ quint32 end = n->lastSourceLocation().end();
+ if (!firstNodeInRange && minStart <= start && end <= maxEnd && start < end)
+ firstNodeInRange = n;
+ }
+ return !firstNodeInRange;
+ }
+};
+
+AST::Node *firstNodeInRange(AST::Node *n, quint32 minStart = 0, quint32 maxEnd = ~quint32(0))
+{
+ FirstNodeVisitor visitor(minStart, maxEnd);
+ AST::Node::accept(n, &visitor);
+ return visitor.firstNodeInRange;
+}
+
+void ScriptExpression::setCode(QString code, QString preCode, QString postCode)
+{
+ m_codeStr = code;
+ if (!preCode.isEmpty() || !postCode.isEmpty())
+ m_codeStr = preCode + code + postCode;
+ m_code = QStringView(m_codeStr).mid(preCode.length(), code.length());
+ m_preCode = QStringView(m_codeStr).mid(0, preCode.length());
+ m_postCode = QStringView(m_codeStr).mid(preCode.length() + code.length(), postCode.length());
+ m_engine = nullptr;
+ m_ast = nullptr;
+ m_localOffset = SourceLocation();
+ if (!m_code.isEmpty()) {
+ IndentInfo preChange(m_preCode, 4);
+ m_localOffset.offset = m_preCode.length();
+ m_localOffset.length = m_code.length();
+ m_localOffset.startColumn = preChange.trailingString.length();
+ m_localOffset.startLine = preChange.nNewlines;
+ m_engine = std::shared_ptr<QQmlJS::Engine>(new QQmlJS::Engine);
+ m_astComments = std::shared_ptr<AstComments>(new AstComments(m_engine));
+ QQmlJS::Lexer lexer(m_engine.get());
+ lexer.setCode(m_codeStr, /*lineno = */ 1, /*qmlMode=*/true);
+ QQmlJS::Parser parser(m_engine.get());
+ if (!parser.parseScript())
+ addErrorLocal(domParsingErrors().error(tr("Parsing of code failed")));
+ for (DiagnosticMessage msg : parser.diagnosticMessages()) {
+ ErrorMessage err = domParsingErrors().errorMessage(msg);
+ err.location.offset -= m_localOffset.offset;
+ err.location.startLine -= m_localOffset.startLine;
+ if (err.location.startLine == 1)
+ err.location.startColumn -= m_localOffset.startColumn;
+ addErrorLocal(err);
+ }
+ m_ast = parser.rootNode();
+ if (AST::Program *programPtr = AST::cast<AST::Program *>(m_ast)) {
+ m_ast = programPtr->statements;
+ }
+ if (!m_preCode.isEmpty())
+ m_ast = firstNodeInRange(m_ast, m_preCode.length(),
+ m_preCode.length() + m_code.length());
+ if (m_expressionType != ExpressionType::FunctionBody) {
+ if (AST::StatementList *sList = AST::cast<AST::StatementList *>(m_ast)) {
+ if (!sList->next)
+ m_ast = sList->statement;
+ }
+ }
+ AstComments::collectComments(m_engine, m_ast, m_astComments, MutableDomItem(), nullptr);
+ }
+}
+
+void ScriptExpression::astDumper(Sink s, AstDumperOptions options) const
+{
+ astNodeDumper(s, ast(), options, 1, 0, [this](SourceLocation astL) {
+ SourceLocation l = this->locationToLocal(astL);
+ return this->code().mid(l.offset, l.length);
+ });
+}
+
+QString ScriptExpression::astRelocatableDump() const
+{
+ return dumperToString([this](Sink s) {
+ this->astDumper(s, AstDumperOption::NoLocations | AstDumperOption::SloppyCompare);
+ });
+}
+
+SourceLocation ScriptExpression::globalLocation(DomItem &self) const
+{
+ if (const FileLocations *fLocPtr = FileLocations::fileLocationsPtr(self)) {
+ return fLocPtr->regions.value(QString(), fLocPtr->fullRegion);
+ }
+ return SourceLocation();
+}
+
+bool MethodInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::parameters, parameters);
+ cont = cont && self.dvValueField(visitor, Fields::methodType, int(methodType));
+ if (!typeName.isEmpty())
+ cont = cont && self.dvReferenceField(visitor, Fields::type, typePath(self));
+ if (methodType == MethodType::Method) {
+ cont = cont && self.dvValueField(visitor, Fields::preCode, preCode(self));
+ cont = cont && self.dvValueField(visitor, Fields::postCode, postCode(self));
+ }
+ if (body)
+ cont = cont && self.dvWrapField(visitor, Fields::body, body);
+ return cont;
+}
+
+QString MethodInfo::preCode(DomItem &) const
+{
+ return QLatin1String("function(){");
+}
+
+QString MethodInfo::postCode(DomItem &) const
+{
+ return QLatin1String("\n}\n");
+}
+
+bool MethodParameter::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::name, name);
+ if (!typeName.isEmpty()) {
+ cont = cont
+ && self.dvReferenceField(visitor, Fields::type, Paths::lookupCppTypePath(typeName));
+ cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
+ }
+ cont = cont && self.dvValueField(visitor, Fields::isPointer, isPointer);
+ cont = cont && self.dvValueField(visitor, Fields::isReadonly, isReadonly);
+ cont = cont && self.dvValueField(visitor, Fields::isList, isList);
+ cont = cont && self.dvWrapField(visitor, Fields::defaultValue, defaultValue);
+ if (!annotations.isEmpty())
+ cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
+ return cont;
+}
+
+bool EnumItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::name, name());
+ cont = cont && self.dvValueField(visitor, Fields::value, value());
+ cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
+ return cont;
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomelements_p.h b/src/qmldom/qqmldomelements_p.h
new file mode 100644
index 0000000000..66b568d871
--- /dev/null
+++ b/src/qmldom/qqmldomelements_p.h
@@ -0,0 +1,1172 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#ifndef QQMLDOMELEMENTS_P_H
+#define QQMLDOMELEMENTS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldomitem_p.h"
+#include "qqmldomconstants_p.h"
+#include "qqmldomcomments_p.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+
+#include <QtCore/QCborValue>
+#include <QtCore/QCborMap>
+#include <QtCore/QMutexLocker>
+#include <QtCore/QPair>
+
+#include <functional>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+// namespace for utility methods building specific paths
+// using a namespace one can reopen it and add more methods in other places
+namespace Paths {
+Path moduleIndexPath(QString uri, int majorVersion, ErrorHandler errorHandler = nullptr);
+Path moduleScopePath(QString uri, Version version, ErrorHandler errorHandler = nullptr);
+Path moduleScopePath(QString uri, QString version, ErrorHandler errorHandler = nullptr);
+inline Path moduleScopePath(QString uri, ErrorHandler errorHandler = nullptr)
+{
+ return moduleScopePath(uri, QString(), errorHandler);
+}
+inline Path qmlDirInfoPath(QString path)
+{
+ return Path::Root(PathRoot::Top).field(Fields::qmldirWithPath).key(path);
+}
+inline Path qmlDirPath(QString path)
+{
+ return qmlDirInfoPath(path).field(Fields::currentItem);
+}
+inline Path qmldirFileInfoPath(QString path)
+{
+ return Path::Root(PathRoot::Top).field(Fields::qmldirFileWithPath).key(path);
+}
+inline Path qmldirFilePath(QString path)
+{
+ return qmldirFileInfoPath(path).field(Fields::currentItem);
+}
+inline Path qmlFileInfoPath(QString canonicalFilePath)
+{
+ return Path::Root(PathRoot::Top).field(Fields::qmlFileWithPath).key(canonicalFilePath);
+}
+inline Path qmlFilePath(QString canonicalFilePath)
+{
+ return qmlFileInfoPath(canonicalFilePath).field(Fields::currentItem);
+}
+inline Path qmlFileObjectPath(QString canonicalFilePath)
+{
+ return qmlFilePath(canonicalFilePath)
+ .field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+}
+inline Path qmltypesFileInfoPath(QString path)
+{
+ return Path::Root(PathRoot::Top).field(Fields::qmltypesFileWithPath).key(path);
+}
+inline Path qmltypesFilePath(QString path)
+{
+ return qmltypesFileInfoPath(path).field(Fields::currentItem);
+}
+inline Path jsFileInfoPath(QString path)
+{
+ return Path::Root(PathRoot::Top).field(Fields::jsFileWithPath).key(path);
+}
+inline Path jsFilePath(QString path)
+{
+ return jsFileInfoPath(path).field(Fields::currentItem);
+}
+inline Path qmlDirectoryInfoPath(QString path)
+{
+ return Path::Root(PathRoot::Top).field(Fields::qmlDirectoryWithPath).key(path);
+}
+inline Path qmlDirectoryPath(QString path)
+{
+ return qmlDirectoryInfoPath(path).field(Fields::currentItem);
+}
+inline Path globalScopeInfoPath(QString name)
+{
+ return Path::Root(PathRoot::Top).field(Fields::globalScopeWithName).key(name);
+}
+inline Path globalScopePath(QString name)
+{
+ return globalScopeInfoPath(name).field(Fields::currentItem);
+}
+inline Path lookupCppTypePath(QString name)
+{
+ return Path::Current(PathCurrent::Lookup).field(Fields::cppType).key(name);
+}
+inline Path lookupPropertyPath(QString name)
+{
+ return Path::Current(PathCurrent::Lookup).field(Fields::propertyDef).key(name);
+}
+inline Path lookupSymbolPath(QString name)
+{
+ return Path::Current(PathCurrent::Lookup).field(Fields::symbol).key(name);
+}
+inline Path lookupTypePath(QString name)
+{
+ return Path::Current(PathCurrent::Lookup).field(Fields::type).key(name);
+}
+inline Path loadInfoPath(Path el)
+{
+ return Path::Root(PathRoot::Env).field(Fields::loadInfo).key(el.toString());
+}
+} // end namespace Paths
+
+class IndentInfo
+{
+public:
+ QStringView string;
+ QStringView trailingString;
+ int nNewlines = 0;
+ int column = 0;
+
+ IndentInfo(QStringView line, int tabSize, int initialColumn = 0)
+ {
+ string = line;
+ int fixup = 0;
+ if (initialColumn < 0) // we do not want % of negative numbers
+ fixup = (-initialColumn + tabSize - 1) / tabSize * tabSize;
+ column = initialColumn + fixup;
+ const QChar tab = QLatin1Char('\t');
+ int iStart = 0;
+ int len = line.length();
+ for (int i = 0; i < len; i++) {
+ if (line[i] == tab)
+ column = ((column / tabSize) + 1) * tabSize;
+ else if (line[i] == QLatin1Char('\n')
+ || (line[i] == QLatin1Char('\r')
+ && (i + 1 == len || line[i + 1] != QLatin1Char('\n')))) {
+ iStart = i + 1;
+ ++nNewlines;
+ column = 0;
+ } else if (!line[i].isLowSurrogate())
+ column++;
+ }
+ column -= fixup;
+ trailingString = line.mid(iStart);
+ }
+};
+
+class QMLDOM_EXPORT CommentableDomElement : public DomElement
+{
+public:
+ CommentableDomElement(Path pathFromOwner = Path()) : DomElement(pathFromOwner) { }
+ CommentableDomElement(const CommentableDomElement &o) : DomElement(o), m_comments(o.m_comments)
+ {
+ }
+ CommentableDomElement &operator=(const CommentableDomElement &o) = default;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ RegionComments &comments() { return m_comments; }
+ const RegionComments &comments() const { return m_comments; }
+
+private:
+ RegionComments m_comments;
+};
+
+class QMLDOM_EXPORT Version
+{
+public:
+ constexpr static DomType kindValue = DomType::Version;
+ enum { Undefined = -1, Latest = -2 };
+
+ Version(qint32 majorVersion = Undefined, qint32 minorVersion = Undefined);
+ static Version fromString(QStringView v);
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
+
+ bool isLatest() const;
+ bool isValid() const;
+ QString stringValue() const;
+ QString majorString() const
+ {
+ if (majorVersion >= 0 || majorVersion == Undefined)
+ return QString::number(majorVersion);
+ return QString();
+ }
+ QString majorSymbolicString() const
+ {
+ if (majorVersion == Version::Latest)
+ return QLatin1String("Latest");
+ if (majorVersion >= 0 || majorVersion == Undefined)
+ return QString::number(majorVersion);
+ return QString();
+ }
+ QString minorString() const
+ {
+ if (minorVersion >= 0 || minorVersion == Undefined)
+ return QString::number(minorVersion);
+ return QString();
+ }
+ int compare(const Version &o) const
+ {
+ int c = majorVersion - o.majorVersion;
+ if (c != 0)
+ return c;
+ return minorVersion - o.minorVersion;
+ }
+
+ qint32 majorVersion;
+ qint32 minorVersion;
+};
+inline bool operator==(const Version &v1, const Version &v2)
+{
+ return v1.compare(v2) == 0;
+}
+inline bool operator!=(const Version &v1, const Version &v2)
+{
+ return v1.compare(v2) != 0;
+}
+inline bool operator<(const Version &v1, const Version &v2)
+{
+ return v1.compare(v2) < 0;
+}
+inline bool operator<=(const Version &v1, const Version &v2)
+{
+ return v1.compare(v2) <= 0;
+}
+inline bool operator>(const Version &v1, const Version &v2)
+{
+ return v1.compare(v2) > 0;
+}
+inline bool operator>=(const Version &v1, const Version &v2)
+{
+ return v1.compare(v2) >= 0;
+}
+
+class QMLDOM_EXPORT Import
+{
+ Q_DECLARE_TR_FUNCTIONS(Import)
+public:
+ constexpr static DomType kindValue = DomType::Import;
+
+ static Import fromUriString(QString importStr, Version v = Version(),
+ QString importId = QString(), ErrorHandler handler = nullptr);
+ static Import fromFileString(QString importStr, QString baseDir = QString(),
+ QString importId = QString(), ErrorHandler handler = nullptr);
+
+ Import(QString uri = QString(), Version version = Version(), QString importId = QString())
+ : uri(uri), version(version), importId(importId)
+ {
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
+ bool isDirectoryImport() const
+ {
+ return uri.startsWith(u"http://") || uri.startsWith(u"https://")
+ || uri.startsWith(u"file://");
+ }
+ QString filePath() const
+ {
+ if (uri.startsWith(u"file://"))
+ return uri.mid(7);
+ return QString();
+ }
+ bool isSpecialImport() const { return uri.startsWith(u"<"); }
+ Path importedPath() const
+ {
+ if (isDirectoryImport()) {
+ if (!filePath().isEmpty()) {
+ return Paths::qmlDirPath(filePath());
+ } else {
+ Q_ASSERT_X(false, "Import", "url imports not supported");
+ return Paths::qmldirFilePath(uri);
+ }
+ } else {
+ return Paths::moduleScopePath(uri, version);
+ }
+ }
+ Import baseImport() const { return Import { uri, version }; }
+
+ friend bool operator==(const Import &i1, const Import &i2)
+ {
+ return i1.uri == i2.uri && i1.version == i2.version && i1.importId == i2.importId
+ && i1.comments == i2.comments && i1.implicit == i2.implicit;
+ }
+ friend bool operator!=(const Import &i1, const Import &i2) { return !(i1 == i2); }
+
+ static QRegularExpression importRe();
+
+ QString uri;
+ Version version;
+ QString importId;
+ RegionComments comments;
+ bool implicit = false;
+};
+
+class QMLDOM_EXPORT ModuleAutoExport
+{
+public:
+ constexpr static DomType kindValue = DomType::ModuleAutoExport;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ {
+ bool cont = true;
+ cont = cont && self.dvWrapField(visitor, Fields::import, import);
+ cont = cont && self.dvValueField(visitor, Fields::inheritVersion, inheritVersion);
+ return cont;
+ }
+
+ friend bool operator==(const ModuleAutoExport &i1, const ModuleAutoExport &i2)
+ {
+ return i1.import == i2.import && i1.inheritVersion == i2.inheritVersion;
+ }
+ friend bool operator!=(const ModuleAutoExport &i1, const ModuleAutoExport &i2)
+ {
+ return !(i1 == i2);
+ }
+
+ Import import;
+ bool inheritVersion = false;
+};
+
+class QMLDOM_EXPORT Pragma
+{
+public:
+ constexpr static DomType kindValue = DomType::Pragma;
+
+ Pragma(QString pragmaName = QString()) : name(pragmaName) { }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ {
+ bool cont = self.dvValueField(visitor, Fields::name, name);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
+ return cont;
+ }
+
+ QString name;
+ RegionComments comments;
+};
+
+class QMLDOM_EXPORT Id
+{
+public:
+ constexpr static DomType kindValue = DomType::Id;
+
+ Id(QString idName = QString(), Path referredObject = Path());
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
+ void updatePathFromOwner(Path pathFromOwner);
+ Path addAnnotation(Path selfPathFromOwner, const QmlObject &ann, QmlObject **aPtr = nullptr);
+
+ QString name;
+ Path referredObjectPath;
+ RegionComments comments;
+ QList<QmlObject> annotations;
+};
+
+class QMLDOM_EXPORT ScriptExpression final : public OwningItem
+{
+ Q_GADGET
+ Q_DECLARE_TR_FUNCTIONS(ScriptExpression)
+public:
+ enum class ExpressionType { BindingExpression, FunctionBody, ArgInitializer };
+ Q_ENUM(ExpressionType);
+ constexpr static DomType kindValue = DomType::ScriptExpression;
+ DomType kind() const override { return kindValue; }
+
+ explicit ScriptExpression(QStringView code, std::shared_ptr<QQmlJS::Engine> engine,
+ AST::Node *ast, std::shared_ptr<AstComments> comments,
+ ExpressionType expressionType,
+ SourceLocation localOffset = SourceLocation(), int derivedFrom = 0,
+ QStringView preCode = QStringView(),
+ QStringView postCode = QStringView())
+ : OwningItem(derivedFrom),
+ m_expressionType(expressionType),
+ m_code(code),
+ m_preCode(preCode),
+ m_postCode(postCode),
+ m_engine(engine),
+ m_ast(ast),
+ m_astComments(comments),
+ m_localOffset(localOffset)
+ {
+ Q_ASSERT(m_astComments);
+ }
+
+ ScriptExpression()
+ : ScriptExpression(QStringView(), std::shared_ptr<QQmlJS::Engine>(), nullptr,
+ std::shared_ptr<AstComments>(), ExpressionType::BindingExpression,
+ SourceLocation(), 0)
+ {
+ }
+
+ explicit ScriptExpression(QString code, ExpressionType expressionType, int derivedFrom = 0,
+ QString preCode = QString(), QString postCode = QString())
+ : OwningItem(derivedFrom), m_expressionType(expressionType)
+ {
+ setCode(code, preCode, postCode);
+ }
+
+ ScriptExpression(const ScriptExpression &e);
+
+ std::shared_ptr<ScriptExpression> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<ScriptExpression>(doCopy(self));
+ }
+
+ std::shared_ptr<ScriptExpression> copyWithUpdatedCode(DomItem &self, QString code) const;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+
+ Path canonicalPath(DomItem &self) const override { return self.m_ownerPath; }
+ // parsed and created if not available
+ AST::Node *ast() const { return m_ast; }
+ // dump of the ast (without locations)
+ void astDumper(Sink s, AstDumperOptions options) const;
+ QString astRelocatableDump() const;
+
+ // definedSymbols name, value, from
+ // usedSymbols name, locations
+ QStringView code() const
+ {
+ QMutexLocker l(mutex());
+ return m_code;
+ }
+
+ ExpressionType expressionType() const
+ {
+ QMutexLocker l(mutex());
+ return m_expressionType;
+ }
+
+ bool isNull() const
+ {
+ QMutexLocker l(mutex());
+ return m_code.isNull();
+ }
+ std::shared_ptr<QQmlJS::Engine> engine() const
+ {
+ QMutexLocker l(mutex());
+ return m_engine;
+ }
+ std::shared_ptr<AstComments> astComments() const { return m_astComments; }
+ SourceLocation globalLocation(DomItem &self) const;
+ SourceLocation localOffset() const { return m_localOffset; }
+ QStringView preCode() const { return m_preCode; }
+ QStringView postCode() const { return m_postCode; }
+
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ return std::shared_ptr<OwningItem>(new ScriptExpression(*this));
+ }
+
+ std::function<SourceLocation(SourceLocation)> locationToGlobalF(DomItem &self) const
+ {
+ SourceLocation loc = globalLocation(self);
+ return [loc, this](SourceLocation x) {
+ return SourceLocation(x.offset - m_localOffset.offset + loc.offset, x.length,
+ x.startLine - m_localOffset.startLine + loc.startLine,
+ ((x.startLine == m_localOffset.startLine) ? x.startColumn
+ - m_localOffset.startColumn + loc.startColumn
+ : x.startColumn));
+ };
+ }
+
+ SourceLocation locationToLocal(SourceLocation x) const
+ {
+ return SourceLocation(
+ x.offset - m_localOffset.offset, x.length, x.startLine - m_localOffset.startLine,
+ ((x.startLine == m_localOffset.startLine)
+ ? x.startColumn - m_localOffset.startColumn
+ : x.startColumn)); // are line and column 1 based? then we should + 1
+ }
+
+ std::function<SourceLocation(SourceLocation)> locationToLocalF(DomItem &) const
+ {
+ return [this](SourceLocation x) { return locationToLocal(x); };
+ }
+
+private:
+ void setCode(QString code, QString preCode, QString postCode);
+ ExpressionType m_expressionType;
+ QString m_codeStr;
+ QStringView m_code;
+ QStringView m_preCode;
+ QStringView m_postCode;
+ mutable std::shared_ptr<QQmlJS::Engine> m_engine;
+ mutable AST::Node *m_ast;
+ std::shared_ptr<AstComments> m_astComments;
+ SourceLocation m_localOffset;
+};
+
+class BindingValue;
+
+class QMLDOM_EXPORT Binding
+{
+public:
+ constexpr static DomType kindValue = DomType::Binding;
+
+ Binding(QString m_name = QString(),
+ std::unique_ptr<BindingValue> value = std::unique_ptr<BindingValue>(),
+ BindingType bindingType = BindingType::Normal);
+ Binding(QString m_name, std::shared_ptr<ScriptExpression> value,
+ BindingType bindingType = BindingType::Normal);
+ Binding(QString m_name, QString scriptCode, BindingType bindingType = BindingType::Normal);
+ Binding(QString m_name, QmlObject value, BindingType bindingType = BindingType::Normal);
+ Binding(QString m_name, QList<QmlObject> value, BindingType bindingType = BindingType::Normal);
+ Binding(const Binding &o);
+ Binding(Binding &&o) = default;
+ ~Binding();
+ Binding &operator=(const Binding &);
+ Binding &operator=(Binding &&) = default;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
+ DomItem valueItem(DomItem &self) const;
+ BindingValueKind valueKind() const;
+ QString name() const { return m_name; }
+ BindingType bindingType() const { return m_bindingType; }
+ QmlObject const *objectValue() const;
+ QList<QmlObject> const *arrayValue() const;
+ std::shared_ptr<ScriptExpression> scriptExpressionValue() const;
+ QmlObject *objectValue();
+ QList<QmlObject> *arrayValue();
+ std::shared_ptr<ScriptExpression> scriptExpressionValue();
+ QList<QmlObject> annotations() const { return m_annotations; }
+ void setAnnotations(QList<QmlObject> annotations) { m_annotations = annotations; }
+ void setValue(std::unique_ptr<BindingValue> &&value) { m_value = std::move(value); }
+ Path addAnnotation(Path selfPathFromOwner, const QmlObject &a, QmlObject **aPtr = nullptr);
+ const RegionComments &comments() const { return m_comments; }
+ RegionComments &comments() { return m_comments; }
+ void updatePathFromOwner(Path newPath);
+ bool isSignalHandler() const
+ {
+ QString baseName = m_name.split(QLatin1Char('.')).last();
+ if (baseName.startsWith(u"on") && baseName.length() > 2 && baseName.at(2).isUpper())
+ return true;
+ return false;
+ }
+ QString preCode() const
+ {
+ return QStringLiteral(u"function %1() {\n").arg(m_name.split(u'.').last());
+ }
+ QString postCode() const { return QStringLiteral(u"\n}\n"); }
+
+private:
+ friend class QmlDomAstCreator;
+ BindingType m_bindingType;
+ QString m_name;
+ std::unique_ptr<BindingValue> m_value;
+ QList<QmlObject> m_annotations;
+ RegionComments m_comments;
+};
+
+class QMLDOM_EXPORT AttributeInfo
+{
+public:
+ enum Access { Private, Protected, Public };
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+
+ Path addAnnotation(Path selfPathFromOwner, const QmlObject &annotation,
+ QmlObject **aPtr = nullptr);
+ void updatePathFromOwner(Path newPath);
+
+ QString name;
+ Access access = Access::Public;
+ QString typeName;
+ bool isReadonly = false;
+ bool isList = false;
+ QList<QmlObject> annotations;
+ RegionComments comments;
+};
+
+class QMLDOM_EXPORT PropertyDefinition : public AttributeInfo
+{
+public:
+ constexpr static DomType kindValue = DomType::PropertyDefinition;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ {
+ bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::isPointer, isPointer);
+ cont = cont && self.dvValueField(visitor, Fields::isAlias, isAlias);
+ cont = cont && self.dvValueField(visitor, Fields::isDefaultMember, isDefaultMember);
+ cont = cont && self.dvValueField(visitor, Fields::isRequired, isRequired);
+ cont = cont && self.dvReferenceField(visitor, Fields::type, typePath());
+ return cont;
+ }
+
+ Path typePath() const
+ {
+ Path res = Path::Current(PathCurrent::Types);
+ for (QString el : typeName.split(QChar::fromLatin1('.')))
+ res = res.key(el);
+ return res;
+ }
+
+ bool isPointer = false;
+ bool isAlias = false;
+ bool isDefaultMember = false;
+ bool isRequired = false;
+};
+
+class QMLDOM_EXPORT PropertyInfo
+{
+public:
+ constexpr static DomType kindValue = DomType::PropertyInfo;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+
+ QList<DomItem> propertyDefs;
+ QList<DomItem> bindings;
+};
+
+class QMLDOM_EXPORT MethodParameter
+{
+public:
+ constexpr static DomType kindValue = DomType::MethodParameter;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+
+ QString name;
+ QString typeName;
+ bool isPointer = false;
+ bool isReadonly = false;
+ bool isList = false;
+ std::shared_ptr<ScriptExpression> defaultValue;
+ QList<QmlObject> annotations;
+ RegionComments comments;
+};
+
+class QMLDOM_EXPORT MethodInfo : public AttributeInfo
+{
+ Q_GADGET
+public:
+ enum MethodType { Signal, Method };
+ Q_ENUM(MethodType)
+
+ constexpr static DomType kindValue = DomType::MethodInfo;
+
+ Path typePath(DomItem &) const
+ {
+ return (typeName.isEmpty() ? Path() : Paths::lookupTypePath(typeName));
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ QString preCode(DomItem &) const;
+ QString postCode(DomItem &) const;
+ void setCode(QString code)
+ {
+ body = std::shared_ptr<ScriptExpression>(
+ new ScriptExpression(code, ScriptExpression::ExpressionType::FunctionBody, 0,
+ QLatin1String("function foo(){\n"), QLatin1String("\n}\n")));
+ }
+
+ MethodInfo() = default;
+
+ QList<MethodParameter> parameters;
+ MethodType methodType = Method;
+ std::shared_ptr<ScriptExpression> body;
+};
+
+class QMLDOM_EXPORT EnumItem
+{
+public:
+ constexpr static DomType kindValue = DomType::EnumItem;
+
+ EnumItem(QString name = QString(), int value = 0) : m_name(name), m_value(value) { }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+
+ QString name() const { return m_name; }
+ double value() const { return m_value; }
+ RegionComments &comments() { return m_comments; }
+ const RegionComments &comments() const { return m_comments; }
+
+private:
+ QString m_name;
+ double m_value;
+ RegionComments m_comments;
+};
+
+class QMLDOM_EXPORT EnumDecl final : public CommentableDomElement
+{
+public:
+ constexpr static DomType kindValue = DomType::EnumDecl;
+ DomType kind() const override { return kindValue; }
+
+ EnumDecl(QString name = QString(), QList<EnumItem> values = QList<EnumItem>(),
+ Path pathFromOwner = Path())
+ : CommentableDomElement(pathFromOwner), m_name(name), m_values(values)
+ {
+ }
+ EnumDecl &operator=(const EnumDecl &) = default;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+
+ QString name() const { return m_name; }
+ void setName(QString name) { m_name = name; }
+ QList<EnumItem> values() const { return m_values; }
+ bool isFlag() const { return m_isFlag; }
+ void setIsFlag(bool flag) { m_isFlag = flag; }
+ QString alias() const { return m_alias; }
+ void setAlias(QString aliasName) { m_alias = aliasName; }
+ void setValues(QList<EnumItem> values) { m_values = values; }
+ Path addValue(EnumItem value)
+ {
+ m_values.append(value);
+ return Path::Field(Fields::values).index(index_type(m_values.size() - 1));
+ }
+ void updatePathFromOwner(Path newP) override;
+
+ QList<QmlObject> annotations() const;
+ void setAnnotations(QList<QmlObject> annotations);
+ Path addAnnotation(const QmlObject &child, QmlObject **cPtr = nullptr);
+
+private:
+ QString m_name;
+ bool m_isFlag;
+ QString m_alias;
+ QList<EnumItem> m_values;
+ QList<QmlObject> m_annotations;
+};
+
+class QMLDOM_EXPORT QmlObject final : public CommentableDomElement
+{
+ Q_DECLARE_TR_FUNCTIONS(QmlObject)
+public:
+ constexpr static DomType kindValue = DomType::QmlObject;
+ DomType kind() const override { return kindValue; }
+
+ QmlObject(Path pathFromOwner = Path());
+ QmlObject &operator=(const QmlObject &) = default;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ bool iterateBaseDirectSubpaths(DomItem &self, DirectVisitor);
+ QList<QString> fields() const;
+ QList<QString> fields(DomItem &) const override { return fields(); }
+ DomItem field(DomItem &self, QStringView name);
+ DomItem field(DomItem &self, QStringView name) const override
+ {
+ return const_cast<QmlObject *>(this)->field(self, name);
+ }
+ void updatePathFromOwner(Path newPath) override;
+ QString localDefaultPropertyName() const;
+ QString defaultPropertyName(DomItem &self) const;
+ virtual bool iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor) const;
+
+ QString idStr() const { return m_idStr; }
+ QString name() const { return m_name; }
+ QList<Path> prototypePaths() const { return m_prototypePaths; }
+ Path nextScopePath() const { return m_nextScopePath; }
+ QMultiMap<QString, PropertyDefinition> propertyDefs() const { return m_propertyDefs; }
+ QMultiMap<QString, Binding> bindings() const { return m_bindings; }
+ QMultiMap<QString, MethodInfo> methods() const { return m_methods; }
+ QList<QmlObject> children() const { return m_children; }
+ QList<QmlObject> annotations() const { return m_annotations; }
+
+ void setIdStr(QString id) { m_idStr = id; }
+ void setName(QString name) { m_name = name; }
+ void setDefaultPropertyName(QString name) { m_defaultPropertyName = name; }
+ void setPrototypePaths(QList<Path> prototypePaths) { m_prototypePaths = prototypePaths; }
+ Path addPrototypePath(Path prototypePath)
+ {
+ index_type idx = index_type(m_prototypePaths.indexOf(prototypePath));
+ if (idx == -1) {
+ idx = index_type(m_prototypePaths.size());
+ m_prototypePaths.append(prototypePath);
+ }
+ return Path::Field(Fields::prototypes).index(idx);
+ }
+ void setNextScopePath(Path nextScopePath) { m_nextScopePath = nextScopePath; }
+ void setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs)
+ {
+ m_propertyDefs = propertyDefs;
+ }
+ void setBindings(QMultiMap<QString, Binding> bindings) { m_bindings = bindings; }
+ void setMethods(QMultiMap<QString, MethodInfo> functionDefs) { m_methods = functionDefs; }
+ void setChildren(QList<QmlObject> children)
+ {
+ m_children = children;
+ if (pathFromOwner())
+ updatePathFromOwner(pathFromOwner());
+ }
+ void setAnnotations(QList<QmlObject> annotations)
+ {
+ m_annotations = annotations;
+ if (pathFromOwner())
+ updatePathFromOwner(pathFromOwner());
+ }
+ Path addPropertyDef(PropertyDefinition propertyDef, AddOption option,
+ PropertyDefinition **pDef = nullptr)
+ {
+ return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::propertyDefs),
+ m_propertyDefs, propertyDef.name, propertyDef,
+ option, pDef);
+ }
+ MutableDomItem addPropertyDef(MutableDomItem &self, PropertyDefinition propertyDef,
+ AddOption option);
+
+ Path addBinding(Binding binding, AddOption option, Binding **bPtr = nullptr)
+ {
+ return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::bindings), m_bindings,
+ binding.name(), binding, option, bPtr);
+ }
+ MutableDomItem addBinding(MutableDomItem &self, Binding binding, AddOption option);
+ Path addMethod(MethodInfo functionDef, AddOption option, MethodInfo **mPtr = nullptr)
+ {
+ return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::methods), m_methods,
+ functionDef.name, functionDef, option, mPtr);
+ }
+ MutableDomItem addMethod(MutableDomItem &self, MethodInfo functionDef, AddOption option);
+ Path addChild(QmlObject child, QmlObject **cPtr = nullptr)
+ {
+ return appendUpdatableElementInQList(pathFromOwner().field(Fields::children), m_children,
+ child, cPtr);
+ }
+ MutableDomItem addChild(MutableDomItem &self, QmlObject child)
+ {
+ Path p = addChild(child);
+ return MutableDomItem(self.owner().item(), p);
+ }
+ Path addAnnotation(const QmlObject &annotation, QmlObject **aPtr = nullptr)
+ {
+ return appendUpdatableElementInQList(pathFromOwner().field(Fields::annotations),
+ m_annotations, annotation, aPtr);
+ }
+
+private:
+ friend class QmlDomAstCreator;
+ QString m_idStr;
+ QString m_name;
+ QList<Path> m_prototypePaths;
+ Path m_nextScopePath;
+ QString m_defaultPropertyName;
+ QMultiMap<QString, PropertyDefinition> m_propertyDefs;
+ QMultiMap<QString, Binding> m_bindings;
+ QMultiMap<QString, MethodInfo> m_methods;
+ QList<QmlObject> m_children;
+ QList<QmlObject> m_annotations;
+};
+
+class Export
+{
+ Q_DECLARE_TR_FUNCTIONS(Export)
+public:
+ constexpr static DomType kindValue = DomType::Export;
+ static Export fromString(Path source, QStringView exp, Path typePath, ErrorHandler h);
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ {
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::uri, uri);
+ cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
+ cont = cont && self.dvWrapField(visitor, Fields::version, version);
+ if (typePath)
+ cont = cont && self.dvReferenceField(visitor, Fields::type, typePath);
+ cont = cont && self.dvValueField(visitor, Fields::isInternal, isInternal);
+ cont = cont && self.dvValueField(visitor, Fields::isSingleton, isSingleton);
+ if (exportSourcePath)
+ cont = cont && self.dvReferenceField(visitor, Fields::exportSource, exportSourcePath);
+ return cont;
+ }
+
+ Path exportSourcePath;
+ QString uri;
+ QString typeName;
+ Version version;
+ Path typePath;
+ bool isInternal = false;
+ bool isSingleton = false;
+};
+
+class QMLDOM_EXPORT Component : public CommentableDomElement
+{
+public:
+ Component(QString name);
+ Component(Path pathFromOwner = Path());
+ Component(const Component &o) = default;
+ Component &operator=(const Component &) = default;
+
+ bool iterateDirectSubpaths(DomItem &, DirectVisitor) override;
+ void updatePathFromOwner(Path newPath) override;
+ DomItem field(DomItem &self, QStringView name) const override
+ {
+ return const_cast<Component *>(this)->field(self, name);
+ }
+ DomItem field(DomItem &self, QStringView name);
+
+ QString name() const { return m_name; }
+ QMultiMap<QString, EnumDecl> enumerations() const { return m_enumerations; }
+ QList<QmlObject> objects() const { return m_objects; }
+ bool isSingleton() const { return m_isSingleton; }
+ bool isCreatable() const { return m_isCreatable; }
+ bool isComposite() const { return m_isComposite; }
+ QString attachedTypeName() const { return m_attachedTypeName; }
+ Path attachedTypePath(DomItem &) const { return m_attachedTypePath; }
+
+ void setName(QString name) { m_name = name; }
+ void setEnumerations(QMultiMap<QString, EnumDecl> enumerations)
+ {
+ m_enumerations = enumerations;
+ }
+ Path addEnumeration(const EnumDecl &enumeration, AddOption option = AddOption::Overwrite,
+ EnumDecl **ePtr = nullptr)
+ {
+ return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::enumerations),
+ m_enumerations, enumeration.name(), enumeration,
+ option, ePtr);
+ }
+ void setObjects(QList<QmlObject> objects) { m_objects = objects; }
+ Path addObject(const QmlObject &object, QmlObject **oPtr = nullptr);
+ void setIsSingleton(bool isSingleton) { m_isSingleton = isSingleton; }
+ void setIsCreatable(bool isCreatable) { m_isCreatable = isCreatable; }
+ void setIsComposite(bool isComposite) { m_isComposite = isComposite; }
+ void setAttachedTypeName(QString name) { m_attachedTypeName = name; }
+ void setAttachedTypePath(Path p) { m_attachedTypePath = p; }
+
+private:
+ friend class QmlDomAstCreator;
+ QString m_name;
+ QMultiMap<QString, EnumDecl> m_enumerations;
+ QList<QmlObject> m_objects;
+ bool m_isSingleton = false;
+ bool m_isCreatable = true;
+ bool m_isComposite = true;
+ QString m_attachedTypeName;
+ Path m_attachedTypePath;
+};
+
+class QMLDOM_EXPORT JsResource final : public Component
+{
+public:
+ constexpr static DomType kindValue = DomType::JsResource;
+ DomType kind() const override { return kindValue; }
+
+ JsResource(Path pathFromOwner = Path()) : Component(pathFromOwner) { }
+ bool iterateDirectSubpaths(DomItem &, DirectVisitor) override
+ { // to do: complete
+ return true;
+ }
+ // globalSymbols defined/exported, required/used
+};
+
+class QMLDOM_EXPORT QmltypesComponent final : public Component
+{
+public:
+ constexpr static DomType kindValue = DomType::QmltypesComponent;
+ DomType kind() const override { return kindValue; }
+
+ QmltypesComponent(Path pathFromOwner = Path()) : Component(pathFromOwner) { }
+ bool iterateDirectSubpaths(DomItem &, DirectVisitor) override;
+ QList<Export> exports() const { return m_exports; }
+ QString fileName() const { return m_fileName; }
+ void setExports(QList<Export> exports) { m_exports = exports; }
+ void addExport(const Export &exportedEntry) { m_exports.append(exportedEntry); }
+ void setFileName(QString fileName) { m_fileName = fileName; }
+ QList<int> metaRevisions() const { return m_metaRevisions; }
+ void setMetaRevisions(QList<int> metaRevisions) { m_metaRevisions = metaRevisions; }
+
+private:
+ QList<Export> m_exports;
+ QList<int> m_metaRevisions;
+ QString m_fileName; // remove?
+};
+
+class QMLDOM_EXPORT QmlComponent final : public Component
+{
+public:
+ constexpr static DomType kindValue = DomType::QmlComponent;
+ DomType kind() const override { return kindValue; }
+
+ QmlComponent(QString name = QString()) : Component(name)
+ {
+ setIsComposite(true);
+ setIsCreatable(true);
+ }
+
+ QmlComponent(const QmlComponent &o)
+ : Component(o), m_nextComponentPath(o.m_nextComponentPath), m_ids(o.m_ids)
+ {
+ }
+ QmlComponent &operator=(const QmlComponent &) = default;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+
+ QMultiMap<QString, Id> ids() const { return m_ids; }
+ Path nextComponentPath() const { return m_nextComponentPath; }
+ void setIds(QMultiMap<QString, Id> ids) { m_ids = ids; }
+ void setNextComponentPath(Path p) { m_nextComponentPath = p; }
+ void updatePathFromOwner(Path newPath) override;
+ Path addId(const Id &id, AddOption option = AddOption::Overwrite, Id **idPtr = nullptr)
+ {
+ // warning does nor remove old idStr when overwriting...
+ return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::ids), m_ids, id.name,
+ id, option, idPtr);
+ }
+ QList<QString> subComponentsNames(DomItem &self) const;
+ QList<DomItem> subComponents(DomItem &self) const;
+
+private:
+ friend class QmlDomAstCreator;
+ Path m_nextComponentPath;
+ QMultiMap<QString, Id> m_ids;
+};
+
+class QMLDOM_EXPORT GlobalComponent final : public Component
+{
+public:
+ constexpr static DomType kindValue = DomType::GlobalComponent;
+ DomType kind() const override { return kindValue; }
+
+ GlobalComponent(Path pathFromOwner = Path()) : Component(pathFromOwner) { }
+};
+
+static ErrorGroups importErrors = { { DomItem::domErrorGroup, NewErrorGroup("importError") } };
+
+class QMLDOM_EXPORT ImportScope
+{
+ Q_DECLARE_TR_FUNCTIONS(ImportScope)
+public:
+ constexpr static DomType kindValue = DomType::ImportScope;
+
+ ImportScope() = default;
+ ~ImportScope() = default;
+
+ QList<Path> importSourcePaths() const { return m_importSourcePaths; }
+
+ QMap<QString, ImportScope> subImports() const { return m_subImports; }
+
+ QList<Path> allSources(DomItem &self) const;
+
+ QSet<QString> importedNames(DomItem &self) const
+ {
+ QSet<QString> res;
+ for (Path p : allSources(self)) {
+ QSet<QString> ks = self.path(p.field(Fields::exports), self.errorHandler()).keys();
+ res += ks;
+ }
+ return res;
+ }
+
+ QList<DomItem> importedItemsWithName(DomItem &self, QString name) const
+ {
+ QList<DomItem> res;
+ for (Path p : allSources(self)) {
+ DomItem source = self.path(p.field(Fields::exports), self.errorHandler());
+ DomItem els = source.key(name);
+ int nEls = els.indexes();
+ for (int i = 0; i < nEls; ++i)
+ res.append(els.index(i));
+ if (nEls == 0 && els) {
+ self.addError(importErrors.warning(
+ tr("Looking up '%1' expected a list of exports, not %2")
+ .arg(name, els.toString())));
+ }
+ }
+ return res;
+ }
+
+ QList<Export> importedExportsWithName(DomItem &self, QString name) const
+ {
+ QList<Export> res;
+ for (DomItem &i : importedItemsWithName(self, name))
+ if (const Export *e = i.as<Export>())
+ res.append(*e);
+ else
+ self.addError(importErrors.warning(
+ tr("Expected Export looking up '%1', not %1").arg(name, i.toString())));
+ return res;
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+
+ void addImport(QStringList p, Path targetExports)
+ {
+ if (!p.isEmpty()) {
+ QString current = p.takeFirst();
+ m_subImports[current].addImport(p, targetExports);
+ } else if (!m_importSourcePaths.contains(targetExports)) {
+ m_importSourcePaths.append(targetExports);
+ }
+ }
+
+private:
+ QList<Path> m_importSourcePaths;
+ QMap<QString, ImportScope> m_subImports;
+};
+
+class BindingValue
+{
+public:
+ BindingValue();
+ BindingValue(const QmlObject &o);
+ BindingValue(std::shared_ptr<ScriptExpression> o);
+ BindingValue(const QList<QmlObject> &l);
+ ~BindingValue();
+ BindingValue(const BindingValue &o);
+ BindingValue &operator=(const BindingValue &o);
+
+ DomItem value(DomItem &binding);
+ void updatePathFromOwner(Path newPath);
+
+private:
+ friend class Binding;
+ void clearValue();
+
+ BindingValueKind kind;
+ union {
+ int dummy;
+ QmlObject object;
+ std::shared_ptr<ScriptExpression> scriptExpression;
+ QList<QmlObject> array;
+ };
+};
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
+#endif // QQMLDOMELEMENTS_P_H
diff --git a/src/qmldom/qqmldomerrormessage.cpp b/src/qmldom/qqmldomerrormessage.cpp
index d95b7c1f6b..e621ac775f 100644
--- a/src/qmldom/qqmldomerrormessage.cpp
+++ b/src/qmldom/qqmldomerrormessage.cpp
@@ -49,6 +49,8 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
+Q_LOGGING_CATEGORY(domLog, "qt.qmldom", QtWarningMsg);
+
enum {
FatalMsgMaxLen=511
};
@@ -182,7 +184,8 @@ ErrorMessage ErrorGroups::errorMessage(Dumper msg, ErrorLevel level, Path elemen
ErrorMessage ErrorGroups::errorMessage(const DiagnosticMessage &msg, Path element, QString canonicalFilePath) const
{
ErrorMessage res(*this, msg, element, canonicalFilePath);
- if (!res.location.isValid() && (res.location.startLine != 0 || res.location.startColumn != 0)) {
+ if (res.location == SourceLocation()
+ && (res.location.startLine != 0 || res.location.startColumn != 0)) {
res.location.offset = -1;
res.location.length = 1;
}
@@ -420,7 +423,7 @@ ErrorMessage &ErrorMessage::withItem(DomItem el)
path = el.canonicalPath();
if (file.isEmpty())
file = el.canonicalFilePath();
- if (!location.isValid()) {
+ if (location == SourceLocation()) {
if (const FileLocations *fLocPtr = FileLocations::fileLocationsPtr(el)) {
location = fLocPtr->regions.value(QString(), fLocPtr->fullRegion);
}
@@ -460,7 +463,10 @@ void ErrorMessage::dump(Sink sink) const
sink(message);
if (path.length()>0) {
sink(u" for ");
- path.dump(sink);
+ if (!file.isEmpty() && path.length() > 3 && path.headKind() == Path::Kind::Root)
+ path.mid(3).dump(sink);
+ else
+ path.dump(sink);
}
}
diff --git a/src/qmldom/qqmldomerrormessage_p.h b/src/qmldom/qqmldomerrormessage_p.h
index 714ab5c4a5..fc3e0a2e11 100644
--- a/src/qmldom/qqmldomerrormessage_p.h
+++ b/src/qmldom/qqmldomerrormessage_p.h
@@ -58,6 +58,7 @@
#include <QtCore/QString>
#include <QtCore/QCborArray>
#include <QtCore/QCborMap>
+#include <QtCore/QLoggingCategory>
#include <QtQml/private/qqmljsdiagnosticmessage_p.h>
QT_BEGIN_NAMESPACE
@@ -65,6 +66,8 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
+Q_DECLARE_LOGGING_CATEGORY(domLog);
+
QMLDOM_EXPORT ErrorLevel errorLevelFromQtMsgType(QtMsgType msgType);
class ErrorGroups;
diff --git a/src/qmldom/qqmldomexternalitems.cpp b/src/qmldom/qqmldomexternalitems.cpp
index 2d71cbc170..ea884fb5c0 100644
--- a/src/qmldom/qqmldomexternalitems.cpp
+++ b/src/qmldom/qqmldomexternalitems.cpp
@@ -38,6 +38,9 @@
#include "qqmldomexternalitems_p.h"
#include "qqmldomtop_p.h"
+#include "qqmldomcomments_p.h"
+#include "qqmldommock_p.h"
+#include "qqmldomelements_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -47,6 +50,8 @@
#include <QtCore/QDir>
#include <QtCore/QScopeGuard>
#include <QtCore/QFileInfo>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QRegularExpressionMatch>
#include <algorithm>
@@ -55,16 +60,23 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-ExternalOwningItem::ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path path, int derivedFrom):
- OwningItem(derivedFrom, lastDataUpdateAt), m_canonicalFilePath(filePath), m_path(path)
+ExternalOwningItem::ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path path,
+ int derivedFrom, QString code)
+ : OwningItem(derivedFrom, lastDataUpdateAt),
+ m_canonicalFilePath(filePath),
+ m_code(code),
+ m_path(path)
{}
-ExternalOwningItem::ExternalOwningItem(const ExternalOwningItem &o):
- OwningItem(o), m_canonicalFilePath(o.m_canonicalFilePath),
- m_path(o.m_path), m_isValid(o.m_isValid)
+ExternalOwningItem::ExternalOwningItem(const ExternalOwningItem &o)
+ : OwningItem(o),
+ m_canonicalFilePath(o.m_canonicalFilePath),
+ m_code(o.m_code),
+ m_path(o.m_path),
+ m_isValid(o.m_isValid)
{}
-QString ExternalOwningItem::canonicalFilePath(const DomItem &) const
+QString ExternalOwningItem::canonicalFilePath(DomItem &) const
{
return m_canonicalFilePath;
}
@@ -74,7 +86,7 @@ QString ExternalOwningItem::canonicalFilePath() const
return m_canonicalFilePath;
}
-Path ExternalOwningItem::canonicalPath(const DomItem &) const
+Path ExternalOwningItem::canonicalPath(DomItem &) const
{
return m_path;
}
@@ -84,6 +96,377 @@ Path ExternalOwningItem::canonicalPath() const
return m_path;
}
+ErrorGroups QmldirFile::myParsingErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Qmldir"),
+ NewErrorGroup("Parsing") } };
+ return res;
+}
+
+QmldirFile::QmldirFile(const QmldirFile &o)
+ : ExternalOwningItem(o),
+ m_uri(o.m_uri),
+ m_qmldir(o.m_qmldir),
+ m_plugins(o.m_plugins),
+ m_qmltypesFilePaths(o.m_qmltypesFilePaths)
+{
+ m_imports += o.m_imports;
+ m_exports += o.m_exports;
+}
+
+std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(QString path, QString code)
+{
+ QString canonicalFilePath = QFileInfo(path).canonicalFilePath();
+ QDateTime dataUpdate = QDateTime::currentDateTime();
+ std::shared_ptr<QmldirFile> res(new QmldirFile(canonicalFilePath, code, dataUpdate));
+ if (canonicalFilePath.isEmpty() && !path.isEmpty())
+ res->addErrorLocal(
+ myParsingErrors().error(tr("QmldirFile started from invalid path '%1'").arg(path)));
+ res->parse();
+ return res;
+}
+
+void QmldirFile::parse()
+{
+ if (canonicalFilePath().isEmpty()) {
+ addErrorLocal(myParsingErrors().error(tr("canonicalFilePath is empty")));
+ setIsValid(false);
+ } else {
+ m_qmldir.parse(m_code);
+ setFromQmldir();
+ }
+}
+
+void QmldirFile::setFromQmldir()
+{
+ m_uri = m_qmldir.typeNamespace();
+ if (m_uri.isEmpty())
+ m_uri = QStringLiteral(u"file://") + canonicalFilePath();
+ Path exportsPath = Path::Field(Fields::exports);
+ QDir baseDir = QFileInfo(canonicalFilePath()).dir();
+ int majorVersion = Version::Undefined;
+ bool ok;
+ int vNr = QFileInfo(baseDir.dirName()).suffix().toInt(&ok);
+ if (ok && vNr > 0) // accept 0?
+ majorVersion = vNr;
+ Path exportSource = canonicalPath();
+ for (auto const &el : m_qmldir.components()) {
+ QString exportFilePath = baseDir.filePath(el.fileName);
+ QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath();
+ if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
+ // created where we expect it)
+ canonicalExportFilePath = exportFilePath;
+ Export exp;
+ exp.exportSourcePath = exportSource;
+ exp.isSingleton = el.singleton;
+ exp.isInternal = el.internal;
+ exp.version =
+ Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion),
+ el.version.hasMinorVersion() ? el.version.minorVersion() : 0);
+ exp.typeName = el.typeName;
+ exp.typePath = Paths::qmlFileObjectPath(canonicalExportFilePath);
+ exp.uri = uri();
+ m_exports.insert(exp.typeName, exp);
+ }
+ for (auto const &el : m_qmldir.scripts()) {
+ QString exportFilePath = baseDir.filePath(el.fileName);
+ QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath();
+ if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
+ // created where we expect it)
+ canonicalExportFilePath = exportFilePath;
+ Export exp;
+ exp.exportSourcePath = exportSource;
+ exp.isSingleton = true;
+ exp.isInternal = false;
+ exp.version =
+ Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion),
+ el.version.hasMinorVersion() ? el.version.minorVersion() : 0);
+ exp.typePath = Paths::jsFilePath(canonicalExportFilePath).field(Fields::rootComponent);
+ exp.uri = uri();
+ exp.typeName = el.nameSpace;
+ m_exports.insert(exp.typeName, exp);
+ }
+ for (QQmlDirParser::Import const &imp : m_qmldir.imports()) {
+ QString uri = imp.module;
+ bool isAutoImport = imp.flags & QQmlDirParser::Import::Auto;
+ Version v;
+ if (isAutoImport)
+ v = Version(majorVersion, int(Version::Latest));
+ else {
+ v = Version((imp.version.hasMajorVersion() ? imp.version.majorVersion()
+ : int(Version::Latest)),
+ (imp.version.hasMinorVersion() ? imp.version.minorVersion()
+ : int(Version::Latest)));
+ }
+ m_imports.append(Import(uri, v));
+ m_autoExports.append(ModuleAutoExport { Import(uri, v), isAutoImport });
+ }
+ for (QQmlDirParser::Import const &imp : m_qmldir.dependencies()) {
+ QString uri = imp.module;
+ if (imp.flags & QQmlDirParser::Import::Auto)
+ qWarning() << "qmldir contains dependency with auto keyword";
+ Version v = Version(
+ (imp.version.hasMajorVersion() ? imp.version.majorVersion() : int(Version::Latest)),
+ (imp.version.hasMinorVersion() ? imp.version.minorVersion()
+ : int(Version::Latest)));
+ m_imports.append(Import(uri, v));
+ }
+ bool hasInvalidTypeinfo = false;
+ for (auto const &el : m_qmldir.typeInfos()) {
+ QString elStr = el;
+ QFileInfo elPath(elStr);
+ if (elPath.isRelative())
+ elPath = QFileInfo(baseDir.filePath(elStr));
+ QString typeInfoPath = elPath.canonicalFilePath();
+ if (typeInfoPath.isEmpty()) {
+ hasInvalidTypeinfo = true;
+ typeInfoPath = elPath.absoluteFilePath();
+ }
+ m_qmltypesFilePaths.append(Paths::qmltypesFilePath(typeInfoPath));
+ }
+ if (m_qmltypesFilePaths.isEmpty() || hasInvalidTypeinfo) {
+ // add all type info files in the directory...
+ for (QFileInfo const &entry :
+ baseDir.entryInfoList(QStringList({ QLatin1String("*.qmltypes") }),
+ QDir::Filter::Readable | QDir::Filter::Files)) {
+ Path p = Paths::qmltypesFilePath(entry.canonicalFilePath());
+ if (!m_qmltypesFilePaths.contains(p))
+ m_qmltypesFilePaths.append(p);
+ }
+ }
+ bool hasErrors = false;
+ for (auto const &el : m_qmldir.errors(uri())) {
+ ErrorMessage msg = myParsingErrors().errorMessage(el);
+ addErrorLocal(msg);
+ if (msg.level == ErrorLevel::Error || msg.level == ErrorLevel::Fatal)
+ hasErrors = true;
+ }
+ setIsValid(!hasErrors); // consider it valid also with errors?
+ m_plugins = m_qmldir.plugins();
+}
+
+QList<ModuleAutoExport> QmldirFile::autoExports() const
+{
+ return m_autoExports;
+}
+
+void QmldirFile::setAutoExports(const QList<ModuleAutoExport> &autoExport)
+{
+ m_autoExports = autoExport;
+}
+
+QCborValue pluginData(QQmlDirParser::Plugin &pl, QStringList cNames)
+{
+ QCborArray names;
+ for (QString n : cNames)
+ names.append(n);
+ return QCborMap({ { QCborValue(QStringView(Fields::name)), pl.name },
+ { QStringView(Fields::path), pl.path },
+ { QStringView(Fields::classNames), names } });
+}
+
+bool QmldirFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::uri, uri());
+ cont = cont && self.dvValueField(visitor, Fields::designerSupported, designerSupported());
+ cont = cont && self.dvReferencesField(visitor, Fields::qmltypesFiles, m_qmltypesFilePaths);
+ cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
+ cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
+ cont = cont && self.dvItemField(visitor, Fields::plugins, [this, &self]() {
+ QStringList cNames = classNames();
+ return self.subListItem(List::fromQListRef<QQmlDirParser::Plugin>(
+ self.pathFromOwner().field(Fields::plugins), m_plugins,
+ [cNames](DomItem &list, const PathEls::PathComponent &p,
+ QQmlDirParser::Plugin &plugin) {
+ return list.subDataItem(p, pluginData(plugin, cNames));
+ }));
+ });
+ cont = cont && self.dvWrapField(visitor, Fields::autoExports, m_autoExports);
+ return cont;
+}
+
+std::shared_ptr<OwningItem> QmlFile::doCopy(DomItem &) const
+{
+ std::shared_ptr<QmlFile> res(new QmlFile(*this));
+ return res;
+}
+
+QmlFile::QmlFile(const QmlFile &o)
+ : ExternalOwningItem(o),
+ m_engine(o.m_engine),
+ m_ast(o.m_ast),
+ m_astComments(o.m_astComments),
+ m_comments(o.m_comments),
+ m_fileLocationsTree(o.m_fileLocationsTree),
+ m_importScope(o.m_importScope)
+{
+ m_pragmas += o.m_pragmas;
+ m_components += o.m_components;
+ m_imports += o.m_imports;
+ Q_ASSERT(m_astComments);
+ if (m_astComments)
+ m_astComments = std::shared_ptr<AstComments>(new AstComments(*m_astComments));
+}
+
+QmlFile::QmlFile(QString filePath, QString code, QDateTime lastDataUpdateAt, int derivedFrom)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlFilePath(filePath), derivedFrom,
+ code),
+ m_engine(new QQmlJS::Engine),
+ m_astComments(new AstComments(m_engine)),
+ m_fileLocationsTree(FileLocations::createTree(canonicalPath()))
+{
+ QQmlJS::Lexer lexer(m_engine.get());
+ lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/true);
+ QQmlJS::Parser parser(m_engine.get());
+ m_isValid = parser.parse();
+ for (DiagnosticMessage msg : parser.diagnosticMessages())
+ addErrorLocal(myParsingErrors().errorMessage(msg).withFile(filePath).withPath(m_path));
+ m_ast = parser.ast();
+}
+
+ErrorGroups QmlFile::myParsingErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("QmlFile"),
+ NewErrorGroup("Parsing") } };
+ return res;
+}
+
+bool QmlFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::isValid, m_isValid);
+ cont = cont && self.dvWrapField(visitor, Fields::components, m_components);
+ cont = cont && self.dvWrapField(visitor, Fields::pragmas, m_pragmas);
+ cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
+ cont = cont && self.dvWrapField(visitor, Fields::importScope, m_importScope);
+ cont = cont && self.dvWrapField(visitor, Fields::importScope, m_importScope);
+ cont = cont && self.dvWrapField(visitor, Fields::fileLocationsTree, m_fileLocationsTree);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
+ cont = cont && self.dvWrapField(visitor, Fields::astComments, m_astComments);
+ return cont;
+}
+
+DomItem QmlFile::field(DomItem &self, QStringView name)
+{
+ if (name == Fields::components)
+ return self.wrapField(Fields::components, m_components);
+ return DomBase::field(self, name);
+}
+
+void QmlFile::addError(DomItem &self, ErrorMessage msg)
+{
+ self.containingObject().addError(msg);
+}
+
+std::shared_ptr<OwningItem> GlobalScope::doCopy(DomItem &self) const
+{
+ std::shared_ptr<GlobalScope> res(
+ new GlobalScope(canonicalFilePath(self), lastDataUpdateAt(), revision()));
+ return res;
+}
+
+bool GlobalScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ return cont;
+}
+
+QmltypesFile::QmltypesFile(const QmltypesFile &o) : ExternalOwningItem(o), m_uris(o.m_uris)
+{
+ m_imports += o.m_imports;
+ m_components += o.m_components;
+ m_exports += o.m_exports;
+}
+
+void QmltypesFile::ensureInModuleIndex(DomItem &self)
+{
+ auto it = m_uris.begin();
+ auto end = m_uris.end();
+ DomItem env = self.environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
+ while (it != end) {
+ QString uri = it.key();
+ for (int majorV : it.value()) {
+ auto mIndex = envPtr->moduleIndexWithUri(env, uri, majorV, EnvLookup::Normal,
+ Changeable::Writable);
+ mIndex->addQmltypeFilePath(self.canonicalPath());
+ }
+ ++it;
+ }
+ }
+}
+
+bool QmltypesFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::components, m_components);
+ cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
+ cont = cont && self.dvItemField(visitor, Fields::uris, [this, &self]() {
+ return self.subMapItem(Map::fromMapRef<QSet<int>>(
+ self.pathFromOwner().field(Fields::uris), m_uris,
+ [](DomItem &map, const PathEls::PathComponent &p, QSet<int> &el) {
+ QList<int> l(el.cbegin(), el.cend());
+ std::sort(l.begin(), l.end());
+ return map.subListItem(
+ List::fromQList<int>(map.pathFromOwner().appendComponent(p), l,
+ [](DomItem &list, const PathEls::PathComponent &p,
+ int &el) { return list.subDataItem(p, el); }));
+ }));
+ });
+ cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
+ return cont;
+}
+
+QmlDirectory::QmlDirectory(QString filePath, QStringList dirList, QDateTime lastDataUpdateAt,
+ int derivedFrom)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlDirectoryPath(filePath), derivedFrom,
+ dirList.join(QLatin1Char('\n')))
+{
+ for (QString f : dirList) {
+ addQmlFilePath(f);
+ }
+}
+
+bool QmlDirectory::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
+ cont = cont && self.dvItemField(visitor, Fields::qmlFiles, [this, &self]() -> DomItem {
+ QDir baseDir(canonicalFilePath());
+ return self.subMapItem(Map::fromMultiMapRef<QString>(
+ self.pathFromOwner().field(Fields::qmlFiles), m_qmlFiles,
+ [baseDir](DomItem &map, const PathEls::PathComponent &p,
+ QString &rPath) -> DomItem {
+ return map.subReferenceItem(
+ p,
+ Paths::qmlFilePath(
+ QFileInfo(baseDir.filePath(rPath)).canonicalFilePath()));
+ }));
+ });
+ return cont;
+}
+
+bool QmlDirectory::addQmlFilePath(QString relativePath)
+{
+ QRegularExpression qmlFileRe(QRegularExpression::anchoredPattern(
+ uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|ui|qmlannotation))"));
+ QRegularExpressionMatch m = qmlFileRe.match(relativePath);
+ if (m.hasMatch() && !m_qmlFiles.values(m.captured(u"compName")).contains(relativePath)) {
+ m_qmlFiles.insert(m.captured(u"compName"), relativePath);
+ Export e;
+ QDir dir(canonicalFilePath());
+ QFileInfo fInfo(dir.filePath(relativePath));
+ e.exportSourcePath = canonicalPath();
+ e.typeName = m.captured(u"compName");
+ e.typePath = Paths::qmlFileObjectPath(fInfo.canonicalFilePath());
+ e.uri = QLatin1String("file://") + canonicalFilePath();
+ m_exports.insert(e.typeName, e);
+ return true;
+ }
+ return false;
+}
+
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomexternalitems_p.h b/src/qmldom/qqmldomexternalitems_p.h
index 84636702c5..e9de497c3c 100644
--- a/src/qmldom/qqmldomexternalitems_p.h
+++ b/src/qmldom/qqmldomexternalitems_p.h
@@ -50,6 +50,9 @@
//
#include "qqmldomitem_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldommoduleindex_p.h"
+#include "qqmldomcomments_p.h"
#include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljsengine_p.h>
@@ -77,18 +80,40 @@ Every owning item has a file or directory it refers to.
*/
class QMLDOM_EXPORT ExternalOwningItem: public OwningItem {
public:
- ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path pathFromTop, int derivedFrom=0);
+ ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path pathFromTop,
+ int derivedFrom = 0, QString code = QString());
ExternalOwningItem(const ExternalOwningItem &o);
- QString canonicalFilePath(const DomItem &) const override;
+ QString canonicalFilePath(DomItem &) const override;
QString canonicalFilePath() const;
- Path canonicalPath(const DomItem &) const override;
+ Path canonicalPath(DomItem &) const override;
Path canonicalPath() const;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor) override {
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
+ {
bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
- cont = cont && self.subDataField(Fields::canonicalFilePath, canonicalFilePath()).visit(visitor);
- cont = cont && self.subDataField(Fields::isValid, isValid()).visit(visitor);
-// if (!code().isNull())
-// cont = cont && self.subDataField(Fields::code, code()).visit(visitor);
+ cont = cont && self.dvValueLazyField(visitor, Fields::canonicalFilePath, [this]() {
+ return canonicalFilePath();
+ });
+ cont = cont
+ && self.dvValueLazyField(visitor, Fields::isValid, [this]() { return isValid(); });
+ if (!code().isNull())
+ cont = cont
+ && self.dvValueLazyField(visitor, Fields::code, [this]() { return code(); });
+ return cont;
+ }
+
+ bool iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor) override
+ {
+ bool cont = OwningItem::iterateSubOwners(self, visitor);
+ cont = cont && self.field(Fields::components).visitKeys([visitor](QString, DomItem &comps) {
+ return comps.visitIndexes([visitor](DomItem &comp) {
+ return comp.field(Fields::objects).visitIndexes([visitor](DomItem &qmlObj) {
+ if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>())
+ return qmlObjPtr->iterateSubOwners(qmlObj, visitor);
+ Q_ASSERT(false);
+ return true;
+ });
+ });
+ });
return cont;
}
@@ -101,13 +126,339 @@ public:
m_isValid = val;
}
// null code means invalid
- virtual QString code() const { return QString(); }
+ const QString &code() const { return m_code; }
+
protected:
QString m_canonicalFilePath;
+ QString m_code;
Path m_path;
bool m_isValid = false;
};
+class QMLDOM_EXPORT QmlDirectory final : public ExternalOwningItem
+{
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ return std::shared_ptr<OwningItem>(new QmlDirectory(*this));
+ }
+
+public:
+ constexpr static DomType kindValue = DomType::QmlDirectory;
+ DomType kind() const override { return kindValue; }
+ QmlDirectory(QString filePath = QString(), QStringList dirList = QStringList(),
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0),
+ int derivedFrom = 0);
+ QmlDirectory(const QmlDirectory &o)
+ : ExternalOwningItem(o), m_exports(o.m_exports), m_qmlFiles(o.m_qmlFiles)
+ {
+ }
+
+ std::shared_ptr<QmlDirectory> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<QmlDirectory>(doCopy(self));
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+
+ QMultiMap<QString, Export> exports() const { return m_exports; }
+
+ QMultiMap<QString, QString> qmlFiles() const { return m_qmlFiles; }
+
+ bool addQmlFilePath(QString relativePath);
+
+private:
+ QMultiMap<QString, Export> m_exports;
+ QMultiMap<QString, QString> m_qmlFiles;
+};
+
+class QMLDOM_EXPORT QmldirFile final : public ExternalOwningItem
+{
+ Q_DECLARE_TR_FUNCTIONS(QmldirFile)
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ std::shared_ptr<OwningItem> copy(new QmldirFile(*this));
+ return copy;
+ }
+
+public:
+ constexpr static DomType kindValue = DomType::QmldirFile;
+ DomType kind() const override { return kindValue; }
+
+ static ErrorGroups myParsingErrors();
+
+ QmldirFile(QString filePath = QString(), QString code = QString(),
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0), int derivedFrom = 0)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmldirFilePath(filePath),
+ derivedFrom, code)
+ {
+ }
+ QmldirFile(const QmldirFile &o);
+
+ static std::shared_ptr<QmldirFile> fromPathAndCode(QString path, QString code);
+
+ std::shared_ptr<QmldirFile> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<QmldirFile>(doCopy(self));
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+
+ QString uri() const { return m_uri; }
+
+ QMultiMap<QString, Export> exports() const { return m_exports; }
+
+ QList<Import> imports() const { return m_imports; }
+
+ QList<Path> qmltypesFilePaths() const { return m_qmltypesFilePaths; }
+
+ bool designerSupported() const { return m_qmldir.designerSupported(); }
+
+ QStringList classNames() const { return m_qmldir.classNames(); }
+
+ QList<ModuleAutoExport> autoExports() const;
+ void setAutoExports(const QList<ModuleAutoExport> &autoExport);
+
+private:
+ void parse();
+ void setFromQmldir();
+
+ QString m_uri;
+ QQmlDirParser m_qmldir;
+ QList<QQmlDirParser::Plugin> m_plugins;
+ QList<Import> m_imports;
+ QList<ModuleAutoExport> m_autoExports;
+ QMultiMap<QString, Export> m_exports;
+ QList<Path> m_qmltypesFilePaths;
+};
+
+class QMLDOM_EXPORT JsFile final : public ExternalOwningItem
+{
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ std::shared_ptr<OwningItem> copy(new JsFile(*this));
+ return copy;
+ }
+
+public:
+ constexpr static DomType kindValue = DomType::JsFile;
+ DomType kind() const override { return kindValue; }
+ JsFile(QString filePath = QString(),
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0),
+ Path pathFromTop = Path(), int derivedFrom = 0)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, pathFromTop, derivedFrom)
+ {
+ }
+ JsFile(const JsFile &o) : ExternalOwningItem(o), m_rootComponent(o.m_rootComponent) { }
+
+ std::shared_ptr<JsFile> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<JsFile>(doCopy(self));
+ }
+
+ std::shared_ptr<QQmlJS::Engine> engine() const { return m_engine; }
+ JsResource rootComponent() const { return m_rootComponent; }
+
+private:
+ std::shared_ptr<QQmlJS::Engine> m_engine;
+ JsResource m_rootComponent;
+};
+
+class QMLDOM_EXPORT QmlFile final : public ExternalOwningItem
+{
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+
+public:
+ constexpr static DomType kindValue = DomType::QmlFile;
+ DomType kind() const override { return kindValue; }
+
+ QmlFile(const QmlFile &o);
+ QmlFile(QString filePath = QString(), QString code = QString(),
+ QDateTime lastDataUpdate = QDateTime::fromMSecsSinceEpoch(0), int derivedFrom = 0);
+ static ErrorGroups myParsingErrors();
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor)
+ override; // iterates the *direct* subpaths, returns false if a quick end was requested
+ DomItem field(DomItem &self, QStringView name) const override
+ {
+ return const_cast<QmlFile *>(this)->field(self, name);
+ }
+ DomItem field(DomItem &self, QStringView name);
+ std::shared_ptr<QmlFile> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<QmlFile>(doCopy(self));
+ }
+ void addError(DomItem &self, ErrorMessage msg) override;
+
+ QMultiMap<QString, QmlComponent> components() const { return m_components; }
+ void setComponents(const QMultiMap<QString, QmlComponent> &components)
+ {
+ m_components = components;
+ }
+ Path addComponent(const QmlComponent &component, AddOption option = AddOption::Overwrite,
+ QmlComponent **cPtr = nullptr)
+ {
+ QStringList nameEls = component.name().split(QChar::fromLatin1('.'));
+ QString key = nameEls.mid(1).join(QChar::fromLatin1('.'));
+ return insertUpdatableElementInMultiMap(Path::Field(Fields::components), m_components, key,
+ component, option, cPtr);
+ }
+
+ AST::UiProgram *ast() const
+ {
+ return m_ast; // avoid making it public? would make moving away from it easier
+ }
+ QList<Import> imports() const { return m_imports; }
+ void setImports(const QList<Import> &imports) { m_imports = imports; }
+ Path addImport(const Import &i)
+ {
+ index_type idx = index_type(m_imports.length());
+ m_imports.append(i);
+ m_importScope.addImport(
+ (i.importId.isEmpty() ? QStringList() : i.importId.split(QChar::fromLatin1('.'))),
+ i.importedPath());
+ return Path::Field(Fields::imports).index(idx);
+ }
+ std::shared_ptr<QQmlJS::Engine> engine() const { return m_engine; }
+ RegionComments &comments() { return m_comments; }
+ std::shared_ptr<AstComments> astComments() const { return m_astComments; }
+ void setAstComments(std::shared_ptr<AstComments> comm) { m_astComments = comm; }
+ FileLocations::Tree fileLocationsTree() const { return m_fileLocationsTree; }
+ void setFileLocationsTree(FileLocations::Tree v) { m_fileLocationsTree = v; }
+ QList<Pragma> pragmas() const { return m_pragmas; }
+ void setPragmas(QList<Pragma> pragmas) { m_pragmas = pragmas; }
+ Path addPragma(const Pragma &pragma)
+ {
+ int idx = m_pragmas.length();
+ m_pragmas.append(pragma);
+ return Path::Field(Fields::pragmas).index(idx);
+ }
+
+private:
+ friend class QmlDomAstCreator;
+ std::shared_ptr<Engine> m_engine;
+ AST::UiProgram *m_ast; // avoid? would make moving away from it easier
+ std::shared_ptr<AstComments> m_astComments;
+ RegionComments m_comments;
+ FileLocations::Tree m_fileLocationsTree;
+ QMultiMap<QString, QmlComponent> m_components;
+ QList<Pragma> m_pragmas;
+ QList<Import> m_imports;
+ ImportScope m_importScope;
+};
+
+class QMLDOM_EXPORT QmltypesFile final : public ExternalOwningItem
+{
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ std::shared_ptr<OwningItem> res(new QmltypesFile(*this));
+ return res;
+ }
+
+public:
+ constexpr static DomType kindValue = DomType::QmltypesFile;
+ DomType kind() const override { return kindValue; }
+
+ QmltypesFile(QString filePath = QString(), QString code = QString(),
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0),
+ int derivedFrom = 0)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmltypesFilePath(filePath),
+ derivedFrom, code)
+ {
+ }
+ QmltypesFile(const QmltypesFile &o);
+
+ void ensureInModuleIndex(DomItem &self);
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ std::shared_ptr<QmltypesFile> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<QmltypesFile>(doCopy(self));
+ }
+
+ void addImport(const Import i)
+ { // builder only: not threadsafe...
+ m_imports.append(i);
+ }
+ QList<Import> imports() const { return m_imports; }
+ QMultiMap<QString, QmltypesComponent> components() const { return m_components; }
+ void setComponents(QMultiMap<QString, QmltypesComponent> c) { m_components = std::move(c); }
+ Path addComponent(const QmltypesComponent &comp, AddOption option = AddOption::Overwrite,
+ QmltypesComponent **cPtr = nullptr)
+ {
+ for (const Export &e : comp.exports())
+ addExport(e);
+ return insertUpdatableElementInMultiMap(Path::Field(u"components"), m_components,
+ comp.name(), comp, option, cPtr);
+ }
+ QMultiMap<QString, Export> exports() const { return m_exports; }
+ void setExports(QMultiMap<QString, Export> e) { m_exports = e; }
+ Path addExport(const Export &e)
+ {
+ index_type i = m_exports.values(e.typeName).length();
+ m_exports.insert(e.typeName, e);
+ addUri(e.uri, e.version.majorVersion);
+ return canonicalPath().field(Fields::exports).index(i);
+ }
+
+ QMap<QString, QSet<int>> uris() const { return m_uris; }
+ void addUri(QString uri, int majorVersion)
+ {
+ QSet<int> &v = m_uris[uri];
+ if (!v.contains(majorVersion)) {
+ v.insert(majorVersion);
+ }
+ }
+
+private:
+ QList<Import> m_imports;
+ QMultiMap<QString, QmltypesComponent> m_components;
+ QMultiMap<QString, Export> m_exports;
+ QMap<QString, QSet<int>> m_uris;
+};
+
+class QMLDOM_EXPORT GlobalScope final : public ExternalOwningItem
+{
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override;
+
+public:
+ constexpr static DomType kindValue = DomType::GlobalScope;
+ DomType kind() const override { return kindValue; }
+
+ GlobalScope(QString filePath = QString(),
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0), int derivedFrom = 0)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::globalScopePath(filePath),
+ derivedFrom)
+ {
+ setIsValid(true);
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ std::shared_ptr<GlobalScope> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<GlobalScope>(doCopy(self));
+ }
+ QString name() const { return m_name; }
+ Language language() const { return m_language; }
+ GlobalComponent rootComponent() const { return m_rootComponent; }
+ void setName(QString name) { m_name = name; }
+ void setLanguage(Language language) { m_language = language; }
+ void setRootComponent(const GlobalComponent &ob)
+ {
+ m_rootComponent = ob;
+ m_rootComponent.updatePathFromOwner(Path::Field(Fields::rootComponent));
+ }
+
+private:
+ QString m_name;
+ Language m_language;
+ GlobalComponent m_rootComponent;
+};
+
} // end namespace Dom
} // end namespace QQmlJS
QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomitem.cpp b/src/qmldom/qqmldomitem.cpp
index 581ffcc5ac..272f88a66c 100644
--- a/src/qmldom/qqmldomitem.cpp
+++ b/src/qmldom/qqmldomitem.cpp
@@ -37,6 +37,10 @@
**/
#include "qqmldomitem_p.h"
#include "qqmldomtop_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldomexternalitems_p.h"
+#include "qqmldommock_p.h"
+#include "qqmldomastdumper_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -54,6 +58,8 @@
#include <QtCore/QCborArray>
#include <QtCore/QJsonValue>
#include <QtCore/QJsonDocument>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QtGlobal>
QT_BEGIN_NAMESPACE
@@ -61,6 +67,8 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
+static Q_LOGGING_CATEGORY(refLog, "qt.qmldom.ref", QtWarningMsg);
+
using std::shared_ptr;
/*!
\internal
@@ -82,14 +90,14 @@ entry with its kind to enable casting usng the DomItem::as DomItem::ownerAs temp
The minimal overload set to be usable is:
\code
Kind kind() const override { return kindValue; } // returns the kind of the current element
- Path pathFromOwner(const DomItem &self) const override; // returns the path from the owner to the current element
- Path canonicalPath(const DomItem &self) const override; // returns the path from
- virtual bool iterateDirectSubpaths(const DomItem &self, function_ref<bool(Path, DomItem)>) const = 0; // iterates the *direct* subpaths, returns false if a quick end was requested
-\endcode
-But you probably want to subclass either DomElement of OwningItem for your element.
-DomElement stores its pathFromOwner, and computes the canonicalPath from it and its owner.
-OwningItem is the unit for updates to the Dom model, exposed changes always change at least one OwningItem.
-They have their lifetime handled with shared_ptr and own (i.e. are responsible of freeing) other items in them.
+ Path pathFromOwner(DomItem &self) const override; // returns the path from the owner to the
+current element Path canonicalPath(DomItem &self) const override; // returns the path from virtual
+bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem)>) const = 0; // iterates
+the *direct* subpaths, returns false if a quick end was requested \endcode But you probably want to
+subclass either DomElement of OwningItem for your element. DomElement stores its pathFromOwner, and
+computes the canonicalPath from it and its owner. OwningItem is the unit for updates to the Dom
+model, exposed changes always change at least one OwningItem. They have their lifetime handled with
+shared_ptr and own (i.e. are responsible of freeing) other items in them.
\sa QQml::Dom::DomItem, QQml::Dom::DomElement, QQml::Dom::OwningItem
*/
@@ -109,80 +117,31 @@ QMap<DomType,QString> domTypeToStringMap()
QString domTypeToString(DomType k)
{
- return domTypeToStringMap().value(k, QString::number(int(k)));
+ QString res = domTypeToStringMap().value(k);
+ if (res.isEmpty())
+ return QString::number(int(k));
+ else
+ return res;
}
-bool domTypeIsObjWrap(DomType k)
+QMap<DomKind, QString> domKindToStringMap()
{
- switch (k) {
- case DomType::ModuleAutoExport:
- case DomType::Import:
- case DomType::Export:
- case DomType::SimpleObjectWrap:
- case DomType::Version:
- case DomType::ErrorMessage:
- case DomType::PropertyDefinition:
- case DomType::MethodParameter:
- case DomType::MethodInfo:
- case DomType::Pragma:
- case DomType::Id:
- case DomType::EnumItem:
- case DomType::Binding:
- case DomType::RequiredProperty:
- case DomType::FileLocations:
- return true;
- default:
- return false;
- }
+ static QMap<DomKind, QString> map = []() {
+ QMetaEnum metaEnum = QMetaEnum::fromType<DomKind>();
+ QMap<DomKind, QString> res;
+ for (int i = 0; i < metaEnum.keyCount(); ++i) {
+ res[DomKind(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
+ }
+ return res;
+ }();
+ return map;
}
-bool domTypeIsDomElement(DomType k)
+QString domKindToString(DomKind k)
{
- switch (k) {
- case DomType::ModuleScope:
- case DomType::QmlObject:
- case DomType::ConstantData:
- case DomType::SimpleObjectWrap:
- case DomType::ScriptExpression:
- case DomType::Reference:
- case DomType::Map:
- case DomType::List:
- case DomType::EnumDecl:
- case DomType::JsResource:
- case DomType::QmltypesComponent:
- case DomType::QmlComponent:
- case DomType::GlobalComponent:
- case DomType::GenericObject:
- return true;
- default:
- return false;
- }
+ return domKindToStringMap().value(k, QString::number(int(k)));
}
-bool domTypeIsOwningItem(DomType k)
-{
- switch (k) {
- case DomType::ModuleIndex:
-
- case DomType::GenericOwner:
-
- case DomType::QmlDirectory:
- case DomType::JsFile:
- case DomType::QmlFile:
- case DomType::QmltypesFile:
- case DomType::GlobalScope:
-
- case DomType::LoadInfo:
- case DomType::AttachedInfo:
-
- case DomType::DomEnvironment:
- case DomType::DomUniverse:
- return true;
- default:
- return false;
- }
-};
-
bool domTypeIsExternalItem(DomType k)
{
switch (k) {
@@ -214,26 +173,30 @@ bool domTypeIsContainer(DomType k)
switch (k) {
case DomType::Map:
case DomType::List:
+ case DomType::ListP:
return true;
default:
return false;
}
}
-bool domTypeCanBeInline(DomType k)
+bool domTypeIsScope(DomType k)
{
switch (k) {
- case DomType::Empty:
- case DomType::Map:
- case DomType::List:
- case DomType::ConstantData:
- case DomType::SimpleObjectWrap:
- case DomType::Reference:
+ case DomType::QmlObject: // prop, methods,...
+ case DomType::ScriptExpression: // Js lexical scope
+ case DomType::QmlComponent: // (ids, enums -> qmlObj)
+ case DomType::QmlFile: // (components ->importScope)
+ case DomType::MethodInfo: // method arguments
+ case DomType::ImportScope: // (types, qualifiedImports)
+ case DomType::GlobalComponent: // global scope (enums -> qmlObj)
+ case DomType::JsResource: // js resurce (enums -> qmlObj)
+ case DomType::QmltypesComponent: // qmltypes component (enums -> qmlObj)
return true;
default:
return false;
}
-};
+}
QCborValue locationToData(SourceLocation loc, QStringView strValue)
{
@@ -248,118 +211,7 @@ QCborValue locationToData(SourceLocation loc, QStringView strValue)
return res;
}
-DomKind DomBase::domKind() const
-{
- return kind2domKind(kind());
-}
-
-bool DomBase::iterateDirectSubpathsConst(const DomItem &self, function_ref<bool (Path, const DomItem &)>visitor) const
-{
- return const_cast<DomBase *>(this)->iterateDirectSubpaths(
- *const_cast<DomItem *>(&self),
- [visitor](Path p, DomItem &item) {
- return visitor(p, item);
- });
-}
-
-DomItem DomBase::containingObject(const DomItem &self) const
-{
- Path path = pathFromOwner(self);
- DomItem base = self.owner();
- if (!path) {
- path = canonicalPath(self);
- base = self;
- }
- Source source = path.split();
- return base.path(source.pathToSource);
-}
-
-quintptr DomBase::id() const
-{
- return quintptr(this);
-}
-
-QString DomBase::typeName() const
-{
- return domTypeToStringMap()[kind()];
-}
-
-QList<QString> const DomBase::fields(const DomItem &self) const
-{
- QList<QString> res;
- iterateDirectSubpathsConst(self, [&res](Path p, const DomItem &){
- if (p.headKind() == Path::Kind::Field)
- res.append(p.headName());
- return true;
- });
- return res;
-}
-
-DomItem DomBase::field(const DomItem &self, QStringView name) const
-{
- DomItem res;
- iterateDirectSubpathsConst(self, [&res, name](Path p, const DomItem & i){
- if (p.headKind() == Path::Kind::Field && p.checkHeadName(name)) {
- res = i;
- return false;
- }
- return true;
- });
- return res;
-}
-
-index_type DomBase::indexes(const DomItem &self) const
-{
- index_type res = 0;
- iterateDirectSubpathsConst(self, [&res](Path p, const DomItem &){
- if (p.headKind() == Path::Kind::Index) {
- index_type i = p.headIndex() + 1;
- if (res < i)
- res = i;
- }
- return true;
- });
- return res;
-}
-
-DomItem DomBase::index(const DomItem &self, qint64 index) const
-{
- DomItem res;
- iterateDirectSubpathsConst(self, [&res, index](Path p, const DomItem &i){
- if (p.headKind() == Path::Kind::Index && p.headIndex() == index) {
- res = i;
- return false;
- }
- return true;
- });
- return res;
-}
-
-QSet<QString> const DomBase::keys(const DomItem &self) const
-{
- QSet<QString> res;
- iterateDirectSubpathsConst(self, [&res](Path p, const DomItem &){
- if (p.headKind() == Path::Kind::Key)
- res.insert(p.headName());
- return true;
- });
- return res;
-}
-
-DomItem DomBase::key(const DomItem &self, QString name) const
-{
- DomItem res;
- iterateDirectSubpathsConst(self, [&res, name](Path p, const DomItem &i){
- if (p.headKind() == Path::Kind::Key && p.checkHeadName(name)) {
- res = i;
- return false;
- }
- return true;
- });
- return res;
-}
-
-QString DomBase::canonicalFilePath(const DomItem & self) const
+QString DomBase::canonicalFilePath(DomItem &self) const
{
auto parent = containingObject(self);
if (parent)
@@ -367,36 +219,37 @@ QString DomBase::canonicalFilePath(const DomItem & self) const
return QString();
}
-SourceLocation DomBase::location(const DomItem & self) const
-{
- auto parent = containingObject(self);
- if (parent)
- return parent.location();
- return SourceLocation();
-}
-
-ConstantData::ConstantData(Path pathFromOwner, QCborValue value, Options options, const SourceLocation & loc):
- DomElement(pathFromOwner, loc), m_value(value), m_options(options)
+ConstantData::ConstantData(Path pathFromOwner, QCborValue value, Options options)
+ : DomElement(pathFromOwner), m_value(value), m_options(options)
{}
-bool ConstantData::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool ConstantData::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
+ static QHash<QString, QString> knownFields;
+ static QBasicMutex m;
+ auto toField = [](QString f) -> QStringView {
+ QMutexLocker l(&m);
+ if (!knownFields.contains(f))
+ knownFields[f] = f;
+ return knownFields[f];
+ };
if (m_value.isMap()) {
QCborMap map = m_value.toMap();
auto it = map.cbegin();
auto end = map.cend();
while (it != end) {
QString key = it.key().toString();
- Path path;
+ PathEls::PathComponent comp;
switch (m_options) {
case ConstantData::Options::MapIsMap:
- path = Path::Key(key);
+ comp = PathEls::Key(key);
break;
case ConstantData::Options::FirstMapIsFields:
- path = Path::Field(key);
+ comp = PathEls::Field(toField(key));
break;
}
- if (!self.subDataPath(path, it.value()).visit(visitor))
+ auto val = it.value();
+ if (!self.dvValue(visitor, comp, val))
return false;
++it;
}
@@ -407,7 +260,7 @@ bool ConstantData::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path,
auto end = array.cend();
index_type i = 0;
while (it != end) {
- if (!self.subDataPath(Path::Index(i++), *it++).visit(visitor))
+ if (!self.dvValue(visitor, PathEls::Index(i++), *it++))
return false;
}
return true;
@@ -436,26 +289,6 @@ DomKind ConstantData::domKind() const
return DomKind::Value;
}
-SimpleObjectWrap::SimpleObjectWrap(Path pathFromOwner, QVariant value,
- std::function<bool(DomItem &, QVariant, function_ref<bool(Path, DomItem &)>)> directSubpathsIterate,
- DomType kind,
- DomKind domKind,
- QString typeName,
- const SourceLocation & loc):
- DomElement(pathFromOwner, loc), m_kind(kind), m_domKind(domKind), m_typeName(typeName), m_value(value),
- m_directSubpathsIterate(directSubpathsIterate)
-{}
-
-bool SimpleObjectWrap::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
-{
- return m_directSubpathsIterate(self, m_value, visitor);
-}
-
-quintptr SimpleObjectWrap::id() const
-{
- return quintptr(m_value.value<void *>());
-}
-
/*!
\internal
\class QQmlJS::Dom::DomItem
@@ -472,15 +305,14 @@ and to the DomEnvironment or DomUniverse that contains them. This means that:
containingObject() and container().
\li the indexing operator [], or the path(), field(), key() and index() methods (along with their
fields(), keys(), and indexes() contreparts) let one visit the contents of the current element.
-\li visitChildren can be used to visit all subEments, if preferred on the top of it a visitor
+\li visitTree can be used to visit all subEments, if preferred on the top of it a visitor
pattern can also be used.
-\li If element specific attributes are wanted the two template casting as and ownerAs allow safe casting
-of the DomItem to a specific concrete type (cast to superclasses is not supported).
-\li Multithreading does not create issues, because even if an update replacing an OwningItem takes
- place the DomItem keeps a shared_ptr to the current owner as long as you use it
-\li Some elements (Empty, List, Map, ConstantData, Reference) might be inline, meaning that they are
- generated on the fly, wrapping data of the original object.
-\endlist
+\li If element specific attributes are wanted the two template casting as and ownerAs allow safe
+casting of the DomItem to a specific concrete type (cast to superclasses is not supported). \li
+Multithreading does not create issues, because even if an update replacing an OwningItem takes place
+the DomItem keeps a shared_ptr to the current owner as long as you use it \li Some elements (Empty,
+List, Map, ConstantData, Reference) might be inline, meaning that they are generated on the fly,
+wrapping data of the original object. \endlist
One of the goals of the DomItem is to allow one to use real typed objects, as one is used to in C++,
and also let one use modern C++ patterns, meaning container that contain the actual object (without
@@ -494,6 +326,7 @@ it every time it needs.
*/
ErrorGroup DomItem::domErrorGroup = NewErrorGroup("Dom");
+DomItem DomItem::empty = DomItem();
ErrorGroups DomItem::myErrors()
{
@@ -507,20 +340,35 @@ ErrorGroups DomItem::myResolveErrors()
return res;
}
-Path DomItem::canonicalPath() const
+Path DomItem::canonicalPath()
{
- Path res = base()->canonicalPath(*this);
- Q_ASSERT((!res || res.headKind() == Path::Kind::Root) && "non anchored canonical path");
+ Path res = visitEl([this](auto &&el) { return el->canonicalPath(*this); });
+ if (!(!res || res.headKind() == Path::Kind::Root)) {
+ qCWarning(domLog) << "non anchored canonical path:" << res.toString();
+ Q_ASSERT(false);
+ }
return res;
}
-DomItem DomItem::containingObject() const
+DomItem DomItem::containingObject()
{
- return base()->containingObject(*this);
+ return visitEl([this](auto &&el) { return el->containingObject(*this); });
}
-DomItem DomItem::fileObject(GoTo options) const
+DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions)
+{
+ if (DomItem res = filterUp([](DomType k, DomItem &) { return k == DomType::QmlObject; },
+ filterOptions))
+ return res;
+ if (options == GoTo::MostLikely) {
+ if (DomItem comp = component(options))
+ return comp.field(Fields::objects).index(0);
+ }
+ return DomItem();
+}
+
+DomItem DomItem::fileObject(GoTo options)
{
DomItem res = *this;
DomType k = res.internalKind();
@@ -530,17 +378,32 @@ DomItem DomItem::fileObject(GoTo options) const
}
if (k == DomType::ExternalItemInfo || (options == GoTo::MostLikely && k == DomType::ExternalItemPair))
return field(Fields::currentItem);
- while (res) {
+ res = owner();
+ k = res.internalKind();
+ while (k != DomType::Empty) {
if (k == DomType::QmlFile || k == DomType::QmldirFile || k == DomType::QmltypesFile
|| k == DomType::JsFile)
break;
res = res.containingObject();
+ res = res.owner();
k = res.internalKind();
}
return res;
}
-DomItem DomItem::container() const
+DomItem DomItem::rootQmlObject(GoTo options)
+{
+ if (DomItem res = filterUp([](DomType k, DomItem &) { return k == DomType::QmlObject; },
+ FilterUpOptions::ReturnInner))
+ return res;
+ if (options == GoTo::MostLikely) {
+ if (DomItem comp = component(options))
+ return comp.field(Fields::objects).index(0);
+ }
+ return DomItem();
+}
+
+DomItem DomItem::container()
{
Path path = pathFromOwner();
if (!path)
@@ -551,16 +414,35 @@ DomItem DomItem::container() const
return containingObject();
}
-DomItem DomItem::owner() const {
- return DomItem(m_top, m_owner, m_ownerPath, m_owner.get());
+DomItem DomItem::globalScope()
+{
+ if (internalKind() == DomType::GlobalScope)
+ return *this;
+ DomItem env = environment();
+ if (shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
+ return env.copy(envPtr->ensureGlobalScopeWithName(env, envPtr->globalScopeName())->current,
+ Path());
+ }
+ return DomItem();
}
-DomItem DomItem::top() const
+DomItem DomItem::owner()
{
- return DomItem(m_top, m_top, m_ownerPath, m_top.get());
+ if (domTypeIsOwningItem(m_kind) || m_kind == DomType::Empty)
+ return *this;
+ return std::visit(
+ [this](auto &&el) { return DomItem(this->m_top, el, this->m_ownerPath, el.get()); },
+ *m_owner);
+}
+
+DomItem DomItem::top()
+{
+ if (domTypeIsTopItem(m_kind) || m_kind == DomType::Empty)
+ return *this;
+ return std::visit([](auto &&el) { return DomItem(el, el, Path(), el.get()); }, *m_top);
}
-DomItem DomItem::environment() const
+DomItem DomItem::environment()
{
DomItem res = top();
if (res.internalKind() == DomType::DomEnvironment)
@@ -568,7 +450,7 @@ DomItem DomItem::environment() const
return DomItem(); // we are in the universe, and cannot go back to the environment...
}
-DomItem DomItem::universe() const
+DomItem DomItem::universe()
{
DomItem res = top();
if (res.internalKind() == DomType::DomUniverse)
@@ -578,29 +460,139 @@ DomItem DomItem::universe() const
return DomItem(); // we should be in an empty DomItem already...
}
-QString DomItem::name() const
+DomItem DomItem::filterUp(function_ref<bool(DomType k, DomItem &)> filter, FilterUpOptions options)
+{
+ DomItem it = *this;
+ DomType k = it.internalKind();
+ switch (options) {
+ case FilterUpOptions::ReturnOuter:
+ case FilterUpOptions::ReturnOuterNoSelf: {
+ bool checkTop = (options == FilterUpOptions::ReturnOuter);
+ while (k != DomType::Empty) {
+ if (checkTop && filter(k, it))
+ return it;
+ checkTop = true;
+ if (!domTypeIsOwningItem(k)) {
+ DomItem el = owner();
+ DomItem res;
+ k = DomType::Empty;
+ Path pp = pathFromOwner();
+ for (Path p : pp.mid(0, pp.length() - 1)) {
+ el = el.path(p);
+ DomType k2 = el.internalKind();
+ if (filter(k2, el)) {
+ k = k2;
+ res = el;
+ }
+ }
+ if (k != DomType::Empty)
+ return res;
+ it = it.owner();
+ }
+ it = it.containingObject();
+ k = it.internalKind();
+ }
+ } break;
+ case FilterUpOptions::ReturnInner:
+ while (k != DomType::Empty) {
+ if (!domTypeIsOwningItem(k)) {
+ DomItem el = owner();
+ Path pp = pathFromOwner();
+ for (Path p : pp) {
+ DomItem child = el.path(p);
+ DomType k2 = child.internalKind();
+ if (filter(k2, child))
+ return child;
+ el = child;
+ }
+ it = it.owner();
+ }
+ it = it.containingObject();
+ k = it.internalKind();
+ }
+ break;
+ }
+ return DomItem();
+}
+
+DomItem DomItem::scope(FilterUpOptions options)
{
- return field(Fields::name).value().toString();
+ DomItem res = filterUp([](DomType, DomItem &el) { return el.isScope(); }, options);
+ return res;
}
-DomItem DomItem::qmlChildren() const
+DomItem DomItem::get(ErrorHandler h, QList<Path> *visitedRefs)
{
- return field(Fields::children);
+ if (const Reference *refPtr = as<Reference>())
+ return refPtr->get(*this, h, visitedRefs);
+ return DomItem();
}
-DomItem DomItem::annotations() const
+QList<DomItem> DomItem::getAll(ErrorHandler h, QList<Path> *visitedRefs)
{
- return field(Fields::annotations);
+ if (const Reference *refPtr = as<Reference>())
+ return refPtr->getAll(*this, h, visitedRefs);
+ return {};
}
-DomItem DomItem::component() const
+PropertyInfo DomItem::propertyInfoWithName(QString name)
{
- DomItem item = *this;
- while (item) {
- DomType kind = item.internalKind() ;
- if (kind == DomType::QmlComponent || kind == DomType::QmltypesComponent || kind == DomType::GlobalComponent)
- return item;
- item = item.containingObject();
+ PropertyInfo pInfo;
+ visitPrototypeChain([&pInfo, name](DomItem &obj) {
+ return obj.visitLocalSymbolsNamed(name, [&pInfo, name](DomItem &el) {
+ switch (el.internalKind()) {
+ case DomType::Binding:
+ pInfo.bindings.append(el);
+ break;
+ case DomType::PropertyDefinition:
+ pInfo.propertyDefs.append(el);
+ break;
+ default:
+ break;
+ }
+ return true;
+ });
+ });
+ return pInfo;
+}
+
+QSet<QString> DomItem::propertyInfoNames()
+{
+ QSet<QString> res;
+ visitPrototypeChain([&res](DomItem &obj) {
+ res += obj.propertyDefs().keys();
+ res += obj.bindings().keys();
+ return true;
+ });
+ return res;
+}
+
+DomItem DomItem::component(GoTo options)
+{
+ if (DomItem res = filterUp(
+ [](DomType kind, DomItem &) {
+ return kind == DomType::QmlComponent || kind == DomType::QmltypesComponent
+ || kind == DomType::GlobalComponent;
+ },
+ FilterUpOptions::ReturnInner))
+ return res;
+ if (options == GoTo::MostLikely) {
+ DomItem item = *this;
+ DomType kind = item.internalKind();
+ if (kind == DomType::List || kind == DomType::Map) {
+ item = item.containingObject();
+ kind = item.internalKind();
+ }
+ switch (kind) {
+ case DomType::ExternalItemPair:
+ case DomType::ExternalItemInfo:
+ item = fileObject(options);
+ Q_FALLTHROUGH();
+ case DomType::QmlFile:
+ return item.field(Fields::components).key(QString()).index(0);
+ default:
+ break;
+ }
}
return DomItem();
}
@@ -610,12 +602,21 @@ struct ResolveToDo {
int pathIndex;
};
-bool DomItem::resolve(Path path,
- DomItem::Visitor visitor,
- ErrorHandler errorHandler,
- ResolveOptions options,
- Path fullPath,
- QList<Path> *visitedRefs) const
+static QMap<LookupType, QString> lookupTypeToStringMap()
+{
+ static QMap<LookupType, QString> map = []() {
+ QMetaEnum metaEnum = QMetaEnum::fromType<LookupType>();
+ QMap<LookupType, QString> res;
+ for (int i = 0; i < metaEnum.keyCount(); ++i) {
+ res[LookupType(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
+ }
+ return res;
+ }();
+ return map;
+}
+
+bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHandler,
+ ResolveOptions options, Path fullPath, QList<Path> *visitedRefs)
{
QList<Path> vRefs;
Path fPath = fullPath;
@@ -623,7 +624,7 @@ bool DomItem::resolve(Path path,
fPath = path;
if (path.length()==0)
return visitor(fPath, *this);
- QSet<QPair<quintptr,int> > visited;
+ QList<QSet<quintptr>> visited(path.length() + 1);
Path myPath = path;
QVector<ResolveToDo> toDos(1); // invariant: always increase pathIndex to guarantee end even with only partial visited match
if (path.headKind() == Path::Kind::Root) {
@@ -660,24 +661,24 @@ bool DomItem::resolve(Path path,
auto toDo = toDos.last();
toDos.removeLast();
{
- auto idNow = toDo.item.base()->id();
+ auto idNow = toDo.item.id();
if (idNow == quintptr(0) && toDo.item == *this)
- idNow = quintptr(base());
- if (idNow != quintptr(0) && visited.contains(qMakePair(idNow,0)))
+ idNow = quintptr(this);
+ if (idNow != quintptr(0) && visited[0].contains(idNow))
continue;
}
int iPath = toDo.pathIndex;
DomItem it = toDo.item;
bool branchExhausted = false;
while (iPath < path.length() && it && !branchExhausted) {
- auto idNow = it.base()->id();
+ auto idNow = it.id();
if (idNow == quintptr() && toDo.item == *this)
- idNow = quintptr(base());
+ idNow = quintptr(this);
if (idNow != quintptr(0)) {
auto vPair = qMakePair(idNow, iPath);
- if (visited.contains(vPair))
+ if (visited[vPair.second].contains(vPair.first))
break;
- visited.insert(vPair);
+ visited[vPair.second].insert(vPair.first);
}
if (options & ResolveOption::TraceVisit && !visitor(path.mid(0,iPath), it))
return false;
@@ -689,28 +690,33 @@ bool DomItem::resolve(Path path,
case Path::Kind::Field:
if (cNow.checkHeadName(Fields::get) && it.internalKind() == DomType::Reference) {
Path toResolve = it.as<Reference>()->referredObjectPath;
+ Path refRef = it.canonicalPath();
if (visitedRefs == nullptr) {
visitedRefs = &vRefs;
- visitedRefs->append(fPath);
}
- if (visitedRefs->contains(toResolve)) {
- myResolveErrors().error([visitedRefs, toResolve](Sink sink) {
- sink(tr("Circular reference:"));
- sink(u"\n");
- for (const Path &vPath : *visitedRefs) {
- sink(u" ");
- vPath.dump(sink);
- sink(u" >\n");
- }
- toResolve.dump(sink);
- }).handle(errorHandler);
+ if (visitedRefs->contains(refRef)) {
+ myResolveErrors()
+ .error([visitedRefs, refRef](Sink sink) {
+ sink(tr("Circular reference:\n"));
+ for (const Path &vPath : *visitedRefs) {
+ sink(u" ");
+ vPath.dump(sink);
+ sink(u" >\n");
+ }
+ refRef.dump(sink);
+ })
+ .handle(errorHandler);
it = DomItem();
} else {
+ visitedRefs->append(refRef);
DomItem resolveRes;
- it.resolve(toResolve, [&resolveRes](Path, const DomItem &r) {
- resolveRes = r;
- return false;
- }, errorHandler, ResolveOption::None, toResolve, visitedRefs);
+ it.resolve(
+ toResolve,
+ [&resolveRes](Path, DomItem &r) {
+ resolveRes = r;
+ return false;
+ },
+ errorHandler, ResolveOption::None, toResolve, visitedRefs);
it = resolveRes;
}
} else {
@@ -741,13 +747,17 @@ bool DomItem::resolve(Path path,
return false;
}
if (!branchExhausted)
- visitChildren(Path(),[toFind, &toDos, iPath](Path, const DomItem &item, bool) {
- // avoid non directly attached?
- DomItem newItem = item[toFind];
- if (newItem)
- toDos.append({newItem, iPath});
- return true;
- }, VisitOption::Recurse | VisitOption::VisitAdopted | VisitOption::NoPath);
+ visitTree(
+ Path(),
+ [toFind, &toDos, iPath](Path, DomItem &item, bool) {
+ // avoid non directly attached?
+ DomItem newItem = item[toFind];
+ if (newItem)
+ toDos.append({ newItem, iPath });
+ return true;
+ },
+ VisitOption::VisitSelf | VisitOption::Recurse
+ | VisitOption::VisitAdopted | VisitOption::NoPath);
branchExhausted = true;
break;
}
@@ -765,64 +775,171 @@ bool DomItem::resolve(Path path,
if (domKind() != DomKind::Object)
it = it.containingObject();
break;
- case PathCurrent::ObjChain:
-
- case PathCurrent::ScopeChain:
+ case PathCurrent::ObjChain: {
+ bool cont = it.visitPrototypeChain(
+ [&toDos, iPath](DomItem &subEl) {
+ toDos.append({ subEl, iPath });
+ return true;
+ },
+ VisitPrototypesOption::Normal, errorHandler, nullptr,
+ visitedRefs); // avoid passing visitedRefs?
+ if (!cont)
+ return false;
+ branchExhausted = true;
+ break;
+ }
+ case PathCurrent::ScopeChain: {
+ bool cont = it.visitScopeChain(
+ [&toDos, iPath](DomItem &subEl) {
+ toDos.append({ subEl, iPath });
+ return true;
+ },
+ LookupOption::Normal, errorHandler);
+ if (!cont)
+ return false;
+ branchExhausted = true;
+ break;
+ }
case PathCurrent::Component:
it = it.component();
break;
case PathCurrent::Module:
case PathCurrent::Ids:
- it = it.component().field(Fields::ids);
+ it = it.component().ids();
break;
case PathCurrent::Types:
it = it.component()[Fields::exports];
break;
case PathCurrent::LookupStrict:
case PathCurrent::LookupDynamic:
- case PathCurrent::Lookup:
+ case PathCurrent::Lookup: {
+ LookupOptions opt = LookupOption::Normal;
if (current == PathCurrent::Lookup) {
- DomItem strict = it.component().field(u"~strictLookup~");
- if (!strict)
- strict = it.environment().field(u"defaultStrictLookup");
+ DomItem comp = it.component();
+ DomItem strict = comp.field(u"~strictLookup~");
+ if (!strict) {
+ DomItem env = it.environment();
+ strict = env.field(u"defaultStrictLookup");
+ }
if (strict && strict.value().toBool())
- current = PathCurrent::LookupStrict;
- else
- current = PathCurrent::LookupDynamic;
+ opt = opt | LookupOption::Strict;
+ } else if (current == PathCurrent::LookupStrict) {
+ opt = opt | LookupOption::Strict;
}
- if (current == PathCurrent::LookupStrict) {
- myResolveErrors().error(tr("@lookupStrict unimplemented"))
+ if (it.internalKind() == DomType::ScriptExpression) {
+ myResolveErrors()
+ .error(tr("Javascript lookups not yet implemented"))
.handle(errorHandler);
return false;
- } else if (current == PathCurrent::LookupDynamic) {
- // expand, add self, prototype, components, prototype, global
- DomItem proto = it.component()[u"~prototype~"];
- if (proto) {
- DomItem pVal = proto[u"get"];
- if (!pVal) {
- myResolveErrors().warning(tr("Could not find prototype %1").arg(proto.toString()))
- .handle(errorHandler);
- } else {
- toDos.append({proto, iPath - 1});
+ }
+ // enter lookup
+ auto idNow = it.id();
+ if (idNow == quintptr(0) && toDo.item == *this)
+ idNow = quintptr(this);
+ if (idNow != quintptr(0)) {
+ auto vPair = qMakePair(idNow, iPath);
+ if (visited[vPair.second].contains(vPair.first))
+ break;
+ visited[vPair.second].insert(vPair.first);
+ }
+ if (options & ResolveOption::TraceVisit && !visitor(path.mid(0, iPath), it))
+ return false;
+ if (iPath + 1 >= path.length()) {
+ myResolveErrors()
+ .error(tr("Premature end of path, expected a field specifying the "
+ "type, and a key specifying the name to search after a "
+ "lookup directive in %2")
+ .arg(path.toString()))
+ .handle(errorHandler);
+ return false;
+ }
+ Path cNow = path[iPath++];
+ if (cNow.headKind() != Path::Kind::Field) {
+ myResolveErrors()
+ .error(tr("Expected a key path specifying the type to search after "
+ "a lookup directive, not %1 at component %2 of %3")
+ .arg(cNow.toString())
+ .arg(iPath)
+ .arg(path.toString()))
+ .handle(errorHandler);
+ return false;
+ }
+ QString expectedType = cNow.headName();
+ LookupType lookupType = LookupType::Symbol;
+ {
+ bool found = false;
+ auto m = lookupTypeToStringMap();
+ auto it = m.begin();
+ auto end = m.end();
+ while (it != end) {
+ if (it.value().compare(expectedType, Qt::CaseInsensitive) == 0) {
+ lookupType = it.key();
+ found = true;
+ }
+ ++it;
+ }
+ if (!found) {
+ QString types;
+ it = lookupTypeToStringMap().begin();
+ while (it != end) {
+ if (!types.isEmpty())
+ types += QLatin1String("', '");
+ types += it.value();
+ ++it;
}
+ myResolveErrors()
+ .error(tr("Type for lookup was expected to be one of '%1', not "
+ "%2")
+ .arg(types, expectedType))
+ .handle(errorHandler);
+ return false;
}
- myResolveErrors().error(tr("@lookupDynamic unimplemented"))
+ }
+ cNow = path[iPath++];
+ if (cNow.headKind() != Path::Kind::Key) {
+ myResolveErrors()
+ .error(tr("Expected a key specifying the path to search after the "
+ "@lookup directive and type, not %1 at component %2 of "
+ "%3")
+ .arg(cNow.toString())
+ .arg(iPath)
+ .arg(path.toString()))
.handle(errorHandler);
return false;
- } else {
- myResolveErrors().error(tr("Unexpected Current path component %1").arg(cNow.headName()))
+ }
+ QString target = cNow.headName();
+ QStringList subpath;
+ if (target.isEmpty()) {
+ myResolveErrors()
+ .warning(tr("Path with empty lookup at component %1 of %2 will "
+ "match nothing in %3.")
+ .arg(iPath)
+ .arg(path.toString())
+ .arg(it.canonicalPath().toString()))
.handle(errorHandler);
- return false;
+ return true;
}
+ it.visitLookup(
+ target,
+ [&toDos, iPath](DomItem &subEl) {
+ toDos.append({ subEl, iPath });
+ return true;
+ },
+ lookupType, opt, errorHandler, &(visited[iPath]), visitedRefs);
+ branchExhausted = true;
break;
}
+ }
break;
}
case Path::Kind::Any:
- visitChildren(Path(), [&toDos, iPath](Path, const DomItem &item, bool) {
- toDos.append({item, iPath});
- return true;
- }, VisitOption::VisitAdopted);
+ visitTree(
+ Path(),
+ [&toDos, iPath](Path, DomItem &item, bool) {
+ toDos.append({ item, iPath });
+ return true;
+ },
+ VisitOption::VisitSelf | VisitOption::Recurse | VisitOption::VisitAdopted);
branchExhausted = true;
break;
case Path::Kind::Filter:
@@ -838,7 +955,7 @@ bool DomItem::resolve(Path path,
return true;
}
-DomItem DomItem::path(Path p, ErrorHandler errorHandler) const
+DomItem DomItem::path(Path p, ErrorHandler errorHandler)
{
if (!p)
return *this;
@@ -850,433 +967,1211 @@ DomItem DomItem::path(Path p, ErrorHandler errorHandler) const
return res;
}
-DomItem DomItem::path(QString p, ErrorHandler errorHandler) const
+DomItem DomItem::path(QString p, ErrorHandler errorHandler)
{
return path(Path::fromString(p, errorHandler));
}
-DomItem DomItem::path(QStringView p, ErrorHandler errorHandler) const
+DomItem DomItem::path(QStringView p, ErrorHandler errorHandler)
{
return path(Path::fromString(p, errorHandler));
}
-QList<QString> const DomItem::fields() const
+QList<QString> DomItem::fields()
+{
+ return visitEl([this](auto &&el) { return el->fields(*this); });
+}
+
+DomItem DomItem::field(QStringView name)
+{
+ return visitEl([this, name](auto &&el) { return el->field(*this, name); });
+}
+
+index_type DomItem::indexes()
+{
+ return visitEl([this](auto &&el) { return el->indexes(*this); });
+}
+
+DomItem DomItem::index(index_type i)
+{
+ return visitEl([this, i](auto &&el) { return el->index(*this, i); });
+}
+
+bool DomItem::visitIndexes(function_ref<bool(DomItem &)> visitor)
+{
+ // use iterateDirectSubpathsConst instead?
+ int nIndexes = indexes();
+ for (int i = 0; i < nIndexes; ++i) {
+ DomItem v = index(i);
+ if (!visitor(v))
+ return false;
+ }
+ return true;
+}
+
+QSet<QString> DomItem::keys()
+{
+ return visitEl([this](auto &&el) { return el->keys(*this); });
+}
+
+QStringList DomItem::sortedKeys()
{
- return base()->fields(*this);
+ QSet<QString> ks = keys();
+ QStringList sortedKs(ks.begin(), ks.end());
+ std::sort(sortedKs.begin(), sortedKs.end());
+ return sortedKs;
}
-DomItem DomItem::field(QStringView name) const
+DomItem DomItem::key(QString name)
{
- return base()->field(*this, name);
+ return visitEl([this, name](auto &&el) { return el->key(*this, name); });
}
-index_type DomItem::indexes() const
+bool DomItem::visitKeys(function_ref<bool(QString, DomItem &)> visitor)
{
- return base()->indexes(*this);
+ // use iterateDirectSubpathsConst instead?
+ for (auto k : sortedKeys()) {
+ DomItem v = key(k);
+ if (!visitor(k, v))
+ return false;
+ }
+ return true;
}
-DomItem DomItem::index(index_type i) const
+QList<DomItem> DomItem::values()
{
- return base()->index(*this, i);
+ QList<DomItem> res;
+ visitEl([this, &res](auto &&el) {
+ return el->iterateDirectSubpathsConst(
+ *this, [&res](const PathEls::PathComponent &, function_ref<DomItem()> item) {
+ res.append(item());
+ return true;
+ });
+ });
+ return res;
}
-QSet<QString> const DomItem::keys() const
+bool DomItem::isCanonicalChild(DomItem &item)
{
- return base()->keys(*this);
+ if (item.isOwningItem()) {
+ return canonicalPath() == item.canonicalPath().dropTail();
+ } else {
+ return item.owner() == owner() && item.pathFromOwner().dropTail() == pathFromOwner();
+ }
}
-DomItem DomItem::key(QString name) const
+bool DomItem::hasAnnotations()
{
- return base()->key(*this, name);
+ bool hasAnnotations = false;
+ DomType iKind = internalKind();
+ switch (iKind) {
+ case DomType::Id:
+ if (const Id *myPtr = as<Id>())
+ hasAnnotations = !myPtr->annotations.isEmpty();
+ break;
+ case DomType::PropertyDefinition:
+ if (const PropertyDefinition *myPtr = as<PropertyDefinition>())
+ hasAnnotations = !myPtr->annotations.isEmpty();
+ break;
+ case DomType::MethodInfo:
+ if (const MethodInfo *myPtr = as<MethodInfo>())
+ hasAnnotations = !myPtr->annotations.isEmpty();
+ break;
+ case DomType::QmlObject:
+ if (const QmlObject *myPtr = as<QmlObject>())
+ hasAnnotations = !myPtr->annotations().isEmpty();
+ break;
+ case DomType::Binding:
+ if (const Binding *myPtr = as<Binding>())
+ hasAnnotations = !myPtr->annotations().isEmpty();
+ break;
+ default:
+ break;
+ }
+ return hasAnnotations;
}
-bool DomItem::visitChildren(
- Path basePath,
- DomItem::ChildrenVisitor visitor,
- VisitOptions options,
- DomItem::ChildrenVisitor openingVisitor,
- DomItem::ChildrenVisitor closingVisitor) const
+bool DomItem::visitTree(Path basePath, DomItem::ChildrenVisitor visitor, VisitOptions options,
+ DomItem::ChildrenVisitor openingVisitor,
+ DomItem::ChildrenVisitor closingVisitor)
{
if (!*this)
return true;
- if (visitor && ! visitor(basePath, *this, true))
+ if (options & VisitOption::VisitSelf && !visitor(basePath, *this, true))
return false;
- if (openingVisitor && !openingVisitor(basePath, *this, true))
+ if (!openingVisitor(basePath, *this, true))
return true;
- auto atEnd = qScopeGuard([closingVisitor, basePath, this](){
- if (closingVisitor)
- closingVisitor(basePath, *this, true);
+ auto atEnd = qScopeGuard(
+ [closingVisitor, basePath, this]() { closingVisitor(basePath, *this, true); });
+ return visitEl([this, basePath, visitor, openingVisitor, closingVisitor, options](auto &&el) {
+ return el->iterateDirectSubpathsConst(
+ *this,
+ [this, basePath, visitor, openingVisitor, closingVisitor,
+ options](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) {
+ Path pNow;
+ if (!(options & VisitOption::NoPath)) {
+ pNow = basePath;
+ pNow = pNow.appendComponent(c);
+ }
+ DomItem item = itemF();
+ bool directChild = isCanonicalChild(item);
+ if (!directChild && !(options & VisitOption::VisitAdopted))
+ return true;
+ if (!directChild || !(options & VisitOption::Recurse)) {
+ if (!visitor(pNow, item, directChild))
+ return false;
+ // give an option to avoid calling open/close when not recursing?
+ // calling it always allows close to do the reverse looping (children before
+ // parent)
+ if (!openingVisitor(pNow, item, directChild))
+ return true;
+ closingVisitor(pNow, item, directChild);
+ } else {
+ return item.visitTree(pNow, visitor, options | VisitOption::VisitSelf,
+ openingVisitor, closingVisitor);
+ }
+ return true;
+ });
});
- return base()->iterateDirectSubpathsConst(*this,
- [this, basePath, visitor, openingVisitor, closingVisitor, options]
- (Path p, const DomItem &item)
- {
- Path pNow;
- if (!(options & VisitOption::NoPath))
- pNow = basePath.path(p);
- if (item.containingObject() != *this) {
- if (!(options & VisitOption::VisitAdopted))
- return true;
- if (visitor && !visitor(pNow, item, false))
- return false;
- if (openingVisitor && !openingVisitor(pNow, item, false))
- return true;
- if (closingVisitor)
- closingVisitor(pNow, item, false);
- } else {
- return item.visitChildren(pNow, visitor, options, openingVisitor, closingVisitor);
- }
+}
+
+bool DomItem::visitPrototypeChain(function_ref<bool(DomItem &)> visitor,
+ VisitPrototypesOptions options, ErrorHandler h,
+ QSet<quintptr> *visited, QList<Path> *visitedRefs)
+{
+ QSet<quintptr> visitedLocal;
+ if (!visited)
+ visited = &visitedLocal;
+ QList<Path> refsLocal;
+ if (!visitedRefs)
+ visitedRefs = &refsLocal;
+ bool shouldVisit = !(options & VisitPrototypesOption::SkipFirst);
+ DomItem current = qmlObject();
+ if (!current) {
+ myErrors().warning(tr("Prototype chain called outside object")).withItem(*this).handle(h);
return true;
- });
+ }
+ QList<DomItem> toDo({ current });
+ while (!toDo.isEmpty()) {
+ current = toDo.takeLast();
+ current = current.proceedToScope(h, visitedRefs);
+ if (visited->contains(current.id())) {
+ // to warn about circular dependencies a purely local visited trace is required
+ // as common ancestors of unrelated objects are valid and should be skipped
+ // so we do not to warn unless requested
+ if (options & VisitPrototypesOption::RevisitWarn)
+ myErrors()
+ .warning(tr("Detected multiple visit of %1 visiting prototypes of %2")
+ .arg(current.canonicalPath().toString(),
+ canonicalPath().toString()))
+ .withItem(*this)
+ .handle(h);
+ continue;
+ }
+ visited->insert(current.id());
+ if (shouldVisit && !visitor(current))
+ return false;
+ shouldVisit = true;
+ current.field(Fields::prototypes)
+ .visitIndexes([&toDo, &current, this, &h, visitedRefs, options](DomItem &el) {
+ Path elId = el.canonicalPath();
+ if (visitedRefs->contains(elId))
+ return true;
+ else
+ visitedRefs->append(elId);
+ QList<DomItem> protos = el.getAll(h, visitedRefs);
+ if (protos.isEmpty()) {
+ if (std::shared_ptr<DomEnvironment> envPtr =
+ environment().ownerAs<DomEnvironment>())
+ if (!(envPtr->options() & DomEnvironment::Option::NoDependencies))
+ myErrors()
+ .warning(tr("could not resolve prototype %1 (%2)")
+ .arg(current.canonicalPath().toString(),
+ el.field(Fields::referredObjectPath)
+ .value()
+ .toString()))
+ .withItem(*this)
+ .handle(h);
+ } else {
+ if (protos.length() > 1) {
+ QStringList protoPaths;
+ for (DomItem &p : protos)
+ protoPaths.append(p.canonicalPath().toString());
+ myErrors()
+ .warning(tr("Multiple definitions found, using first only, "
+ "resolving prototype %1 (%2): %3")
+ .arg(current.canonicalPath().toString(),
+ el.field(Fields::referredObjectPath)
+ .value()
+ .toString(),
+ protoPaths.join(QLatin1String(", "))))
+ .withItem(*this)
+ .handle(h);
+ }
+ int nProtos = 1; // change to protos.length() to us all prototypes found
+ // (sloppier)
+ for (int i = nProtos; i != 0;) {
+ DomItem proto = protos.at(--i);
+ if (proto.internalKind() == DomType::Export) {
+ if (!(options & VisitPrototypesOption::ManualProceedToScope))
+ proto = proto.proceedToScope(h, visitedRefs);
+ toDo.append(proto);
+ } else if (proto.internalKind() == DomType::QmlObject) {
+ toDo.append(proto);
+ } else {
+ myErrors()
+ .warning(tr("Unexpected prototype type %1 (%2)")
+ .arg(current.canonicalPath().toString(),
+ el.field(Fields::referredObjectPath)
+ .value()
+ .toString()))
+ .withItem(*this)
+ .handle(h);
+ }
+ }
+ }
+ return true;
+ });
+ }
+ return true;
}
-DomItem DomItem::operator[](const QString &cName) const
+bool DomItem::visitDirectAccessibleScopes(function_ref<bool(DomItem &)> visitor,
+ VisitPrototypesOptions options, ErrorHandler h,
+ QSet<quintptr> *visited, QList<Path> *visitedRefs)
{
- if (internalKind() == DomType::Map)
- return key(cName);
- return field(cName);
+ if (internalKind() == DomType::QmlObject)
+ return visitPrototypeChain(visitor, options, h, visited, visitedRefs);
+ if (visited && id() != 0) {
+ if (visited->contains(id()))
+ return true;
+ visited->insert(id());
+ }
+ if (!(options & VisitPrototypesOption::SkipFirst))
+ visitor(*this);
+ return true;
}
-DomItem DomItem::operator[](QStringView cName) const
+/*!
+ * \brief DomItem::visitStaticTypePrototypeChains
+ * \param visitor
+ * \param visitFirst
+ * \param visited
+ * \return
+ *
+ * visit the values JS reaches accessing a type directly: the values if it is a singleton or the
+ * attached type
+ */
+bool DomItem::visitStaticTypePrototypeChains(function_ref<bool(DomItem &)> visitor,
+ VisitPrototypesOptions options, ErrorHandler h,
+ QSet<quintptr> *visited, QList<Path> *visitedRefs)
+{
+ QSet<quintptr> visitedLocal;
+ if (!visited)
+ visited = &visitedLocal;
+ DomItem current = qmlObject();
+ DomItem comp = current.component();
+ if (comp.field(Fields::isSingleton).value().toBool(false)
+ && !current.visitPrototypeChain(visitor, options, h, visited, visitedRefs))
+ return false;
+ if (DomItem attachedT = current.component().field(Fields::attachedType).field(Fields::get))
+ if (!attachedT.visitPrototypeChain(
+ visitor, options & ~VisitPrototypesOptions(VisitPrototypesOption::SkipFirst), h,
+ visited, visitedRefs))
+ return false;
+ return true;
+}
+
+bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptions options,
+ ErrorHandler h, QSet<quintptr> *visited, QList<Path> *visitedRefs)
{
- if (internalKind() == DomType::Map)
- return key(cName.toString());
- return field(cName);
+ QSet<quintptr> visitedLocal;
+ if (!visited)
+ visited = &visitedLocal;
+ QList<Path> visitedRefsLocal;
+ if (!visitedRefs)
+ visitedRefs = &visitedRefsLocal;
+ DomItem current = scope();
+ if (!current) {
+ myResolveErrors().warning(tr("Called visitScopeChain outside scopes")).handle(h);
+ return true;
+ }
+ QList<DomItem> toDo { current };
+ bool visitFirst = !(options & LookupOption::SkipFirstScope);
+ bool visitCurrent = visitFirst;
+ bool first = true;
+ while (!toDo.isEmpty()) {
+ DomItem current = toDo.takeLast();
+ if (visited->contains(current.id()))
+ continue;
+ visited->insert(current.id());
+ if (visitCurrent && !visitor(current))
+ return false;
+ visitCurrent = true;
+ switch (current.internalKind()) {
+ case DomType::QmlObject: {
+ if (!current.visitPrototypeChain(visitor, VisitPrototypesOption::SkipFirst, h, visited,
+ visitedRefs))
+ return false;
+ DomItem root = current.rootQmlObject();
+ if (root && root != current) {
+ first = false;
+ toDo.append(root);
+ } else if (DomItem next = current.scope(
+ FilterUpOptions::ReturnOuterNoSelf)) { // should be the component
+ toDo.append(next);
+ }
+ } break;
+ case DomType::ScriptExpression: // Js lexical scope
+ first = false;
+ if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
+ toDo.append(next);
+ break;
+ case DomType::QmlComponent: // ids/attached type
+ if ((options & LookupOption::Strict) == 0) {
+ if (DomItem comp = current.field(Fields::nextComponent))
+ toDo.append(comp);
+ }
+ if (first && visitFirst && (options & LookupOption::VisitTopClassType)
+ && *this == current) { // visit attached type if it is the top of the chain
+ if (DomItem attachedT = current.field(Fields::attachedType).field(Fields::get))
+ toDo.append(attachedT);
+ }
+ if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
+ toDo.append(next);
+ first = false;
+ break;
+ case DomType::QmlFile: // subComponents, imported types
+ if (DomItem iScope =
+ current.field(Fields::importScope)) // treat file as a separate scope?
+ toDo.append(iScope);
+ first = false;
+ break;
+ case DomType::MethodInfo: // method arguments
+ first = false;
+ if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
+ toDo.append(next);
+ break;
+ case DomType::ImportScope: // types
+ first = false;
+ if (auto globalC = globalScope().field(Fields::rootComponent))
+ toDo.append(globalC);
+ break;
+ case DomType::JsResource:
+ case DomType::GlobalComponent:
+ first = false;
+ if (DomItem next = current.field(Fields::objects).index(0))
+ toDo.append(next);
+ break;
+ case DomType::QmltypesComponent:
+ first = false;
+ break;
+ default:
+ first = false;
+ myResolveErrors()
+ .error(tr("Unexpected non scope object %1 (%2) reached in visitScopeChain")
+ .arg(domTypeToString(current.internalKind()),
+ current.canonicalPath().toString()))
+ .handle(h);
+ Q_ASSERT(false);
+ break;
+ }
+ }
+ return true;
}
-DomItem DomItem::operator[](Path p) const
+QSet<QString> DomItem::localSymbolNames()
{
- return path(p);
+ QSet<QString> res;
+ switch (internalKind()) {
+ case DomType::QmlObject:
+ res += propertyDefs().keys();
+ res += bindings().keys();
+ res += methods().keys();
+ break;
+ case DomType::ScriptExpression:
+ // to do
+ break;
+ case DomType::QmlComponent:
+ res += ids().keys();
+ Q_FALLTHROUGH();
+ case DomType::QmlFile: // subComponents, imported types
+ {
+ DomItem comps = field(Fields::components);
+ for (auto k : comps.keys())
+ if (!k.isEmpty())
+ res.insert(k);
+ break;
+ }
+ case DomType::QmltypesComponent:
+ case DomType::JsResource:
+ case DomType::GlobalComponent:
+ res += enumerations().keys();
+ break;
+ case DomType::MethodInfo: {
+ DomItem params = field(Fields::parameters);
+ params.visitIndexes([&res](DomItem &p) {
+ const MethodParameter *pPtr = p.as<MethodParameter>();
+ res.insert(pPtr->name);
+ return true;
+ });
+ break;
+ }
+ case DomType::ImportScope: {
+ const ImportScope *currentPtr = as<ImportScope>();
+ res += currentPtr->importedNames(*this);
+ for (auto k : currentPtr->subImports().keys())
+ res.insert(k);
+ break;
+ }
+ default:
+ break;
+ }
+ return res;
}
-QCborValue DomItem::value() const
+bool DomItem::visitLookup1(QString symbolName, function_ref<bool(DomItem &)> visitor,
+ LookupOptions opts, ErrorHandler h, QSet<quintptr> *visited,
+ QList<Path> *visitedRefs)
{
- if (internalKind() == DomType::ConstantData)
- return static_cast<ConstantData const *>(base())->value();
- return QCborValue();
+ return visitScopeChain(
+ [symbolName, visitor](DomItem &obj) {
+ return obj.visitLocalSymbolsNamed(symbolName,
+ [visitor](DomItem &el) { return visitor(el); });
+ },
+ opts, h, visited, visitedRefs);
}
-void DomItem::dumpPtr(Sink sink) const
+class CppTypeInfo
{
- sink(u"DomItem{ topPtr:");
- sink(QString::number((quintptr)m_top.get(),16));
- sink(u", ownerPtr:");
- sink(QString::number((quintptr)m_owner.get(),16));
- sink(u", m_basePtr:");
- sink(QString::number((quintptr)m_base,16));
- sink(u", basePtr:");
- sink(QString::number((quintptr)base(),16));
- sink(u"}");
+ Q_DECLARE_TR_FUNCTIONS(CppTypeInfo)
+public:
+ CppTypeInfo() = default;
+
+ static CppTypeInfo fromString(QStringView target, ErrorHandler h = nullptr)
+ {
+ CppTypeInfo res;
+ QRegularExpression reTarget = QRegularExpression(QRegularExpression::anchoredPattern(
+ 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>\*?))"));
+ QRegularExpressionMatch m = reTarget.match(target);
+ if (!m.hasMatch()) {
+ DomItem::myResolveErrors()
+ .error(tr("Unexpected complex CppType %1").arg(target))
+ .handle(h);
+ }
+ res.baseType = m.captured(u"baseType");
+ res.isPointer = !m.captured(u"ptr").isEmpty();
+ if (!m.captured(u"list").isEmpty()) {
+ res.isList = true;
+ res.baseType = m.captured(u"list");
+ res.isPointer = !m.captured(u"listPtr").isEmpty();
+ }
+ if (!m.captured(u"mapValue").isEmpty()) {
+ res.isMap = true;
+ if (m.captured(u"mapKey") != u"QString") {
+ DomItem::myResolveErrors()
+ .error(tr("Unexpected complex CppType %1 (map with non QString key)")
+ .arg(target))
+ .handle(h);
+ }
+ res.baseType = m.captured(u"mapValue");
+ res.isPointer = !m.captured(u"mapPtr").isEmpty();
+ }
+ return res;
+ }
+
+ QString baseType;
+ bool isPointer = false;
+ bool isMap = false;
+ bool isList = false;
+};
+
+bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor,
+ LookupType lookupType, LookupOptions opts, ErrorHandler errorHandler,
+ QSet<quintptr> *visited, QList<Path> *visitedRefs)
+{
+ if (target.isEmpty())
+ return true;
+ switch (lookupType) {
+ case LookupType::Binding:
+ case LookupType::Method:
+ case LookupType::Property:
+ case LookupType::PropertyDef:
+ case LookupType::Symbol:
+ case LookupType::Type: {
+ QStringList subpath = target.split(QChar::fromLatin1('.'));
+ if (subpath.length() == 1) {
+ return visitLookup1(subpath.first(), visitor, opts, errorHandler, visited, visitedRefs);
+ } else {
+ return visitLookup1(
+ subpath.at(0),
+ [&subpath, visitor, lookupType, &errorHandler, visitedRefs](DomItem &newIt) {
+ QVector<ResolveToDo> lookupToDos({ ResolveToDo {
+ newIt, 1 } }); // invariant: always increase pathIndex to guarantee
+ // end even with only partial visited match
+ QList<QSet<quintptr>> lookupVisited(subpath.length() + 1);
+ while (!lookupToDos.isEmpty()) {
+ ResolveToDo tNow = lookupToDos.takeFirst();
+ auto vNow = qMakePair(tNow.item.id(), tNow.pathIndex);
+ if (vNow.first != 0) {
+ if (lookupVisited[vNow.second].contains(vNow.first))
+ continue;
+ else
+ lookupVisited[vNow.second].insert(vNow.first);
+ }
+ DomItem subNow = tNow.item;
+ int iSubPath = tNow.pathIndex;
+ Q_ASSERT(iSubPath < subpath.length());
+ QString subPathNow = subpath[iSubPath++];
+ DomItem scope = subNow.proceedToScope();
+ if (iSubPath < subpath.length()) {
+ if (scope.internalKind() == DomType::QmlObject)
+ scope.visitDirectAccessibleScopes(
+ [&lookupToDos, subPathNow, iSubPath](DomItem &el) {
+ return el.visitLocalSymbolsNamed(
+ subPathNow,
+ [&lookupToDos, iSubPath](DomItem &subEl) {
+ lookupToDos.append({ subEl, iSubPath });
+ return true;
+ });
+ },
+ VisitPrototypesOption::Normal, errorHandler,
+ &(lookupVisited[vNow.second]), visitedRefs);
+ } else {
+ bool cont = scope.visitDirectAccessibleScopes(
+ [&visitor, subPathNow, lookupType](DomItem &el) -> bool {
+ if (lookupType == LookupType::Symbol)
+ return el.visitLocalSymbolsNamed(subPathNow,
+ visitor);
+ else
+ return el.visitLocalSymbolsNamed(
+ subPathNow,
+ [lookupType,
+ &visitor](DomItem &el) -> bool {
+ bool correctType = false;
+ DomType iType = el.internalKind();
+ switch (lookupType) {
+ case LookupType::Binding:
+ correctType =
+ (iType == DomType::Binding);
+ break;
+ case LookupType::Method:
+ correctType =
+ (iType
+ == DomType::MethodInfo);
+ break;
+ case LookupType::Property:
+ correctType =
+ (iType
+ == DomType::
+ PropertyDefinition
+ || iType
+ == DomType::
+ Binding);
+ break;
+ case LookupType::PropertyDef:
+ correctType =
+ (iType
+ == DomType::
+ PropertyDefinition);
+ break;
+ case LookupType::Type:
+ correctType =
+ (iType
+ == DomType::
+ Export); // accept
+ // direct
+ // QmlObject
+ // ref?
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ if (correctType)
+ return visitor(el);
+ return true;
+ });
+ },
+ VisitPrototypesOption::Normal, errorHandler,
+ &(lookupVisited[vNow.second]), visitedRefs);
+ if (!cont)
+ return false;
+ }
+ }
+ return true;
+ },
+ opts, errorHandler, visited, visitedRefs);
+ }
+ break;
+ }
+ case LookupType::CppType: {
+ QString baseTarget = CppTypeInfo::fromString(target, errorHandler).baseType;
+ DomItem localQmltypes = owner();
+ while (localQmltypes && localQmltypes.internalKind() != DomType::QmltypesFile) {
+ localQmltypes = localQmltypes.containingObject();
+ localQmltypes = localQmltypes.owner();
+ }
+ if (localQmltypes) {
+ if (DomItem localTypes = localQmltypes.field(Fields::components).key(baseTarget)) {
+ bool cont = localTypes.visitIndexes([&visitor](DomItem &els) {
+ return els.visitIndexes([&visitor](DomItem &el) {
+ if (DomItem obj = el.field(Fields::objects).index(0))
+ return visitor(obj);
+ return true;
+ });
+ });
+ if (!cont)
+ return false;
+ }
+ }
+ DomItem qmltypes = environment().field(Fields::qmltypesFileWithPath);
+ return qmltypes.visitKeys([baseTarget, &visitor](QString, DomItem &els) {
+ DomItem comps =
+ els.field(Fields::currentItem).field(Fields::components).key(baseTarget);
+ return comps.visitIndexes([&visitor](DomItem &el) {
+ if (DomItem obj = el.field(Fields::objects).index(0))
+ return visitor(obj);
+ return true;
+ });
+ });
+ break;
+ }
+ }
+ Q_ASSERT(false);
+ return true;
}
-void DomItem::dump(Sink s, int indent) const
+DomItem DomItem::proceedToScope(ErrorHandler h, QList<Path> *visitedRefs)
{
- base()->dump(*this, s, indent);
+ // follow references, resolve exports
+ DomItem current = *this;
+ while (current) {
+ switch (current.internalKind()) {
+ case DomType::Reference: {
+ Path currentPath = current.canonicalPath();
+ current = current.get(h, visitedRefs);
+ break;
+ }
+ case DomType::Export:
+ current = current.field(Fields::type);
+ break;
+ default:
+ return current.scope();
+ break;
+ }
+ }
+ return DomItem();
}
-QString DomItem::toString() const
+QList<DomItem> DomItem::lookup(QString symbolName, LookupType type, LookupOptions opts,
+ ErrorHandler errorHandler)
{
- return dumperToString([this](Sink s){ dump(s); });
+ QList<DomItem> res;
+ visitLookup(
+ symbolName,
+ [&res](DomItem &el) {
+ res.append(el);
+ return true;
+ },
+ type, opts, errorHandler);
+ return res;
}
-int DomItem::derivedFrom() const
+DomItem DomItem::lookupFirst(QString symbolName, LookupType type, LookupOptions opts,
+ ErrorHandler errorHandler)
{
- if (m_owner)
- return m_owner->derivedFrom();
- return 0;
+ DomItem res;
+ visitLookup(
+ symbolName,
+ [&res](DomItem &el) {
+ res = el;
+ return false;
+ },
+ type, opts, errorHandler);
+ return res;
}
-int DomItem::revision() const {
- if (m_owner)
- return m_owner->revision();
- else
- return -1;
+quintptr DomItem::id()
+{
+ return visitEl([](auto &&b) { return b->id(); });
}
-QDateTime DomItem::createdAt() const
+Path DomItem::pathFromOwner()
{
- if (m_owner)
- return m_owner->createdAt();
- else
- return QDateTime::fromMSecsSinceEpoch(0);
+ return visitEl([this](auto &&e) { return e->pathFromOwner(*this); });
}
-QDateTime DomItem::frozenAt() const
+QString DomItem::canonicalFilePath()
{
- if (m_owner)
- return m_owner->frozenAt();
- else
- return QDateTime::fromMSecsSinceEpoch(0);
+ return visitEl([this](auto &&e) { return e->canonicalFilePath(*this); });
}
-QDateTime DomItem::lastDataUpdateAt() const
+DomItem DomItem::fileLocationsTree()
{
- if (m_owner)
- return m_owner->lastDataUpdateAt();
- else
- return QDateTime::fromMSecsSinceEpoch(0);
+ if (DomItem l = field(Fields::fileLocationsTree))
+ return l;
+ auto res = FileLocations::findAttachedInfo(*this, AttachedInfo::FindOption::SetFoundTreePath);
+ if (res && res.foundTreePath.value()) {
+ return copy(res.foundTree, res.foundTreePath.value());
+ }
+ return DomItem();
}
-void DomItem::addError(ErrorMessage msg) const
+DomItem DomItem::fileLocations()
{
- if (m_owner)
- m_owner->addError(this->copy(m_owner), msg.withItem(*this));
- else
- defaultErrorHandler(msg.withItem(*this));
+ return fileLocationsTree().field(Fields::infoItem);
}
-ErrorHandler DomItem::errorHandler() const
+MutableDomItem DomItem::makeCopy(DomItem::CopyOption option)
{
- DomItem self = *this;
- return [self](ErrorMessage m){
- self.addError(m);
- };
+ if (m_kind == DomType::Empty)
+ return MutableDomItem();
+ DomItem o = owner();
+ if (option == CopyOption::EnvDisconnected) {
+ DomItem newItem = std::visit(
+ [this, &o](auto &&el) {
+ auto copyPtr = el->makeCopy(o);
+ return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get());
+ },
+ *m_owner);
+ return MutableDomItem(newItem.path(pathFromOwner()));
+ }
+ DomItem env = environment();
+ std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
+ Q_ASSERT(envPtr);
+ std::shared_ptr<DomEnvironment> newEnvPtr(
+ new DomEnvironment(envPtr, envPtr->loadPaths(), envPtr->options()));
+ DomBase *eBase = envPtr.get();
+ if (std::holds_alternative<DomEnvironment *>(m_element) && eBase
+ && std::get<DomEnvironment *>(m_element) == eBase)
+ return MutableDomItem(DomItem(newEnvPtr));
+ DomItem newItem = std::visit(
+ [this, newEnvPtr, &o](auto &&el) {
+ auto copyPtr = el->makeCopy(o);
+ return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get());
+ },
+ *m_owner);
+
+ switch (o.internalKind()) {
+ case DomType::QmlDirectory:
+ newEnvPtr->addQmlDirectory(newItem.ownerAs<QmlDirectory>(), AddOption::Overwrite);
+ break;
+ case DomType::JsFile:
+ newEnvPtr->addJsFile(newItem.ownerAs<JsFile>(), AddOption::Overwrite);
+ break;
+ case DomType::QmlFile:
+ newEnvPtr->addQmlFile(newItem.ownerAs<QmlFile>(), AddOption::Overwrite);
+ break;
+ case DomType::QmltypesFile:
+ newEnvPtr->addQmltypesFile(newItem.ownerAs<QmltypesFile>(), AddOption::Overwrite);
+ break;
+ case DomType::GlobalScope: {
+ newEnvPtr->addGlobalScope(newItem.ownerAs<GlobalScope>(), AddOption::Overwrite);
+ } break;
+ case DomType::ModuleIndex:
+ case DomType::MockOwner:
+ case DomType::ScriptExpression:
+ case DomType::AstComments:
+ case DomType::LoadInfo:
+ case DomType::AttachedInfo:
+ case DomType::DomEnvironment:
+ case DomType::DomUniverse:
+ qCWarning(domLog) << "DomItem::makeCopy " << internalKindStr()
+ << " does not support binding to environment";
+ Q_ASSERT(false);
+ return MutableDomItem();
+ default:
+ qCWarning(domLog) << "DomItem::makeCopy(" << internalKindStr()
+ << ") is not an known OwningItem";
+ Q_ASSERT(o.isOwningItem());
+ return MutableDomItem();
+ }
+ DomItem newEnv(newEnvPtr);
+ Q_ASSERT(newEnv.path(o.canonicalPath()).m_owner == newItem.m_owner);
+ return MutableDomItem(newItem.path(pathFromOwner()));
}
-void DomItem::clearErrors(ErrorGroups groups, bool iterate) const
+bool DomItem::commitToBase()
{
- if (m_owner) {
- m_owner->clearErrors(groups);
- if (iterate)
- iterateSubOwners([groups](DomItem i){
- i.clearErrors(groups, true);
- return true;
- });
+ DomItem env = environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
+ return envPtr->commitToBase(env);
}
+ return false;
}
-bool DomItem::iterateErrors(function_ref<bool (DomItem, ErrorMessage)> visitor, bool iterate,
- Path inPath) const
+bool DomItem::visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)> visitor)
{
- if (m_owner) {
- if (!m_owner->iterateErrors(owner(), visitor, inPath))
+ if (name.isEmpty()) // no empty symbol
+ return true;
+ // we could avoid discriminating by type and just access all the needed stuff in the correct
+ // sequence, making sure it is empty if not provided, but for now I find it clearer to check the
+ // type
+ DomItem f;
+ DomItem v;
+ switch (internalKind()) {
+ case DomType::QmlObject:
+ f = field(Fields::propertyDefs);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
return false;
- if (iterate && !iterateSubOwners([inPath, visitor](DomItem i){
- return i.iterateErrors(visitor, true, inPath);
+ f = field(Fields::bindings);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
+ return false;
+ f = field(Fields::methods);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
+ return false;
+ break;
+ case DomType::ScriptExpression:
+ // to do
+ break;
+ case DomType::QmlComponent:
+ f = field(Fields::ids);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
+ return false;
+ Q_FALLTHROUGH();
+ case DomType::JsResource:
+ case DomType::GlobalComponent:
+ case DomType::QmltypesComponent:
+ f = field(Fields::enumerations);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
+ return false;
+ break;
+ case DomType::MethodInfo: {
+ DomItem params = field(Fields::parameters);
+ if (!params.visitIndexes([name, visitor](DomItem &p) {
+ const MethodParameter *pPtr = p.as<MethodParameter>();
+ if (pPtr->name == name && !visitor(p))
+ return false;
+ return true;
}))
return false;
+ break;
+ }
+ case DomType::QmlFile: {
+ f = field(Fields::components);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
+ return false;
+ break;
+ }
+ case DomType::ImportScope: {
+ f = field(Fields::imported);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
+ return false;
+ f = field(Fields::qualifiedImports);
+ v = f.key(name);
+ if (!v.visitIndexes(visitor))
+ return false;
+ break;
+ default:
+ Q_ASSERT(!isScope());
+ break;
+ }
}
return true;
}
-bool DomItem::iterateSubOwners(function_ref<bool (DomItem)> visitor) const
+DomItem DomItem::operator[](const QString &cName)
{
- if (m_owner)
- return m_owner->iterateSubOwners(owner(), visitor);
- return true;
+ if (internalKind() == DomType::Map)
+ return key(cName);
+ return field(cName);
}
-shared_ptr<DomTop> DomItem::topPtr() const
+DomItem DomItem::operator[](QStringView cName)
{
- return m_top;
+ if (internalKind() == DomType::Map)
+ return key(cName.toString());
+ return field(cName);
}
-shared_ptr<OwningItem> DomItem::owningItemPtr() const
+DomItem DomItem::operator[](Path p)
{
- return m_owner;
+ return path(p);
}
-DomItem DomItem::copy(shared_ptr<OwningItem> owner, Path ownerPath, DomBase *base) const
+QCborValue DomItem::value()
{
- return DomItem(m_top, owner, ownerPath, base);
+ if (internalKind() == DomType::ConstantData)
+ return std::get<ConstantData>(m_element).value();
+ return QCborValue();
}
-DomItem DomItem::copy(shared_ptr<OwningItem> owner, Path ownerPath) const
+void DomItem::dumpPtr(Sink sink)
{
- return DomItem(m_top, owner, ownerPath, owner.get());
+ sink(u"DomItem{ topPtr:");
+ sink(QString::number((quintptr)topPtr().get(), 16));
+ sink(u", ownerPtr:");
+ sink(QString::number((quintptr)owningItemPtr().get(), 16));
+ sink(u", ownerPath:");
+ m_ownerPath.dump(sink);
+ sink(u", elPtr:");
+ sink(QString::number((quintptr)base(),16));
+ sink(u"}");
}
-DomItem DomItem::copy(DomBase *base) const
+void DomItem::dump(Sink s, int indent,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter)
{
- return DomItem(m_top, m_owner, m_ownerPath, base);
+ visitEl([this, s, indent, filter](auto &&e) { e->dump(*this, s, indent, filter); });
}
-Subpath DomItem::subDataField(QStringView fieldName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+QString DomItem::toString()
{
- return subDataPath(Path::Field(fieldName), value, options, loc);
+ return dumperToString([this](Sink s){ dump(s); });
}
-Subpath DomItem::subDataField(QString fieldName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+int DomItem::derivedFrom()
{
- return subDataPath(Path::Field(fieldName), value, options, loc);
+ if (m_owner)
+ return std::visit([](auto &&ow) { return ow->derivedFrom(); }, *m_owner);
+ return 0;
}
-Subpath DomItem::subDataIndex(index_type i, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+int DomItem::revision()
{
- return subDataPath(Path::Index(i), value, options, loc);
+ if (m_owner)
+ return std::visit([](auto &&ow) { return ow->revision(); }, *m_owner);
+ else
+ return -1;
}
-Subpath DomItem::subDataKey(QStringView keyName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+QDateTime DomItem::createdAt()
{
- return subDataPath(Path::Key(keyName), value, options, loc);
+ if (m_owner)
+ return std::visit([](auto &&ow) { return ow->createdAt(); }, *m_owner);
+ else
+ return QDateTime::fromMSecsSinceEpoch(0);
}
-Subpath DomItem::subDataKey(QString keyName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+QDateTime DomItem::frozenAt()
{
- return subDataPath(Path::Key(keyName), value, options, loc);
+ if (m_owner)
+ return std::visit([](auto &&ow) { return ow->frozenAt(); }, *m_owner);
+ else
+ return QDateTime::fromMSecsSinceEpoch(0);
}
-Subpath DomItem::subDataPath(Path path, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+QDateTime DomItem::lastDataUpdateAt()
{
- if (domTypeIsOwningItem(internalKind()))
- return Subpath{path, DomItem(m_top, m_owner,
- ConstantData(path, value, options, loc))};
+ if (m_owner)
+ return std::visit([](auto &&ow) { return ow->lastDataUpdateAt(); }, *m_owner);
else
- return Subpath{path, DomItem(m_top, m_owner,
- ConstantData(pathFromOwner().path(path), value, options, loc))};
+ return QDateTime::fromMSecsSinceEpoch(0);
}
-Subpath DomItem::subReferenceField(QStringView fieldName, Path referencedObject,
- const SourceLocation & loc) const
+void DomItem::addError(ErrorMessage msg)
{
- return subReferencePath(Path::Field(fieldName), referencedObject, loc);
+ if (m_owner) {
+ DomItem myOwner = owner();
+ std::visit(
+ [this, &myOwner, &msg](auto &&ow) { ow->addError(myOwner, msg.withItem(*this)); },
+ *m_owner);
+ } else
+ defaultErrorHandler(msg.withItem(*this));
}
-Subpath DomItem::subReferenceField(QString fieldName, Path referencedObject, const SourceLocation & loc) const
+ErrorHandler DomItem::errorHandler()
{
- return subReferencePath(Path::Field(fieldName), referencedObject, loc);
+ DomItem self = *this;
+ return [self](ErrorMessage m) mutable { self.addError(m); };
}
-Subpath DomItem::subReferenceKey(QStringView keyName, Path referencedObject, const SourceLocation & loc) const
+void DomItem::clearErrors(ErrorGroups groups, bool iterate)
{
- return subReferencePath(Path::Key(keyName), referencedObject,loc);
+ if (m_owner) {
+ std::visit([&groups](auto &&ow) { ow->clearErrors(groups); }, *m_owner);
+ if (iterate)
+ iterateSubOwners([groups](DomItem i){
+ i.clearErrors(groups, true);
+ return true;
+ });
+ }
}
-Subpath DomItem::subReferenceKey(QString keyName, Path referencedObject, const SourceLocation & loc) const
+bool DomItem::iterateErrors(function_ref<bool(DomItem, ErrorMessage)> visitor, bool iterate,
+ Path inPath)
{
- return subReferencePath(Path::Key(keyName), referencedObject, loc);
+ if (m_owner) {
+ DomItem ow = owner();
+ if (!std::visit([&ow, visitor,
+ inPath](auto &&el) { return el->iterateErrors(ow, visitor, inPath); },
+ *m_owner))
+ return false;
+ if (iterate && !iterateSubOwners([inPath, visitor](DomItem &i) {
+ return i.iterateErrors(visitor, true, inPath);
+ }))
+ return false;
+ }
+ return true;
}
-Subpath DomItem::subReferenceIndex(index_type i, Path referencedObject, const SourceLocation & loc) const
+bool DomItem::iterateSubOwners(function_ref<bool(DomItem &)> visitor)
{
- return subReferencePath(Path::Index(i), referencedObject, loc);
+ if (m_owner) {
+ DomItem ow = owner();
+ return std::visit([&ow, visitor](auto &&o) { return o->iterateSubOwners(ow, visitor); },
+ *m_owner);
+ }
+ return true;
}
-Subpath DomItem::subReferencePath(Path path, Path referencedObject, const SourceLocation & loc) const
+bool DomItem::iterateDirectSubpaths(DirectVisitor v)
{
- if (domTypeIsOwningItem(internalKind()))
- return Subpath{path, DomItem(m_top, m_owner,
- Reference(referencedObject, path, loc))};
- else
- return Subpath{path, DomItem(m_top, m_owner,
- Reference(referencedObject, pathFromOwner().path(path), loc))};
+ return visitMutableEl(
+ [this, v](auto &&el) mutable { return el->iterateDirectSubpaths(*this, v); });
}
-Subpath DomItem::toSubField(QStringView fieldName) const
+shared_ptr<DomTop> DomItem::topPtr()
{
- return Subpath{Path::Field(fieldName), *this};
+ if (m_top)
+ return std::visit([](auto &&el) -> shared_ptr<DomTop> { return el; }, *m_top);
+ return {};
}
-Subpath DomItem::toSubField(QString fieldName) const
+shared_ptr<OwningItem> DomItem::owningItemPtr()
{
- return Subpath{Path::Field(fieldName), *this};
+ if (m_owner)
+ return std::visit([](auto &&el) -> shared_ptr<OwningItem> { return el; }, *m_owner);
+ return {};
}
-Subpath DomItem::toSubKey(QStringView keyName) const
+const DomBase *DomItem::base()
{
- return Subpath{Path::Key(keyName), *this};
+ return visitEl([](auto &&el) { return static_cast<const DomBase *>(&(*el)); });
}
-Subpath DomItem::toSubKey(QString keyName) const
+DomBase *DomItem::mutableBase()
{
- return Subpath{Path::Key(keyName), *this};
+ return visitMutableEl([](auto &&el) { return static_cast<DomBase *>(&(*el)); });
}
-Subpath DomItem::toSubIndex(index_type i) const
+DomItem::DomItem(std::shared_ptr<DomEnvironment> envPtr):
+ DomItem(envPtr, envPtr, Path(), envPtr.get())
{
- return Subpath{Path::Index(i), *this};
}
-Subpath DomItem::toSubPath(Path subPath) const
+DomItem::DomItem(std::shared_ptr<DomUniverse> universePtr):
+ DomItem(universePtr, universePtr, Path(), universePtr.get())
{
- return Subpath{subPath, *this};
}
-Subpath DomItem::subList(const List &list) const
+void DomItem::loadFile(QString canonicalFilePath, QString logicalPath, QString code,
+ QDateTime codeDate, DomTop::Callback callback, LoadOptions loadOptions)
{
- return Subpath{list.pathFromOwner().last(), DomItem(m_top, m_owner, list)};
+ DomItem topEl = top();
+ if (topEl.internalKind() == DomType::DomEnvironment
+ || topEl.internalKind() == DomType::DomUniverse) {
+ if (auto univ = topEl.ownerAs<DomUniverse>())
+ univ->loadFile(*this, canonicalFilePath, logicalPath, code, codeDate, callback,
+ loadOptions);
+ else if (auto env = topEl.ownerAs<DomEnvironment>()) {
+ if (env->options() & DomEnvironment::Option::NoDependencies)
+ env->loadFile(topEl, canonicalFilePath, logicalPath, code, codeDate, callback,
+ DomTop::Callback(), DomTop::Callback(), loadOptions);
+ else
+ env->loadFile(topEl, canonicalFilePath, logicalPath, code, codeDate,
+ DomTop::Callback(), DomTop::Callback(), callback, loadOptions);
+ } else
+ Q_ASSERT(false && "expected either DomUniverse or DomEnvironment cast to succeed");
+ } else {
+ addError(myErrors().warning(tr("loadFile called without DomEnvironment or DomUniverse.")));
+ callback(Paths::qmlFileInfoPath(canonicalFilePath), DomItem::empty, DomItem::empty);
+ }
}
-Subpath DomItem::subMap(const Map &map) const
+void DomItem::loadFile(QString filePath, QString logicalPath, DomTop::Callback callback,
+ LoadOptions loadOptions)
{
- return Subpath{map.pathFromOwner().last(), DomItem(m_top, m_owner, map)};
+ DomItem topEl = top();
+ if (topEl.internalKind() == DomType::DomEnvironment
+ || topEl.internalKind() == DomType::DomUniverse) {
+ if (auto univ = topEl.ownerAs<DomUniverse>())
+ univ->loadFile(*this, filePath, logicalPath, callback, loadOptions);
+ else if (auto env = topEl.ownerAs<DomEnvironment>()) {
+ if (env->options() & DomEnvironment::Option::NoDependencies)
+ env->loadFile(topEl, filePath, logicalPath, callback, DomTop::Callback(),
+ DomTop::Callback(), loadOptions);
+ else
+ env->loadFile(topEl, filePath, logicalPath, DomTop::Callback(), DomTop::Callback(),
+ callback, loadOptions);
+ } else
+ Q_ASSERT(false && "expected either DomUniverse or DomEnvironment cast to succeed");
+ } else {
+ addError(myErrors().warning(tr("loadFile called without DomEnvironment or DomUniverse.")));
+ callback(Paths::qmlFileInfoPath(filePath), DomItem::empty, DomItem::empty);
+ }
}
-Subpath DomItem::subObjectWrap(const SimpleObjectWrap &obj) const
+void DomItem::loadModuleDependency(QString uri, Version version,
+ std::function<void(Path, DomItem &, DomItem &)> callback,
+ ErrorHandler errorHandler)
{
- return Subpath{obj.pathFromOwner().last(), DomItem(m_top, m_owner, obj)};
+ DomItem topEl = top();
+ if (topEl.internalKind() == DomType::DomEnvironment) {
+ if (auto envPtr = topEl.ownerAs<DomEnvironment>()) {
+ if (envPtr->options() & DomEnvironment::Option::NoDependencies)
+ envPtr->loadModuleDependency(topEl, uri, version, callback, nullptr, errorHandler);
+ else
+ envPtr->loadModuleDependency(topEl, uri, version, nullptr, callback, errorHandler);
+ } else
+ Q_ASSERT(false && "loadDependency expected the DomEnvironment cast to succeed");
+ } else {
+ addError(myErrors().warning(tr("loadModuleDependency called without DomEnvironment.")));
+ callback(Paths::moduleScopePath(uri, version), DomItem::empty, DomItem::empty);
+ }
}
-DomItem::DomItem():
- DomItem(shared_ptr<DomTop>(), shared_ptr<OwningItem>(), Path(), nullptr)
+void DomItem::loadBuiltins(std::function<void(Path, DomItem &, DomItem &)> callback, ErrorHandler h)
{
+ DomItem env = environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>())
+ envPtr->loadBuiltins(env, callback, h);
+ else
+ myErrors().error(tr("Cannot load builtins without DomEnvironment")).handle(h);
}
-DomItem::DomItem(std::shared_ptr<DomEnvironment> envPtr):
- DomItem(envPtr, envPtr, Path(), envPtr.get())
-{}
-
-DomItem::DomItem(std::shared_ptr<DomUniverse> universePtr):
- DomItem(universePtr, universePtr, Path(), universePtr.get())
-{}
-
-DomItem::DomItem(std::shared_ptr<DomTop> top, std::shared_ptr<OwningItem> owner, Path ownerPath, DomBase *base):
- m_top(top), m_owner(owner), m_ownerPath(ownerPath), m_base(base)
+void DomItem::loadPendingDependencies()
{
- if (m_base == nullptr || m_base->kind() == DomType::Empty) { // avoid null ptr, and allow only a single kind of Empty
- m_top.reset();
- m_owner.reset();
- m_base = nullptr;
- }
+ DomItem env = environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>())
+ envPtr->loadPendingDependencies(env);
+ else
+ myErrors().error(tr("Called loadPendingDependencies without environment")).handle();
}
-DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, Map map):
- m_top(top), m_owner(owner), m_base(nullptr),
- inlineEl(map)
-{}
-
-DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, List list):
- m_top(top), m_owner(owner), m_base(nullptr),
- inlineEl(list)
-{}
-
-DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, ConstantData data):
- m_top(top), m_owner(owner), m_base(nullptr),
- inlineEl(data)
-{}
-
-DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, Reference reference):
- m_top(top), m_owner(owner), m_base(nullptr), inlineEl(reference)
-{}
-
-DomItem::DomItem(std::shared_ptr<DomTop> top, std::shared_ptr<OwningItem> owner, SimpleObjectWrap wrapper):
- m_top(top), m_owner(owner), m_base(nullptr), inlineEl(wrapper)
-{}
-
Empty::Empty()
{}
-Path Empty::pathFromOwner(const DomItem &) const
+Path Empty::pathFromOwner(DomItem &) const
{
return Path();
}
-Path Empty::canonicalPath(const DomItem &) const
+Path Empty::canonicalPath(DomItem &) const
{
return Path();
}
-bool Empty::iterateDirectSubpaths(DomItem &, function_ref<bool (Path, DomItem &)>)
+bool Empty::iterateDirectSubpaths(DomItem &, DirectVisitor)
{
return true;
}
-DomItem Empty::containingObject(const DomItem &self) const
+DomItem Empty::containingObject(DomItem &self) const
{
return self;
}
-void Empty::dump(const DomItem &, Sink s, int) const
+void Empty::dump(DomItem &, Sink s, int,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)>) const
{
s(u"null");
}
@@ -1290,27 +2185,31 @@ quintptr Map::id() const
return quintptr(0);
}
-bool Map::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)>visitor)
+bool Map::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
- for (QString k:keys(self)) {
- DomItem el = key(self, k);
- if (!visitor(Path::Key(k), el))
+ QSet<QString> ksSet = keys(self);
+ QStringList ksList = QStringList(ksSet.begin(), ksSet.end());
+ std::sort(ksList.begin(), ksList.end());
+ for (QString k : ksList) {
+ if (!visitor(PathEls::Key(k), [&self, this, k]() { return key(self, k); }))
return false;
}
return true;
}
-const QSet<QString> Map::keys(const DomItem &self) const
+const QSet<QString> Map::keys(DomItem &self) const
{
return m_keys(self);
}
-DomItem Map::key(const DomItem &self, QString name) const
+DomItem Map::key(DomItem &self, QString name) const
{
return m_lookup(self, name);
}
-void DomBase::dump(const DomItem &self, Sink sink, int indent) const
+void DomBase::dump(
+ DomItem &self, Sink sink, int indent,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const
{
bool comma = false;
DomKind dK = self.domKind();
@@ -1372,48 +2271,54 @@ void DomBase::dump(const DomItem &self, Sink sink, int indent) const
}
});
index_type idx = 0;
- iterateDirectSubpathsConst(self, [&comma, &idx, dK, sink, indent, self](Path p, const DomItem &i) {
- if (comma)
- sink(u",");
- else
- comma = true;
- switch (p.headKind()) {
- case Path::Kind::Field:
- sinkNewline(sink, indent + 2);
- if (dK != DomKind::Object)
- sink(u"UNEXPECTED ENTRY ERROR:");
- sinkEscaped(sink, p.headName());
- sink(u":");
- break;
- case Path::Kind::Key:
- sinkNewline(sink, indent + 2);
- if (dK != DomKind::Map)
- sink(u"UNEXPECTED ENTRY ERROR:");
- sinkEscaped(sink, p.headName());
- sink(u":");
- break;
- case Path::Kind::Index:
- sinkNewline(sink, indent + 2);
- if (dK != DomKind::List)
- sink(u"UNEXPECTED ENTRY ERROR:");
- else if (idx++ != p.headIndex())
- sink(u"OUT OF ORDER ARRAY:");
- break;
- default:
- sinkNewline(sink, indent + 2);
- sink(u"UNEXPECTED PATH KIND ERROR (ignored)");
- break;
- }
- DomItem cObj=i.container();
- if (cObj == self) {
- i.dump(sink, indent + 2);
- } else {
- sink(uR"({ "~type~": "Reference", "immediate": true, "referredObjectPath":")");
- i.canonicalPath().dump([sink](QStringView s){ sinkEscaped(sink, s, EscapeOptions::NoOuterQuotes); });
- sink(u"\"}");
- }
- return true;
- });
+ self.iterateDirectSubpaths(
+ [&comma, &idx, dK, sink, indent, &self, filter](const PathEls::PathComponent &c,
+ function_ref<DomItem()> itemF) {
+ DomItem i = itemF();
+ if (!filter(self, c, i))
+ return true;
+ if (comma)
+ sink(u",");
+ else
+ comma = true;
+ switch (c.kind()) {
+ case Path::Kind::Field:
+ sinkNewline(sink, indent + 2);
+ if (dK != DomKind::Object)
+ sink(u"UNEXPECTED ENTRY ERROR:");
+ sinkEscaped(sink, c.name());
+ sink(u":");
+ break;
+ case Path::Kind::Key:
+ sinkNewline(sink, indent + 2);
+ if (dK != DomKind::Map)
+ sink(u"UNEXPECTED ENTRY ERROR:");
+ sinkEscaped(sink, c.name());
+ sink(u":");
+ break;
+ case Path::Kind::Index:
+ sinkNewline(sink, indent + 2);
+ if (dK != DomKind::List)
+ sink(u"UNEXPECTED ENTRY ERROR:");
+ else if (idx++ != c.index())
+ sink(u"OUT OF ORDER ARRAY:");
+ break;
+ default:
+ sinkNewline(sink, indent + 2);
+ sink(u"UNEXPECTED PATH KIND ERROR (ignored)");
+ break;
+ }
+ if (self.isCanonicalChild(i)) {
+ i.dump(sink, indent + 2, filter);
+ } else {
+ sink(uR"({ "~type~": "Reference", "immediate": true, "referredObjectPath":")");
+ i.canonicalPath().dump([sink](QStringView s) {
+ sinkEscaped(sink, s, EscapeOptions::NoOuterQuotes);
+ });
+ sink(u"\"}");
+ }
+ return true;
+ });
}
List::List(Path pathFromOwner, List::LookupFunction lookup, List::Length length,
@@ -1427,66 +2332,70 @@ quintptr List::id() const
return quintptr(0);
}
-void List::dump(const DomItem &self, Sink sink, int indent) const
+void List::dump(
+ DomItem &self, Sink sink, int indent,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const
{
bool first = true;
sink(u"[");
- iterateDirectSubpathsConst(self, [indent, &first, sink](Path, const DomItem &item) {
- if (first)
- first = false;
- else
- sink(u",");
- sinkNewline(sink, indent + 2);
- item.dump(sink, indent + 2);
- return true;
- });
+ const_cast<List *>(this)->iterateDirectSubpaths(
+ self,
+ [&self, indent, &first, sink, filter](const PathEls::PathComponent &c,
+ function_ref<DomItem()> itemF) {
+ DomItem item = itemF();
+ if (!filter(self, c, item))
+ return true;
+ if (first)
+ first = false;
+ else
+ sink(u",");
+ sinkNewline(sink, indent + 2);
+ item.dump(sink, indent + 2, filter);
+ return true;
+ });
sink(u"]");
}
-bool List::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)>visitor)
+bool List::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
if (m_iterator) {
- return m_iterator(self, [visitor](index_type i, DomItem &item){
- return visitor(Path::Index(i), item);
+ return m_iterator(self, [visitor](index_type i, function_ref<DomItem()> itemF) {
+ return visitor(PathEls::Index(i), itemF);
});
}
index_type len = indexes(self);
for (index_type i = 0; i < len; ++i) {
- DomItem idx = index(self, i);
- if (!visitor(Path::Index(i), idx))
+ if (!visitor(PathEls::Index(i), [this, &self, i]() { return index(self, i); }))
return false;
}
return true;
}
-index_type List::indexes(const DomItem &self) const
+index_type List::indexes(DomItem &self) const
{
return m_length(self);
}
-DomItem List::index(const DomItem &self, index_type index) const
+DomItem List::index(DomItem &self, index_type index) const
{
return m_lookup(self, index);
}
-DomElement::DomElement(Path pathFromOwner, const SourceLocation & loc):
- loc(loc), m_pathFromOwner(pathFromOwner)
-{
-}
+DomElement::DomElement(Path pathFromOwner) : m_pathFromOwner(pathFromOwner) { }
-Path DomElement::pathFromOwner(const DomItem &) const
+Path DomElement::pathFromOwner(DomItem &) const
{
Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
return m_pathFromOwner;
}
-Path DomElement::canonicalPath(const DomItem &self) const
+Path DomElement::canonicalPath(DomItem &self) const
{
Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
return self.owner().canonicalPath().path(m_pathFromOwner);
}
-DomItem DomElement::containingObject(const DomItem &self) const
+DomItem DomElement::containingObject(DomItem &self) const
{
Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
return DomBase::containingObject(self);
@@ -1498,13 +2407,35 @@ void DomElement::updatePathFromOwner(Path newPath)
m_pathFromOwner = newPath;
}
-SourceLocation DomElement::location(const DomItem &) const
+bool Reference::shouldCache() const
{
- return loc;
+ for (Path p : referredObjectPath) {
+ switch (p.headKind()) {
+ case Path::Kind::Current:
+ switch (p.headCurrent()) {
+ case PathCurrent::Lookup:
+ case PathCurrent::LookupDynamic:
+ case PathCurrent::LookupStrict:
+ case PathCurrent::ObjChain:
+ case PathCurrent::ScopeChain:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case Path::Kind::Empty:
+ case Path::Kind::Any:
+ case Path::Kind::Filter:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
}
-Reference::Reference(Path referredObject, Path pathFromOwner, const SourceLocation & loc):
- DomElement(pathFromOwner, loc), referredObjectPath(referredObject)
+Reference::Reference(Path referredObject, Path pathFromOwner, const SourceLocation &)
+ : DomElement(pathFromOwner), referredObjectPath(referredObject)
{
}
@@ -1513,44 +2444,149 @@ quintptr Reference::id() const
return quintptr(0);
}
-
-bool Reference::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool Reference::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
- if (!self.subDataField(Fields::referredObjectPath, referredObjectPath.toString()).visit(visitor))
- return false;
- DomItem res = get(self);
- if (!visitor(Path::Field(Fields::get), res))
- return false;
- return true;
+ bool cont = true;
+ cont = cont && self.dvValueLazyField(visitor, Fields::referredObjectPath, [this]() {
+ return referredObjectPath.toString();
+ });
+ cont = cont
+ && self.dvItemField(visitor, Fields::get, [this, &self]() { return this->get(self); });
+ return cont;
}
-DomItem Reference::field(const DomItem &self, QStringView name) const
+DomItem Reference::field(DomItem &self, QStringView name) const
{
if (Fields::referredObjectPath == name)
- return self.subDataField(Fields::referredObjectPath, referredObjectPath.toString()).item;
+ return self.subDataItemField(Fields::referredObjectPath, referredObjectPath.toString());
if (Fields::get == name)
return get(self);
return DomItem();
}
-const QList<QString> Reference::fields(const DomItem &) const
+QList<QString> Reference::fields(DomItem &) const
{
return QList<QString>({QString::fromUtf16(Fields::referredObjectPath), QString::fromUtf16(Fields::get)});
}
-DomItem Reference::index(const DomItem &, index_type) const {
+DomItem Reference::index(DomItem &, index_type) const
+{
return DomItem();
}
-DomItem Reference::key(const DomItem &, QString) const {
+DomItem Reference::key(DomItem &, QString) const
+{
return DomItem();
}
-DomItem Reference::get(const DomItem &self) const
+DomItem Reference::get(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) const
{
- if (!referredObjectPath)
- return DomItem();
- return self[referredObjectPath];
+ DomItem res;
+ if (referredObjectPath) {
+ DomItem env;
+ Path selfPath;
+ Path cachedPath;
+ if (shouldCache()) {
+ env = self.environment();
+ selfPath = self.canonicalPath();
+ RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
+ switch (cached.cached) {
+ case RefCacheEntry::Cached::None:
+ break;
+ case RefCacheEntry::Cached::First:
+ case RefCacheEntry::Cached::All:
+ if (!cached.canonicalPaths.isEmpty())
+ cachedPath = cached.canonicalPaths.first();
+ else
+ return res;
+ break;
+ }
+ if (cachedPath) {
+ res = env.path(cachedPath);
+ if (!res)
+ qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
+ << " leads to invalid path " << cachedPath;
+ else
+ return res;
+ }
+ }
+ QList<Path> visitedRefsLocal;
+ self.resolve(
+ referredObjectPath,
+ [&res](Path, DomItem &el) {
+ res = el;
+ return false;
+ },
+ h, ResolveOption::None, referredObjectPath,
+ (visitedRefs ? visitedRefs : &visitedRefsLocal));
+ if (env)
+ RefCacheEntry::addForPath(
+ env, selfPath, RefCacheEntry { RefCacheEntry::Cached::First, { cachedPath } });
+ }
+ return res;
+}
+
+QList<DomItem> Reference::getAll(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) const
+{
+ QList<DomItem> res;
+ if (referredObjectPath) {
+ DomItem env;
+ Path selfPath;
+ QList<Path> cachedPaths;
+ if (shouldCache()) {
+ selfPath = canonicalPath(self);
+ env = self.environment();
+ RefCacheEntry cached = RefCacheEntry::forPath(env, selfPath);
+ switch (cached.cached) {
+ case RefCacheEntry::Cached::None:
+ case RefCacheEntry::Cached::First:
+ break;
+ case RefCacheEntry::Cached::All:
+ cachedPaths += cached.canonicalPaths;
+ if (cachedPaths.isEmpty())
+ return res;
+ }
+ }
+ if (!cachedPaths.isEmpty()) {
+ bool outdated = false;
+ for (Path p : cachedPaths) {
+ DomItem newEl = env.path(p);
+ if (!newEl) {
+ outdated = true;
+ qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
+ << " leads to invalid path " << p;
+ break;
+ } else {
+ res.append(newEl);
+ }
+ }
+ if (outdated) {
+ res.clear();
+ } else {
+ return res;
+ }
+ }
+ self.resolve(
+ referredObjectPath,
+ [&res](Path, DomItem &el) {
+ res.append(el);
+ return true;
+ },
+ h, ResolveOption::None, referredObjectPath, visitedRefs);
+ if (env) {
+ QList<Path> canonicalPaths;
+ for (DomItem i : res) {
+ if (i)
+ canonicalPaths.append(i.canonicalPath());
+ else
+ qCWarning(refLog)
+ << "getAll of reference at " << selfPath << " visits empty items.";
+ }
+ RefCacheEntry::addForPath(env, selfPath,
+ RefCacheEntry { RefCacheEntry::Cached::All, canonicalPaths });
+ }
+ }
+ return res;
}
/*!
@@ -1603,38 +2639,35 @@ int OwningItem::nextRevision()
return ++nextRev;
}
-bool OwningItem::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool OwningItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
bool cont = true;
- QMultiMap<Path, ErrorMessage> myErrors = localErrors();
- cont = cont && self.subMap(
- Map(
- self.pathFromOwner().field(Fields::errors),
- [myErrors](const DomItem &map, QString key) {
- auto it = myErrors.find(Path::fromString(key));
- if (it != myErrors.end())
- return map.subDataKey(
- key, it->toCbor(),
- ConstantData::Options::FirstMapIsFields, it->location).item;
- else
- return DomItem();
- }, [myErrors](const DomItem &) {
- QSet<QString> res;
- auto it = myErrors.keyBegin();
- auto end = myErrors.keyEnd();
- while (it != end)
- res.insert(it++->toString());
- return res;
- }, QLatin1String("ErrorMessages"))).visit(visitor);
+ cont = cont && self.dvItemField(visitor, Fields::errors, [&self, this]() {
+ QMultiMap<Path, ErrorMessage> myErrors = localErrors();
+ return self.subMapItem(Map(
+ self.pathFromOwner().field(Fields::errors),
+ [myErrors](DomItem &map, QString key) {
+ auto it = myErrors.find(Path::fromString(key));
+ if (it != myErrors.end())
+ return map.subDataItem(PathEls::Key(key), it->toCbor(),
+ ConstantData::Options::FirstMapIsFields);
+ else
+ return DomItem();
+ },
+ [myErrors](DomItem &) {
+ QSet<QString> res;
+ auto it = myErrors.keyBegin();
+ auto end = myErrors.keyEnd();
+ while (it != end)
+ res.insert(it++->toString());
+ return res;
+ },
+ QLatin1String("ErrorMessages")));
+ });
return cont;
}
-Path OwningItem::pathFromOwner(const DomItem &) const
-{
- return Path();
-}
-
-DomItem OwningItem::containingObject(const DomItem &self) const
+DomItem OwningItem::containingObject(DomItem &self) const
{
Source s = self.canonicalPath().split();
if (s.pathFromSource) {
@@ -1692,7 +2725,7 @@ void OwningItem::refreshedDataAt(QDateTime tNew)
m_lastDataUpdateAt = tNew;
}
-void OwningItem::addError(const DomItem &, ErrorMessage msg)
+void OwningItem::addError(DomItem &, ErrorMessage msg)
{
addErrorLocal(msg);
}
@@ -1720,7 +2753,7 @@ void OwningItem::clearErrors(ErrorGroups groups)
}
}
-bool OwningItem::iterateErrors(const DomItem &self, function_ref<bool (DomItem, ErrorMessage)> visitor,
+bool OwningItem::iterateErrors(DomItem &self, function_ref<bool(DomItem, ErrorMessage)> visitor,
Path inPath)
{
QMultiMap<Path, ErrorMessage> myErrors;
@@ -1731,144 +2764,344 @@ bool OwningItem::iterateErrors(const DomItem &self, function_ref<bool (DomItem,
auto it = myErrors.lowerBound(inPath);
auto end = myErrors.end();
while (it != end && it.key().mid(0, inPath.length()) == inPath) {
- if (!visitor(self, *it))
+ if (!visitor(self, *it++))
return false;
}
return true;
}
-bool OwningItem::iterateSubOwners(const DomItem &self, function_ref<bool (const DomItem &)>visitor)
+bool OwningItem::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor)
{
- return iterateDirectSubpathsConst(self,[self, visitor](Path, const DomItem &i) {
- if (i.owningItemPtr() != self.owningItemPtr() && i.container().id() == self.id())
- return visitor(i);
+ return self.iterateDirectSubpaths(
+ [&self, visitor](const PathEls::PathComponent &, function_ref<DomItem()> iF) {
+ DomItem i = iF();
+ if (i.owningItemPtr() != self.owningItemPtr()) {
+ DomItem container = i.container();
+ if (container.id() == self.id())
+ return visitor(i);
+ }
+ return true;
+ });
+}
+
+bool operator==(const DomItem &o1c, const DomItem &o2c)
+{
+ DomItem &o1 = *const_cast<DomItem *>(&o1c);
+ DomItem &o2 = *const_cast<DomItem *>(&o2c);
+ if (o1.m_kind != o2.m_kind)
+ return false;
+ return o1.visitMutableEl([&o1, &o2](auto &&el1) {
+ auto &&el2 = std::get<std::decay_t<decltype(el1)>>(o2.m_element);
+ auto id1 = el1->id();
+ auto id2 = el2->id();
+ if (id1 != id2)
+ return false;
+ if (id1 != quintptr(0))
+ return true;
+ if (o1.m_owner != o2.m_owner)
+ return false;
+ Path p1 = el1->pathFromOwner(o1);
+ Path p2 = el2->pathFromOwner(o2);
+ if (p1 != p2)
+ return false;
return true;
});
}
-GenericObject GenericObject::copy() const
+ErrorHandler MutableDomItem::errorHandler()
{
- QMap<QString, GenericObject> newObjs;
- auto objs = subObjects;
- auto itO = objs.cbegin();
- auto endO = objs.cend();
- while (itO != endO) {
- newObjs.insert(itO.key(),itO->copy());
- ++itO;
- }
- return GenericObject(pathFromOwner(), loc, newObjs, subValues);
+ MutableDomItem self;
+ return [&self](ErrorMessage m) { self.addError(m); };
}
-std::pair<QString, GenericObject> GenericObject::asStringPair() const
+MutableDomItem MutableDomItem::addPrototypePath(Path prototypePath)
{
- return std::make_pair(pathFromOwner().last().headName(), *this);
+ if (QmlObject *el = mutableAs<QmlObject>()) {
+ return path(el->addPrototypePath(prototypePath));
+ } else {
+ Q_ASSERT(false && "setPrototypePath on non qml scope");
+ return {};
+ }
}
-bool GenericObject::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+MutableDomItem MutableDomItem::setNextScopePath(Path nextScopePath)
{
- bool cont = true;
- auto itV = subValues.begin();
- auto endV = subValues.end();
- while (itV != endV) {
- cont = cont && self.subDataField(itV.key(), *itV).visit(visitor);
- ++itV;
- }
- auto itO = subObjects.begin();
- auto endO = subObjects.end();
- while (itO != endO) {
- cont = cont && self.copy(&(*itO)).toSubField(itO.key()).visit(visitor);
- ++itO;
+ if (QmlObject *el = mutableAs<QmlObject>()) {
+ el->setNextScopePath(nextScopePath);
+ return field(Fields::nextScope);
+ } else {
+ Q_ASSERT(false && "setNextScopePath on non qml scope");
+ return {};
}
- return cont;
}
-std::shared_ptr<OwningItem> GenericOwner::doCopy(const DomItem &) const
+MutableDomItem MutableDomItem::setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs)
{
- return std::make_shared<GenericOwner>(*this);
+ if (QmlObject *el = mutableAs<QmlObject>()) {
+ el->setPropertyDefs(propertyDefs);
+ return field(Fields::propertyDefs);
+ } else {
+ Q_ASSERT(false && "setPropertyDefs on non qml scope");
+ return {};
+ }
}
-GenericOwner::GenericOwner(const GenericOwner &o):
- OwningItem(o), pathFromTop(o.pathFromTop),
- subValues(o.subValues)
+MutableDomItem MutableDomItem::setBindings(QMultiMap<QString, Binding> bindings)
{
- auto objs = o.subObjects;
- auto itO = objs.cbegin();
- auto endO = objs.cend();
- while (itO != endO) {
- subObjects.insert(itO.key(),itO->copy());
- ++itO;
+ if (QmlObject *el = mutableAs<QmlObject>()) {
+ el->setBindings(bindings);
+ return field(Fields::bindings);
+ } else {
+ Q_ASSERT(false && "setBindings on non qml scope");
+ return {};
}
}
-std::shared_ptr<GenericOwner> GenericOwner::makeCopy(const DomItem &self)
+MutableDomItem MutableDomItem::setMethods(QMultiMap<QString, MethodInfo> functionDefs)
{
- return std::static_pointer_cast<GenericOwner>(doCopy(self));
+ if (QmlObject *el = mutableAs<QmlObject>())
+ el->setMethods(functionDefs);
+ else
+ Q_ASSERT(false && "setMethods on non qml scope");
+ return {};
+}
+
+MutableDomItem MutableDomItem::setChildren(QList<QmlObject> children)
+{
+ if (QmlObject *el = mutableAs<QmlObject>()) {
+ el->setChildren(children);
+ return field(Fields::children);
+ } else
+ Q_ASSERT(false && "setChildren on non qml scope");
+ return {};
+}
+
+MutableDomItem MutableDomItem::setAnnotations(QList<QmlObject> annotations)
+{
+ if (QmlObject *el = mutableAs<QmlObject>())
+ el->setAnnotations(annotations);
+ else if (Binding *el = mutableAs<Binding>()) {
+ el->setAnnotations(annotations);
+ el->updatePathFromOwner(pathFromOwner());
+ } else if (PropertyDefinition *el = mutableAs<PropertyDefinition>()) {
+ el->annotations = annotations;
+ el->updatePathFromOwner(pathFromOwner());
+ } else if (MethodInfo *el = mutableAs<MethodInfo>()) {
+ el->annotations = annotations;
+ el->updatePathFromOwner(pathFromOwner());
+ } else if (EnumDecl *el = mutableAs<EnumDecl>()) {
+ el->setAnnotations(annotations);
+ el->updatePathFromOwner(pathFromOwner());
+ } else if (!annotations.isEmpty()) {
+ Q_ASSERT(false && "setAnnotations on element not supporting them");
+ }
+ return field(Fields::annotations);
}
-
-Path GenericOwner::canonicalPath(const DomItem &) const
+MutableDomItem MutableDomItem::setScript(std::shared_ptr<ScriptExpression> exp)
{
- return pathFromTop;
+ switch (internalKind()) {
+ case DomType::Binding:
+ if (Binding *b = mutableAs<Binding>()) {
+ b->setValue(std::make_unique<BindingValue>(exp));
+ return field(Fields::value);
+ }
+ break;
+ case DomType::MethodInfo:
+ if (MethodInfo *m = mutableAs<MethodInfo>()) {
+ m->body = exp;
+ return field(Fields::body);
+ }
+ break;
+ case DomType::MethodParameter:
+ if (MethodParameter *p = mutableAs<MethodParameter>()) {
+ p->defaultValue = exp;
+ return field(Fields::body);
+ }
+ break;
+ case DomType::ScriptExpression:
+ return container().setScript(exp);
+ default:
+ qCWarning(domLog) << "setScript called on" << internalKindStr();
+ Q_ASSERT_X(false, "setScript",
+ "setScript supported only on Binding, MethodInfo, MethodParameter, and "
+ "ScriptExpression contained in them");
+ }
+ return MutableDomItem();
}
-bool GenericOwner::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+MutableDomItem MutableDomItem::setCode(QString code)
{
- bool cont = true;
- auto itV = subValues.begin();
- auto endV = subValues.end();
- while (itV != endV) {
- cont = cont && self.subDataField(itV.key(), *itV).visit(visitor);
- ++itV;
- }
- auto itO = subObjects.begin();
- auto endO = subObjects.end();
- while (itO != endO) {
- cont = cont && self.copy(&(*itO)).toSubField(itO.key()).visit(visitor);
- ++itO;
+ DomItem it = item();
+ switch (it.internalKind()) {
+ case DomType::Binding:
+ if (Binding *b = mutableAs<Binding>()) {
+ std::shared_ptr<ScriptExpression> exp(new ScriptExpression(
+ code, ScriptExpression::ExpressionType::BindingExpression));
+ b->setValue(std::make_unique<BindingValue>(exp));
+ return field(Fields::value);
+ }
+ break;
+ case DomType::MethodInfo:
+ if (MethodInfo *m = mutableAs<MethodInfo>()) {
+ QString pre = m->preCode(it);
+ QString post = m->preCode(it);
+ m->body = std::shared_ptr<ScriptExpression>(new ScriptExpression(
+ code, ScriptExpression::ExpressionType::FunctionBody, 0, pre, post));
+ return field(Fields::body);
+ }
+ break;
+ case DomType::MethodParameter:
+ if (MethodParameter *p = mutableAs<MethodParameter>()) {
+ p->defaultValue = std::shared_ptr<ScriptExpression>(
+ new ScriptExpression(code, ScriptExpression::ExpressionType::ArgInitializer));
+ return field(Fields::defaultValue);
+ }
+ break;
+ case DomType::ScriptExpression:
+ if (std::shared_ptr<ScriptExpression> exp = ownerAs<ScriptExpression>()) {
+ std::shared_ptr<ScriptExpression> newExp = exp->copyWithUpdatedCode(it, code);
+ return container().setScript(newExp);
+ }
+ break;
+ default:
+ qCWarning(domLog) << "setCode called on" << internalKindStr();
+ Q_ASSERT_X(
+ false, "setCode",
+ "setCode supported only on Binding, MethodInfo, MethodParameter, ScriptExpression");
}
- return cont;
+ return MutableDomItem();
}
-bool operator ==(const DomItem &o1, const DomItem &o2) {
- if (o1.base() == o2.base())
- return true;
- quintptr i1 = o1.id();
- quintptr i2 = o2.id();
- if (i1 != i2)
- return false;
- if (i1 != quintptr(0))
- return true;
- Path p1 = o1.pathFromOwner();
- Path p2 = o2.pathFromOwner();
- if (p1 != p2)
- return false;
- return o1.owningItemPtr() == o2.owningItemPtr();
+MutableDomItem MutableDomItem::addPropertyDef(PropertyDefinition propertyDef, AddOption option)
+{
+ if (QmlObject *el = mutableAs<QmlObject>())
+ return el->addPropertyDef(*this, propertyDef, option);
+ else
+ Q_ASSERT(false && "addPropertyDef on non qml scope");
+ return MutableDomItem();
}
-QString MutableDomItem::name() const
+MutableDomItem MutableDomItem::addBinding(Binding binding, AddOption option)
{
- return base().name();
+ if (QmlObject *el = mutableAs<QmlObject>())
+ return el->addBinding(*this, binding, option);
+ else
+ Q_ASSERT(false && "addBinding on non qml scope");
+ return MutableDomItem();
}
-ErrorHandler MutableDomItem::errorHandler() const
+MutableDomItem MutableDomItem::addMethod(MethodInfo functionDef, AddOption option)
{
- MutableDomItem self;
- return [self](ErrorMessage m){
- self.addError(m);
- };
+ if (QmlObject *el = mutableAs<QmlObject>())
+ return el->addMethod(*this, functionDef, option);
+ else
+ Q_ASSERT(false && "addMethod on non qml scope");
+ return MutableDomItem();
+}
+
+MutableDomItem MutableDomItem::addChild(QmlObject child)
+{
+ if (QmlObject *el = mutableAs<QmlObject>()) {
+ return el->addChild(*this, child);
+ } else if (QmlComponent *el = mutableAs<QmlComponent>()) {
+ Path p = el->addObject(child);
+ return owner().path(p); // convenience: treat component objects as children
+ } else {
+ Q_ASSERT(false && "addChild on non qml scope");
+ }
+ return MutableDomItem();
+}
+
+MutableDomItem MutableDomItem::addAnnotation(QmlObject annotation)
+{
+ Path res;
+ switch (internalKind()) {
+ case DomType::QmlObject: {
+ QmlObject *el = mutableAs<QmlObject>();
+ res = el->addAnnotation(annotation);
+ } break;
+ case DomType::Binding: {
+ Binding *el = mutableAs<Binding>();
+
+ res = el->addAnnotation(m_pathFromOwner, annotation);
+ } break;
+ case DomType::PropertyDefinition: {
+ PropertyDefinition *el = mutableAs<PropertyDefinition>();
+ res = el->addAnnotation(m_pathFromOwner, annotation);
+ } break;
+ case DomType::MethodInfo: {
+ MethodInfo *el = mutableAs<MethodInfo>();
+ res = el->addAnnotation(m_pathFromOwner, annotation);
+ } break;
+ case DomType::Id: {
+ Id *el = mutableAs<Id>();
+ res = el->addAnnotation(m_pathFromOwner, annotation);
+ } break;
+ default:
+ Q_ASSERT(false && "addAnnotation on element not supporting them");
+ }
+ return MutableDomItem(owner().item(), res);
+}
+
+MutableDomItem MutableDomItem::addPreComment(const Comment &comment, QString regionName)
+{
+ index_type idx;
+ MutableDomItem rC = field(Fields::comments);
+ if (auto rcPtr = rC.mutableAs<RegionComments>()) {
+ auto &preList = rcPtr->regionComments[regionName].preComments;
+ idx = preList.length();
+ preList.append(comment);
+ MutableDomItem res = path(Path::Field(Fields::comments)
+ .field(Fields::regionComments)
+ .key(regionName)
+ .field(Fields::preComments)
+ .index(idx));
+ Q_ASSERT(res);
+ return res;
+ }
+ return MutableDomItem();
+}
+
+MutableDomItem MutableDomItem::addPostComment(const Comment &comment, QString regionName)
+{
+ index_type idx;
+ MutableDomItem rC = field(Fields::comments);
+ if (auto rcPtr = rC.mutableAs<RegionComments>()) {
+ auto &postList = rcPtr->regionComments[regionName].postComments;
+ idx = postList.length();
+ postList.append(comment);
+ MutableDomItem res = path(Path::Field(Fields::comments)
+ .field(Fields::regionComments)
+ .key(regionName)
+ .field(Fields::postComments)
+ .index(idx));
+ Q_ASSERT(res);
+ return res;
+ }
+ return MutableDomItem();
}
QDebug operator<<(QDebug debug, const DomItem &c)
{
- dumperToQDebug([&c](Sink s) {
- c.dump(s);
- }, debug);
+ dumperToQDebug([&c](Sink s) { const_cast<DomItem *>(&c)->dump(s); }, debug);
return debug;
}
QDebug operator<<(QDebug debug, const MutableDomItem &c)
{
- return debug.noquote().nospace() << "MutableDomItem(" << domTypeToString(c.internalKind())
- << ", " << c.canonicalPath().toString() << ")";
+ MutableDomItem cc(c);
+ return debug.noquote().nospace() << "MutableDomItem(" << domTypeToString(cc.internalKind())
+ << ", " << cc.canonicalPath().toString() << ")";
+}
+
+bool ListPBase::iterateDirectSubpaths(DomItem &self, DirectVisitor v)
+{
+ index_type len = index_type(m_pList.size());
+ for (index_type i = 0; i < len; ++i) {
+ if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, i); }))
+ return false;
+ }
+ return true;
}
} // end namespace Dom
diff --git a/src/qmldom/qqmldomitem_p.h b/src/qmldom/qqmldomitem_p.h
index 9fc73e4486..f94344e266 100644
--- a/src/qmldom/qqmldomitem_p.h
+++ b/src/qmldom/qqmldomitem_p.h
@@ -52,13 +52,14 @@
#include "qqmldom_global.h"
#include "qqmldom_fwd_p.h"
#include "qqmldomconstants_p.h"
-#include "qqmldomfunctionref_p.h"
#include "qqmldomstringdumper_p.h"
#include "qqmldompath_p.h"
#include "qqmldomerrormessage_p.h"
+#include "qqmldomfunctionref_p.h"
#include <QtCore/QMap>
#include <QtCore/QMultiMap>
+#include <QtCore/QSet>
#include <QtCore/QString>
#include <QtCore/QStringView>
#include <QtCore/QDebug>
@@ -70,6 +71,10 @@
#include <memory>
#include <typeinfo>
#include <utility>
+#include <type_traits>
+#include <variant>
+#include <optional>
+#include <cstddef>
QT_BEGIN_NAMESPACE
@@ -79,19 +84,143 @@ namespace Dom {
class Path;
-bool domTypeIsObjWrap(DomType k);
-bool domTypeIsDomElement(DomType);
-bool domTypeIsOwningItem(DomType);
-bool domTypeIsExternalItem(DomType k);
-bool domTypeIsTopItem(DomType k);
-bool domTypeIsContainer(DomType k);
-bool domTypeCanBeInline(DomType k);
+constexpr bool domTypeIsObjWrap(DomType k);
+constexpr bool domTypeIsValueWrap(DomType k);
+constexpr bool domTypeIsDomElement(DomType);
+constexpr bool domTypeIsOwningItem(DomType);
+constexpr bool domTypeIsUnattachedOwningItem(DomType);
+QMLDOM_EXPORT bool domTypeIsExternalItem(DomType k);
+QMLDOM_EXPORT bool domTypeIsTopItem(DomType k);
+QMLDOM_EXPORT bool domTypeIsContainer(DomType k);
+constexpr bool domTypeCanBeInline(DomType k)
+{
+ switch (k) {
+ case DomType::Empty:
+ case DomType::Map:
+ case DomType::List:
+ case DomType::ListP:
+ case DomType::ConstantData:
+ case DomType::SimpleObjectWrap:
+ case DomType::Reference:
+ return true;
+ default:
+ return false;
+ }
+}
+QMLDOM_EXPORT bool domTypeIsScope(DomType k);
QMLDOM_EXPORT QMap<DomType,QString> domTypeToStringMap();
QMLDOM_EXPORT QString domTypeToString(DomType k);
+QMLDOM_EXPORT QMap<DomKind, QString> domKindToStringMap();
+QMLDOM_EXPORT QString domKindToString(DomKind k);
QMLDOM_EXPORT QCborValue locationToData(SourceLocation loc, QStringView strValue=u"");
+inline bool noFilter(DomItem &, const PathEls::PathComponent &, DomItem &)
+{
+ return true;
+}
+
+using DirectVisitor = function_ref<bool(const PathEls::PathComponent &, function_ref<DomItem()>)>;
+// using DirectVisitor = function_ref<bool(Path, DomItem &)>;
+
+namespace {
+template<typename T>
+struct IsMultiMap : std::false_type
+{
+};
+
+template<typename Key, typename T>
+struct IsMultiMap<QMultiMap<Key, T>> : std::true_type
+{
+};
+
+template<typename T>
+struct IsMap : std::false_type
+{
+};
+
+template<typename Key, typename T>
+struct IsMap<QMap<Key, T>> : std::true_type
+{
+};
+
+template<typename... Ts>
+using void_t = void;
+
+template<typename T, typename = void>
+struct IsDomObject : std::false_type
+{
+};
+
+template<typename T>
+struct IsDomObject<T, void_t<decltype(T::kindValue)>> : std::true_type
+{
+};
+
+template<typename T, typename = void>
+struct IsInlineDom : std::false_type
+{
+};
+
+template<typename T>
+struct IsInlineDom<T, void_t<decltype(T::kindValue)>>
+ : std::integral_constant<bool, domTypeCanBeInline(T::kindValue)>
+{
+};
+
+template<typename T>
+struct IsInlineDom<T *, void_t<decltype(T::kindValue)>> : std::true_type
+{
+};
+
+template<typename T>
+struct IsInlineDom<std::shared_ptr<T>, void_t<decltype(T::kindValue)>> : std::true_type
+{
+};
+
+template<typename T>
+struct IsSharedPointerToDomObject : std::false_type
+{
+};
+
+template<typename T>
+struct IsSharedPointerToDomObject<std::shared_ptr<T>> : IsDomObject<T>
+{
+};
+
+template<typename T, typename = void>
+struct IsList : std::false_type
+{
+};
+
+template<typename T>
+struct IsList<T, void_t<typename T::value_type>> : std::true_type
+{
+};
+
+}
+
+template<typename T>
+union SubclassStorage {
+ int i;
+ T lp;
+ T *data() { return reinterpret_cast<T *>(this); }
+ const T *data() const { return reinterpret_cast<const T *>(this); }
+ SubclassStorage() { }
+ SubclassStorage(T &&el) { el.moveTo(data()); }
+ SubclassStorage(const T *el) { el->copyTo(data()); }
+ SubclassStorage(const SubclassStorage &o) : SubclassStorage(o.data()) { }
+ SubclassStorage(const SubclassStorage &&o) : SubclassStorage(o.data()) { }
+ SubclassStorage &operator=(const SubclassStorage &o)
+ {
+ data()->~T();
+ o.data()->copyTo(data());
+ return *this;
+ }
+ ~SubclassStorage() { data()->~T(); }
+};
+
class QMLDOM_EXPORT DomBase{
public:
virtual ~DomBase() = default;
@@ -99,32 +228,37 @@ public:
// minimal overload set:
virtual DomType kind() const = 0;
virtual DomKind domKind() const;
- virtual Path pathFromOwner(const DomItem &self) const = 0;
- virtual Path canonicalPath(const DomItem &self) const = 0;
- virtual bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) = 0; // iterates the *direct* subpaths, returns false if a quick end was requested
- bool iterateDirectSubpathsConst(const DomItem &self, function_ref<bool(Path, const DomItem &)>) const; // iterates the *direct* subpaths, returns false if a quick end was requested
-
- virtual DomItem containingObject(const DomItem &self) const; // the DomItem corresponding to the canonicalSource source
- virtual void dump(const DomItem &, Sink sink, int indent) const;
+ virtual Path pathFromOwner(DomItem &self) const = 0;
+ virtual Path canonicalPath(DomItem &self) const = 0;
+ virtual bool
+ iterateDirectSubpaths(DomItem &self,
+ DirectVisitor visitor) = 0; // iterates the *direct* subpaths, returns
+ // false if a quick end was requested
+ bool iterateDirectSubpathsConst(DomItem &self, DirectVisitor)
+ const; // iterates the *direct* subpaths, returns false if a quick end was requested
+
+ virtual DomItem containingObject(
+ DomItem &self) const; // the DomItem corresponding to the canonicalSource source
+ virtual void
+ dump(DomItem &, Sink sink, int indent,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const;
virtual quintptr id() const;
- virtual QString typeName() const;
+ QString typeName() const;
- virtual QList<QString> const fields(const DomItem &self) const;
- virtual DomItem field(const DomItem &self, QStringView name) const;
+ virtual QList<QString> fields(DomItem &self) const;
+ virtual DomItem field(DomItem &self, QStringView name) const;
- virtual index_type indexes(const DomItem &self) const;
- virtual DomItem index(const DomItem &self, index_type index) const;
+ virtual index_type indexes(DomItem &self) const;
+ virtual DomItem index(DomItem &self, index_type index) const;
- virtual QSet<QString> const keys(const DomItem &self) const;
- virtual DomItem key(const DomItem &self, QString name) const;
+ virtual QSet<QString> const keys(DomItem &self) const;
+ virtual DomItem key(DomItem &self, QString name) const;
- virtual QString canonicalFilePath(const DomItem &self) const;
- virtual SourceLocation location(const DomItem &self) const;
+ virtual QString canonicalFilePath(DomItem &self) const;
virtual QCborValue value() const {
return QCborValue();
}
-
};
inline DomKind kind2domKind(DomType k)
@@ -133,6 +267,7 @@ inline DomKind kind2domKind(DomType k)
case DomType::Empty:
return DomKind::Empty;
case DomType::List:
+ case DomType::ListP:
return DomKind::List;
case DomType::Map:
return DomKind::Map;
@@ -143,80 +278,113 @@ inline DomKind kind2domKind(DomType k)
}
}
-class QMLDOM_EXPORT Empty: public DomBase {
+class QMLDOM_EXPORT Empty final : public DomBase
+{
public:
constexpr static DomType kindValue = DomType::Empty;
DomType kind() const override { return kindValue; }
+ Empty *operator->() { return this; }
+ const Empty *operator->() const { return this; }
+ Empty &operator*() { return *this; }
+ const Empty &operator*() const { return *this; }
+
Empty();
- Path pathFromOwner(const DomItem &self) const override;
- Path canonicalPath(const DomItem &self) const override;
- DomItem containingObject(const DomItem &self) const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)>) override;
- void dump(const DomItem &, Sink s, int indent) const override;
+ quintptr id() const override { return ~quintptr(0); }
+ Path pathFromOwner(DomItem &self) const override;
+ Path canonicalPath(DomItem &self) const override;
+ DomItem containingObject(DomItem &self) const override;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ void dump(DomItem &, Sink s, int indent,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter)
+ const override;
};
class QMLDOM_EXPORT DomElement: public DomBase {
protected:
DomElement& operator=(const DomElement&) = default;
public:
- DomElement(Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation());
+ DomElement(Path pathFromOwner = Path());
DomElement(const DomElement &o) = default;
- Path pathFromOwner(const DomItem &self) const override;
+ Path pathFromOwner(DomItem &self) const override;
Path pathFromOwner() const { return m_pathFromOwner; }
- Path canonicalPath(const DomItem &self) const override;
- DomItem containingObject(const DomItem &self) const override;
+ Path canonicalPath(DomItem &self) const override;
+ DomItem containingObject(DomItem &self) const override;
virtual void updatePathFromOwner(Path newPath);
- SourceLocation location(const DomItem &self) const override;
-
- SourceLocation loc;
private:
Path m_pathFromOwner;
};
-class QMLDOM_EXPORT Map: public DomElement {
+class QMLDOM_EXPORT Map final : public DomElement
+{
public:
constexpr static DomType kindValue = DomType::Map;
DomType kind() const override { return kindValue; }
- using LookupFunction = std::function<DomItem (const DomItem&, QString)>;
- using Keys = std::function<QSet<QString> (const DomItem &)>;
+ Map *operator->() { return this; }
+ const Map *operator->() const { return this; }
+ Map &operator*() { return *this; }
+ const Map &operator*() const { return *this; }
+
+ using LookupFunction = std::function<DomItem(DomItem &, QString)>;
+ using Keys = std::function<QSet<QString>(DomItem &)>;
Map(Path pathFromOwner, LookupFunction lookup, Keys keys, QString targetType);
quintptr id() const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
- QSet<QString> const keys(const DomItem &self) const override;
- DomItem key(const DomItem &self, QString name) const override;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ QSet<QString> const keys(DomItem &self) const override;
+ DomItem key(DomItem &self, QString name) const override;
+
+ template<typename T>
+ static Map fromMultiMapRef(
+ Path pathFromOwner, QMultiMap<QString, T> &mmap,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &c, T &)> elWrapper);
+ template<typename T>
+ static Map
+ fromMapRef(Path pathFromOwner, QMap<QString, T> &mmap,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper);
- template <typename T>
- static Map fromMultiMapRef(Path pathFromOwner, QMultiMap<QString,T> &mmap, std::function<DomItem(const DomItem &, Path, T&)> elWrapper);
- template <typename T>
- static Map fromMapRef(Path pathFromOwner, QMap<QString,T> &mmap, std::function<DomItem(const DomItem &, Path, T&)> elWrapper);
private:
LookupFunction m_lookup;
Keys m_keys;
QString m_targetType;
};
-class QMLDOM_EXPORT List: public DomElement {
+class QMLDOM_EXPORT List final : public DomElement
+{
public:
constexpr static DomType kindValue = DomType::List;
DomType kind() const override { return kindValue; }
- using LookupFunction = std::function<DomItem (const DomItem &, index_type)>;
- using Length = std::function<index_type (const DomItem &)>;
- using IteratorFunction = std::function<bool (const DomItem &, function_ref<bool(index_type,DomItem &)>)>;
+ List *operator->() { return this; }
+ const List *operator->() const { return this; }
+ List &operator*() { return *this; }
+ const List &operator*() const { return *this; }
+
+ using LookupFunction = std::function<DomItem(DomItem &, index_type)>;
+ using Length = std::function<index_type(DomItem &)>;
+ using IteratorFunction =
+ std::function<bool(DomItem &, function_ref<bool(index_type, function_ref<DomItem()>)>)>;
List(Path pathFromOwner, LookupFunction lookup, Length length, IteratorFunction iterator, QString elType);
quintptr id() const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
- void dump(const DomItem &, Sink s, int indent) const override;
- index_type indexes(const DomItem &self) const override;
- DomItem index(const DomItem &self, index_type index) const override;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ void
+ dump(DomItem &, Sink s, int indent,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)>) const override;
+ index_type indexes(DomItem &self) const override;
+ DomItem index(DomItem &self, index_type index) const override;
template<typename T>
- static List fromQList(Path pathFromOwner, QList<T> list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options = ListOptions::Normal);
+ static List
+ fromQList(Path pathFromOwner, QList<T> list,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ ListOptions options = ListOptions::Normal);
template<typename T>
- static List fromQListRef(Path pathFromOwner, QList<T> &list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options = ListOptions::Normal);
+ static List
+ fromQListRef(Path pathFromOwner, QList<T> &list,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ ListOptions options = ListOptions::Normal);
+
private:
LookupFunction m_lookup;
Length m_length;
@@ -224,18 +392,100 @@ private:
QString m_elType;
};
-class QMLDOM_EXPORT ConstantData: public DomElement {
+class QMLDOM_EXPORT ListPBase : public DomElement
+{
+public:
+ constexpr static DomType kindValue = DomType::ListP;
+ DomType kind() const override { return kindValue; }
+
+ ListPBase(Path pathFromOwner, const QList<void *> &pList, QString elType)
+ : DomElement(pathFromOwner), m_pList(pList), m_elType(elType)
+ {
+ }
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor v) override;
+ virtual void copyTo(ListPBase *) const { Q_ASSERT(false); };
+ virtual void moveTo(ListPBase *) const { Q_ASSERT(false); };
+ quintptr id() const override { return quintptr(0); }
+ index_type indexes(DomItem &) const override { return index_type(m_pList.size()); }
+
+protected:
+ QList<void *> m_pList;
+ QString m_elType;
+};
+
+template<typename T>
+class QMLDOM_EXPORT ListPT final : public ListPBase
+{
+public:
+ constexpr static DomType kindValue = DomType::ListP;
+
+ ListPT(Path pathFromOwner, QList<T *> pList, QString elType = QString(),
+ ListOptions options = ListOptions::Normal)
+ : ListPBase(pathFromOwner, {},
+ (elType.isEmpty() ? QLatin1String(typeid(T).name()) : elType))
+ {
+ static_assert(sizeof(ListPBase) == sizeof(ListPT),
+ "ListPT does not have the same size as ListPBase");
+ static_assert(alignof(ListPBase) == alignof(ListPT),
+ "ListPT does not have the same size as ListPBase");
+ m_pList.reserve(pList.size());
+ if (options == ListOptions::Normal) {
+ for (void *p : pList)
+ m_pList.append(p);
+ } else if (options == ListOptions::Reverse) {
+ for (qsizetype i = pList.length(); i-- != 0;)
+ // probably writing in reverse and reading sequentially would be better
+ m_pList.append(pList.at(i));
+ } else {
+ Q_ASSERT(false);
+ }
+ }
+ void copyTo(ListPBase *t) const override { new (t) ListPT(*this); }
+ void moveTo(ListPBase *t) const override { new (t) ListPT(std::move(*this)); }
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor v) override;
+
+ DomItem index(DomItem &self, index_type index) const override;
+};
+
+class QMLDOM_EXPORT ListP
+{
+public:
+ constexpr static DomType kindValue = DomType::ListP;
+ template<typename T>
+ ListP(Path pathFromOwner, QList<T *> pList, QString elType = QString(),
+ ListOptions options = ListOptions::Normal)
+ : list(ListPT<T>(pathFromOwner, pList, elType, options))
+ {
+ }
+ ListP() = delete;
+
+ ListPBase *operator->() { return list.data(); }
+ const ListPBase *operator->() const { return list.data(); }
+ ListPBase &operator*() { return *list.data(); }
+ const ListPBase &operator*() const { return *list.data(); }
+
+private:
+ SubclassStorage<ListPBase> list;
+};
+
+class QMLDOM_EXPORT ConstantData final : public DomElement
+{
public:
constexpr static DomType kindValue = DomType::ConstantData;
- DomType kind() const override { return kindValue; }
+ DomType kind() const override { return kindValue; }
enum class Options {
MapIsMap,
FirstMapIsFields
};
- ConstantData(Path pathFromOwner, QCborValue value, Options options = Options::MapIsMap, const SourceLocation & loc = SourceLocation());
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
+ ConstantData *operator->() { return this; }
+ const ConstantData *operator->() const { return this; }
+ ConstantData &operator*() { return *this; }
+ const ConstantData &operator*() const { return *this; }
+
+ ConstantData(Path pathFromOwner, QCborValue value, Options options = Options::MapIsMap);
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
quintptr id() const override;
DomKind domKind() const override;
QCborValue value() const override { return m_value; }
@@ -245,283 +495,650 @@ private:
Options m_options;
};
-class QMLDOM_EXPORT SimpleObjectWrap: public DomElement {
+class QMLDOM_EXPORT SimpleObjectWrapBase : public DomElement
+{
public:
constexpr static DomType kindValue = DomType::SimpleObjectWrap;
- DomType kind() const override { return kindValue; }
+ DomType kind() const final override { return m_kind; }
- template <typename T>
- static SimpleObjectWrap fromDataObject(
- Path pathFromOwner, T const & val,
- std::function<QCborValue(T const &)> toData,
- const SourceLocation & loc = SourceLocation(),
- DomType kind = kindValue,
- DomKind domKind = DomKind::Object,
- QString typeName = QString());
+ quintptr id() const final override { return m_id; }
+ DomKind domKind() const final override { return m_domKind; }
template <typename T>
- static SimpleObjectWrap fromObjectRef(
- Path pathFromOwner, T &value,
- std::function<bool(DomItem &, T &val, function_ref<bool(Path, DomItem &)>)> directSubpathsIterate,
- const SourceLocation & loc = SourceLocation(),
- DomType kind = kindValue,
- QString typeName = QString(),
- DomKind domKind = DomKind::Object);
-
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
- quintptr id() const override;
- QString typeName() const override { return m_typeName; }
- DomType internalKind() const { return m_kind; }
- DomKind domKind() const override { return m_domKind; }
- template <typename T>
T const *as() const
{
- return m_value.value<T*>();
+ if (m_options & SimpleWrapOption::ValueType) {
+ if (m_value.metaType().id() == QMetaType::fromType<T>().id())
+ return reinterpret_cast<const T *>(m_value.constData());
+ return nullptr;
+ } else {
+ return m_value.value<T *>();
+ }
}
template <typename T>
T *mutableAs()
{
- return m_value.value<T*>();
+ if (m_options & SimpleWrapOption::ValueType) {
+ if (m_value.metaType().id() == QMetaType::fromType<T>().id())
+ return reinterpret_cast<T *>(m_value.data());
+ return nullptr;
+ } else {
+ return m_value.value<T *>();
+ }
+ }
+
+ SimpleObjectWrapBase() = delete;
+ virtual void copyTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); }
+ virtual void moveTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); }
+ bool iterateDirectSubpaths(DomItem &, DirectVisitor) override
+ {
+ Q_ASSERT(false);
+ return true;
+ }
+
+protected:
+ friend class TestDomItem;
+ SimpleObjectWrapBase(Path pathFromOwner, QVariant value, quintptr idValue,
+ DomType kind = kindValue,
+ SimpleWrapOptions options = SimpleWrapOption::None)
+ : DomElement(pathFromOwner),
+ m_kind(kind),
+ m_domKind(kind2domKind(kind)),
+ m_value(value),
+ m_id(idValue),
+ m_options(options)
+ {
}
-private:
- SimpleObjectWrap(
- Path pathFromOwner, QVariant value,
- std::function<bool(DomItem &, QVariant, function_ref<bool(Path, DomItem &)>)> directSubpathsIterate,
- DomType kind = kindValue,
- DomKind domKind = DomKind::Object,
- QString typeName = QString(),
- const SourceLocation & loc = SourceLocation());
DomType m_kind;
DomKind m_domKind;
- QString m_typeName;
QVariant m_value;
- std::function<bool(DomItem &, QVariant, function_ref<bool(Path, DomItem &)>)> m_directSubpathsIterate;
+ quintptr m_id;
+ SimpleWrapOptions m_options;
};
-class QMLDOM_EXPORT Reference: public DomElement {
+template<typename T>
+class QMLDOM_EXPORT SimpleObjectWrapT final : public SimpleObjectWrapBase
+{
+public:
+ constexpr static DomType kindValue = DomType::SimpleObjectWrap;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
+ {
+ return mutableAsT()->iterateDirectSubpaths(self, visitor);
+ }
+
+ T const *asT() const
+ {
+ if constexpr (domTypeIsValueWrap(T::kindValue)) {
+ if (m_value.metaType().id() == QMetaType::fromType<T>().id())
+ return reinterpret_cast<const T *>(m_value.constData());
+ return nullptr;
+ } else if constexpr (domTypeIsObjWrap(T::kindValue)) {
+ return m_value.value<T *>();
+ } else {
+ Q_ASSERT_X(false, "SimpleObjectWrapT", "wrapping of unexpected type");
+ }
+ }
+
+ T *mutableAsT()
+ {
+ if (domTypeIsValueWrap(T::kindValue)) {
+ if (m_value.metaType().id() == QMetaType::fromType<T>().id())
+ return reinterpret_cast<T *>(m_value.data());
+ return nullptr;
+ } else if constexpr (domTypeIsObjWrap(T::kindValue)) {
+ return m_value.value<T *>();
+ } else {
+ Q_ASSERT_X(false, "SimpleObjectWrap", "wrapping of unexpected type");
+ return nullptr;
+ }
+ }
+
+ void copyTo(SimpleObjectWrapBase *target) const override
+ {
+ static_assert(sizeof(SimpleObjectWrapBase) == sizeof(SimpleObjectWrapT),
+ "Size mismatch in SimpleObjectWrapT");
+ static_assert(alignof(SimpleObjectWrapBase) == alignof(SimpleObjectWrapT),
+ "Size mismatch in SimpleObjectWrapT");
+ new (target) SimpleObjectWrapT(*this);
+ }
+
+ void moveTo(SimpleObjectWrapBase *target) const override
+ {
+ static_assert(sizeof(SimpleObjectWrapBase) == sizeof(SimpleObjectWrapT),
+ "Size mismatch in SimpleObjectWrapT");
+ static_assert(alignof(SimpleObjectWrapBase) == alignof(SimpleObjectWrapT),
+ "Size mismatch in SimpleObjectWrapT");
+ new (target) SimpleObjectWrapT(std::move(*this));
+ }
+
+ SimpleObjectWrapT(Path pathFromOwner, QVariant v, quintptr idValue, SimpleWrapOptions o)
+ : SimpleObjectWrapBase(pathFromOwner, v, idValue, T::kindValue, o)
+ {
+ Q_ASSERT(domTypeIsValueWrap(T::kindValue) == bool(o & SimpleWrapOption::ValueType));
+ }
+};
+
+class QMLDOM_EXPORT SimpleObjectWrap
+{
+public:
+ constexpr static DomType kindValue = DomType::SimpleObjectWrap;
+
+ SimpleObjectWrapBase *operator->() { return wrap.data(); }
+ const SimpleObjectWrapBase *operator->() const { return wrap.data(); }
+ SimpleObjectWrapBase &operator*() { return *wrap.data(); }
+ const SimpleObjectWrapBase &operator*() const { return *wrap.data(); }
+
+ template<typename T>
+ static SimpleObjectWrap fromObjectRef(Path pathFromOwner, T &value)
+ {
+ return SimpleObjectWrap(pathFromOwner, value);
+ }
+ SimpleObjectWrap() = delete;
+
+private:
+ template<typename T>
+ SimpleObjectWrap(Path pathFromOwner, T &value)
+ {
+ using BaseT = std::decay_t<T>;
+ if constexpr (domTypeIsObjWrap(BaseT::kindValue)) {
+ new (wrap.data()) SimpleObjectWrapT<BaseT>(pathFromOwner, QVariant::fromValue(&value),
+ quintptr(&value), SimpleWrapOption::None);
+ } else if constexpr (domTypeIsValueWrap(BaseT::kindValue)) {
+ new (wrap.data()) SimpleObjectWrapT<BaseT>(pathFromOwner, QVariant::fromValue(value),
+ quintptr(0), SimpleWrapOption::ValueType);
+ } else {
+ qCWarning(domLog) << "Unexpected object to wrap in SimpleObjectWrap: "
+ << domTypeToString(BaseT::kindValue);
+ Q_ASSERT_X(false, "SimpleObjectWrap",
+ "simple wrap of unexpected object"); // allow? (mocks for testing,...)
+ new (wrap.data())
+ SimpleObjectWrapT<BaseT>(pathFromOwner, nullptr, 0, SimpleWrapOption::None);
+ }
+ }
+ SubclassStorage<SimpleObjectWrapBase> wrap;
+};
+
+class QMLDOM_EXPORT Reference final : public DomElement
+{
+ Q_GADGET
public:
constexpr static DomType kindValue = DomType::Reference;
DomType kind() const override { return kindValue; }
+ Reference *operator->() { return this; }
+ const Reference *operator->() const { return this; }
+ Reference &operator*() { return *this; }
+ const Reference &operator*() const { return *this; }
+
+ bool shouldCache() const;
Reference(Path referredObject = Path(), Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation());
quintptr id() const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
- DomItem field(const DomItem &self, QStringView name) const override;
- QList<QString> const fields(const DomItem &self) const override;
- index_type indexes(const DomItem &) const override {
- return 0;
- }
- DomItem index(const DomItem &, index_type) const override;
- QSet<QString> const keys(const DomItem &) const override {
- return {};
- }
- DomItem key(const DomItem &, QString) const override;
-
- DomItem get(const DomItem &self) const;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ DomItem field(DomItem &self, QStringView name) const override;
+ QList<QString> fields(DomItem &self) const override;
+ index_type indexes(DomItem &) const override { return 0; }
+ DomItem index(DomItem &, index_type) const override;
+ QSet<QString> const keys(DomItem &) const override { return {}; }
+ DomItem key(DomItem &, QString) const override;
+
+ DomItem get(DomItem &self, ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr) const;
+ QList<DomItem> getAll(DomItem &self, ErrorHandler h = nullptr,
+ QList<Path> *visitedRefs = nullptr) const;
Path referredObjectPath;
};
-inline bool emptyChildrenVisitor(Path, const DomItem &, bool) { return true; }
+using ElementT = std::variant<
+ Empty, Map, List, ListP, ConstantData, SimpleObjectWrap, Reference, GlobalComponent *,
+ JsResource *, QmlComponent *, QmltypesComponent *, EnumDecl *, MockObject *, ModuleScope *,
+ AstComments *, AttachedInfo *, DomEnvironment *, DomUniverse *, ExternalItemInfoBase *,
+ ExternalItemPairBase *, GlobalScope *, JsFile *, QmlDirectory *, QmlFile *, QmldirFile *,
+ QmlObject *, QmltypesFile *, LoadInfo *, MockOwner *, ModuleIndex *, ScriptExpression *>;
+
+using TopT = std::variant<std::shared_ptr<DomEnvironment>, std::shared_ptr<DomUniverse>>;
+
+using OwnerT =
+ std::variant<std::shared_ptr<ModuleIndex>, std::shared_ptr<MockOwner>,
+ std::shared_ptr<ExternalItemInfoBase>, std::shared_ptr<ExternalItemPairBase>,
+ std::shared_ptr<QmlDirectory>, std::shared_ptr<QmldirFile>,
+ std::shared_ptr<JsFile>, std::shared_ptr<QmlFile>,
+ std::shared_ptr<QmltypesFile>, std::shared_ptr<GlobalScope>,
+ std::shared_ptr<ScriptExpression>, std::shared_ptr<AstComments>,
+ std::shared_ptr<LoadInfo>, std::shared_ptr<AttachedInfo>,
+ std::shared_ptr<DomEnvironment>, std::shared_ptr<DomUniverse>>;
+
+inline bool emptyChildrenVisitor(Path, DomItem &, bool)
+{
+ return true;
+}
class MutableDomItem;
class QMLDOM_EXPORT DomItem {
Q_DECLARE_TR_FUNCTIONS(DomItem);
public:
- using Callback = function<void(Path, const DomItem &, const DomItem &)>;
+ using Callback = function<void(Path, DomItem &, DomItem &)>;
using InternalKind = DomType;
- using Visitor = function_ref<bool(Path, const DomItem &)>;
- using ChildrenVisitor = function_ref<bool(Path, const DomItem &, bool)>;
+ using Visitor = function_ref<bool(Path, DomItem &)>;
+ using ChildrenVisitor = function_ref<bool(Path, DomItem &, bool)>;
static ErrorGroup domErrorGroup;
static ErrorGroups myErrors();
static ErrorGroups myResolveErrors();
+ static DomItem empty;
- operator bool() const { return base()->kind() != DomType::Empty; }
- InternalKind internalKind() const {
- InternalKind res = base()->kind();
- if (res == InternalKind::SimpleObjectWrap)
- return static_cast<SimpleObjectWrap const *>(base())->internalKind();
- return res;
+ enum class CopyOption { EnvConnected, EnvDisconnected };
+
+ template<typename F>
+ auto visitMutableEl(F f)
+ {
+ return std::visit(f, this->m_element);
}
- DomKind domKind() const {
- return base()->domKind();
+ template<typename F>
+ auto visitEl(F f)
+ {
+ return std::visit(f, this->m_element);
}
- Path canonicalPath() const;
- DomItem containingObject() const;
- DomItem container() const;
- DomItem component() const;
- DomItem owner() const;
- DomItem top() const;
- DomItem environment() const;
- DomItem universe() const;
-
- DomItem fileObject(GoTo option = GoTo::Strict) const;
-
- // convenience getters
- QString name() const;
- DomItem qmlChildren() const;
- DomItem annotations() const;
-
- bool resolve(Path path, Visitor visitor, ErrorHandler errorHandler, ResolveOptions options = ResolveOption::None, Path fullPath = Path(), QList<Path> *visitedRefs = nullptr) const;
-
- DomItem operator[](Path path) const;
- DomItem operator[](QStringView component) const;
- DomItem operator[](const QString &component) const;
- DomItem operator[](const char16_t *component) const { return (*this)[QStringView(component)]; } // to avoid clash with stupid builtin ptrdiff_t[DomItem&], coming from C
- DomItem operator[](index_type i) const { return index(i); }
- DomItem operator[](int i) const { return index(i); }
-
- DomItem path(Path p, ErrorHandler h = &defaultErrorHandler) const;
- DomItem path(QString p, ErrorHandler h = &defaultErrorHandler) const;
- DomItem path(QStringView p, ErrorHandler h = &defaultErrorHandler) const;
-
- QList<QString> const fields() const;
- DomItem field(QStringView name) const;
+ explicit operator bool() const { return m_kind != DomType::Empty; }
+ InternalKind internalKind() const {
+ return m_kind;
+ }
+ QString internalKindStr() const { return domTypeToString(internalKind()); }
+ DomKind domKind() const
+ {
+ if (m_kind == DomType::ConstantData)
+ return std::get<ConstantData>(m_element).domKind();
+ else
+ return kind2domKind(m_kind);
+ }
- index_type indexes() const;
- DomItem index(index_type) const;
+ Path canonicalPath();
- QSet<QString> const keys() const;
- DomItem key(QString name) const;
+ DomItem filterUp(function_ref<bool(DomType k, DomItem &)> filter, FilterUpOptions options);
+ DomItem containingObject();
+ DomItem container();
+ DomItem owner();
+ DomItem top();
+ DomItem environment();
+ DomItem universe();
- bool visitChildren(Path basePath, ChildrenVisitor visitor, VisitOptions options = VisitOption::VisitAdopted, ChildrenVisitor openingVisitor = emptyChildrenVisitor, ChildrenVisitor closingVisitor = emptyChildrenVisitor) const;
+ DomItem qmlObject(GoTo option = GoTo::Strict,
+ FilterUpOptions options = FilterUpOptions::ReturnOuter);
+ DomItem fileObject(GoTo option = GoTo::Strict);
+ DomItem rootQmlObject(GoTo option = GoTo::Strict);
+ DomItem globalScope();
+ DomItem component(GoTo option = GoTo::Strict);
+ DomItem scope(FilterUpOptions options = FilterUpOptions::ReturnOuter);
- quintptr id() const { return base()->id(); }
- Path pathFromOwner() const { return base()->pathFromOwner(*this); }
- QString canonicalFilePath() const { return base()->canonicalFilePath(*this); }
- SourceLocation location() const { return base()->location(*this); }
+ // convenience getters
+ DomItem get(ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr);
+ QList<DomItem> getAll(ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr);
+ bool isOwningItem() { return domTypeIsOwningItem(internalKind()); }
+ bool isExternalItem() { return domTypeIsExternalItem(internalKind()); }
+ bool isTopItem() { return domTypeIsTopItem(internalKind()); }
+ bool isContainer() { return domTypeIsContainer(internalKind()); }
+ bool isScope() { return domTypeIsScope(internalKind()); }
+ bool isCanonicalChild(DomItem &child);
+ bool hasAnnotations();
+ QString name() { return field(Fields::name).value().toString(); }
+ DomItem pragmas() { return field(Fields::pragmas); }
+ DomItem ids() { return field(Fields::ids); }
+ QString idStr() { return field(Fields::idStr).value().toString(); }
+ DomItem propertyInfos() { return field(Fields::propertyInfos); }
+ PropertyInfo propertyInfoWithName(QString name);
+ QSet<QString> propertyInfoNames();
+ DomItem propertyDefs() { return field(Fields::propertyDefs); }
+ DomItem bindings() { return field(Fields::bindings); }
+ DomItem methods() { return field(Fields::methods); }
+ DomItem enumerations() { return field(Fields::enumerations); }
+ DomItem children() { return field(Fields::children); }
+ DomItem child(index_type i) { return field(Fields::children).index(i); }
+ DomItem annotations()
+ {
+ if (hasAnnotations())
+ return field(Fields::annotations);
+ else
+ return DomItem();
+ }
- QCborValue value() const;
+ bool resolve(Path path, Visitor visitor, ErrorHandler errorHandler,
+ ResolveOptions options = ResolveOption::None, Path fullPath = Path(),
+ QList<Path> *visitedRefs = nullptr);
- void dumpPtr(Sink) const;
- void dump(Sink, int indent = 0) const;
- QString toString() const;
+ DomItem operator[](Path path);
+ DomItem operator[](QStringView component);
+ DomItem operator[](const QString &component);
+ DomItem operator[](const char16_t *component)
+ {
+ return (*this)[QStringView(component)];
+ } // to avoid clash with stupid builtin ptrdiff_t[DomItem&], coming from C
+ DomItem operator[](index_type i) { return index(i); }
+ DomItem operator[](int i) { return index(i); }
+ index_type size() { return indexes() + keys().size(); }
+ index_type length() { return size(); }
+
+ DomItem path(Path p, ErrorHandler h = &defaultErrorHandler);
+ DomItem path(QString p, ErrorHandler h = &defaultErrorHandler);
+ DomItem path(QStringView p, ErrorHandler h = &defaultErrorHandler);
+
+ QList<QString> fields();
+ DomItem field(QStringView name);
+
+ index_type indexes();
+ DomItem index(index_type);
+ bool visitIndexes(function_ref<bool(DomItem &)> visitor);
+
+ QSet<QString> keys();
+ QStringList sortedKeys();
+ DomItem key(QString name);
+ DomItem key(QStringView name) { return key(name.toString()); }
+ bool visitKeys(function_ref<bool(QString, DomItem &)> visitor);
+
+ QList<DomItem> values();
+
+ bool visitTree(Path basePath, ChildrenVisitor visitor,
+ VisitOptions options = VisitOption::Default,
+ ChildrenVisitor openingVisitor = emptyChildrenVisitor,
+ ChildrenVisitor closingVisitor = emptyChildrenVisitor);
+ bool visitPrototypeChain(function_ref<bool(DomItem &)> visitor,
+ VisitPrototypesOptions options = VisitPrototypesOption::Normal,
+ ErrorHandler h = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr);
+ bool visitDirectAccessibleScopes(function_ref<bool(DomItem &)> visitor,
+ VisitPrototypesOptions options = VisitPrototypesOption::Normal,
+ ErrorHandler h = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr);
+ bool
+ visitStaticTypePrototypeChains(function_ref<bool(DomItem &)> visitor,
+ VisitPrototypesOptions options = VisitPrototypesOption::Normal,
+ ErrorHandler h = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr);
+ bool visitScopeChain(function_ref<bool(DomItem &)> visitor,
+ LookupOptions = LookupOption::Normal, ErrorHandler h = nullptr,
+ QSet<quintptr> *visited = nullptr, QList<Path> *visitedRefs = nullptr);
+ bool visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)> visitor);
+ QSet<QString> localSymbolNames();
+ bool visitLookup1(QString symbolName, function_ref<bool(DomItem &)> visitor,
+ LookupOptions = LookupOption::Normal, ErrorHandler h = nullptr,
+ QSet<quintptr> *visited = nullptr, QList<Path> *visitedRefs = nullptr);
+ bool visitLookup(QString symbolName, function_ref<bool(DomItem &)> visitor,
+ LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal,
+ ErrorHandler errorHandler = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr);
+ bool visitSubSymbolsNamed(QString name, function_ref<bool(DomItem &)> visitor);
+ DomItem proceedToScope(ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr);
+ QList<DomItem> lookup(QString symbolName, LookupType type = LookupType::Symbol,
+ LookupOptions = LookupOption::Normal,
+ ErrorHandler errorHandler = nullptr);
+ DomItem lookupFirst(QString symbolName, LookupType type = LookupType::Symbol,
+ LookupOptions = LookupOption::Normal, ErrorHandler errorHandler = nullptr);
+
+ quintptr id();
+ Path pathFromOwner();
+ QString canonicalFilePath();
+ DomItem fileLocationsTree();
+ DomItem fileLocations();
+ MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected);
+ bool commitToBase();
+ DomItem refreshed() { return top().path(canonicalPath()); }
+ QCborValue value();
+
+ void dumpPtr(Sink sink);
+ void dump(Sink, int indent = 0,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter =
+ noFilter);
+ QString toString();
+ QString toString() const
+ {
+ DomItem self = *this;
+ return self.toString();
+ }
// OwnigItem elements
- int derivedFrom() const;
- int revision() const;
- QDateTime createdAt() const;
- QDateTime frozenAt() const;
- QDateTime lastDataUpdateAt() const;
-
- void addError(ErrorMessage msg) const;
- ErrorHandler errorHandler() const;
- void clearErrors(ErrorGroups groups = ErrorGroups({}), bool iterate = true) const;
+ int derivedFrom();
+ int revision();
+ QDateTime createdAt();
+ QDateTime frozenAt();
+ QDateTime lastDataUpdateAt();
+
+ void addError(ErrorMessage msg);
+ ErrorHandler errorHandler();
+ void clearErrors(ErrorGroups groups = ErrorGroups({}), bool iterate = true);
// return false if a quick exit was requested
bool iterateErrors(function_ref<bool(DomItem source, ErrorMessage msg)> visitor, bool iterate,
- Path inPath = Path())const;
-
- bool iterateSubOwners(function_ref<bool(DomItem owner)> visitor) const;
-
- Subpath subDataField(QStringView fieldName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
- Subpath subDataField(QString fieldName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
- Subpath subDataIndex(index_type i, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
- Subpath subDataKey(QStringView keyName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
- Subpath subDataKey(QString keyName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
- Subpath subDataPath(Path path, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
- Subpath subReferenceField(QStringView fieldName, Path referencedObject,
- const SourceLocation & loc = SourceLocation()) const;
- Subpath subReferenceField(QString fieldName, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
- Subpath subReferenceKey(QStringView keyName, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
- Subpath subReferenceKey(QString keyName, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
- Subpath subReferenceIndex(index_type i, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
- Subpath subReferencePath(Path subPath, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
-
- Subpath toSubField(QStringView fieldName) const;
- Subpath toSubField(QString fieldName) const;
- Subpath toSubKey(QStringView keyName) const;
- Subpath toSubKey(QString keyName) const;
- Subpath toSubIndex(index_type i) const;
- Subpath toSubPath(Path subPath) const;
- Subpath subList(const List &list) const;
- Subpath subMap(const Map &map) const;
- Subpath subObjectWrap(const SimpleObjectWrap &o) const;
- template <typename T>
- Subpath subWrapPath(Path p, T &obj, SourceLocation loc = SourceLocation()) const;
- template <typename T>
- Subpath subWrapField(QString p, T &obj, SourceLocation loc = SourceLocation()) const;
- template <typename T>
- Subpath subWrapField(QStringView p, T &obj, SourceLocation loc = SourceLocation()) const;
- template <typename T>
- Subpath subWrapKey(QString p, T &obj, SourceLocation loc = SourceLocation()) const;
- template <typename T>
- Subpath subWrapKey(QStringView p, T &obj, SourceLocation loc = SourceLocation()) const;
- template <typename T>
- Subpath subWrapIndex(index_type i, T &obj, SourceLocation loc = SourceLocation()) const;
+ Path inPath = Path());
+
+ bool iterateSubOwners(function_ref<bool(DomItem &owner)> visitor);
+ bool iterateDirectSubpaths(DirectVisitor v);
+
+ template<typename T>
+ DomItem subDataItem(const PathEls::PathComponent &c, T value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap);
+ template<typename T>
+ DomItem subDataItemField(QStringView f, T value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap)
+ {
+ return subDataItem(PathEls::Field(f), value, options);
+ }
+ template<typename T>
+ DomItem subValueItem(const PathEls::PathComponent &c, T value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap);
+ template<typename T>
+ bool dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, T value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap);
+ template<typename T>
+ bool dvValueField(DirectVisitor visitor, QStringView f, T value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap)
+ {
+ return this->dvValue<T>(visitor, PathEls::Field(f), value, options);
+ }
+ template<typename F>
+ bool dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
+ ConstantData::Options options = ConstantData::Options::MapIsMap);
+ template<typename F>
+ bool dvValueLazyField(DirectVisitor visitor, QStringView f, F valueF,
+ ConstantData::Options options = ConstantData::Options::MapIsMap)
+ {
+ return this->dvValueLazy(visitor, PathEls::Field(f), valueF, options);
+ }
+ DomItem subLocationItem(const PathEls::PathComponent &c, SourceLocation loc,
+ QStringView code = QStringView())
+ {
+ return this->subDataItem(c, locationToData(loc, code));
+ }
+ // bool dvSubReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path
+ // referencedObject);
+ DomItem subReferencesItem(const PathEls::PathComponent &c, QList<Path> paths);
+ DomItem subReferenceItem(const PathEls::PathComponent &c, Path referencedObject);
+ bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path referencedObject)
+ {
+ return dvItem(visitor, c, [c, this, referencedObject]() {
+ return this->subReferenceItem(c, referencedObject);
+ });
+ }
+ bool dvReferences(DirectVisitor visitor, const PathEls::PathComponent &c, QList<Path> paths)
+ {
+ return dvItem(visitor, c, [c, this, paths]() { return this->subReferencesItem(c, paths); });
+ }
+ bool dvReferenceField(DirectVisitor visitor, QStringView f, Path referencedObject)
+ {
+ return dvReference(visitor, PathEls::Field(f), referencedObject);
+ }
+ bool dvReferencesField(DirectVisitor visitor, QStringView f, QList<Path> paths)
+ {
+ return dvReferences(visitor, PathEls::Field(f), paths);
+ }
+ bool dvItem(DirectVisitor visitor, const PathEls::PathComponent &c, function_ref<DomItem()> it)
+ {
+ return visitor(c, it);
+ }
+ bool dvItemField(DirectVisitor visitor, QStringView f, function_ref<DomItem()> it)
+ {
+ return dvItem(visitor, PathEls::Field(f), it);
+ }
+ DomItem subListItem(const List &list);
+ DomItem subMapItem(const Map &map);
+ DomItem subObjectWrapItem(SimpleObjectWrap obj)
+ {
+ return DomItem(m_top, m_owner, m_ownerPath, obj);
+ }
+ template<typename Owner>
+ DomItem subOwnerItem(const PathEls::PathComponent &c, Owner o)
+ {
+ if constexpr (domTypeIsUnattachedOwningItem(Owner::element_type::kindValue))
+ return DomItem(m_top, o, canonicalPath().appendComponent(c), o.get());
+ else
+ return DomItem(m_top, o, Path(), o.get());
+ }
+ template<typename T>
+ DomItem wrap(const PathEls::PathComponent &c, T &obj);
+ template<typename T>
+ DomItem wrapField(QStringView f, T &obj)
+ {
+ return wrap<T>(PathEls::Field(f), obj);
+ }
+ template<typename T>
+ bool dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj);
+ template<typename T>
+ bool dvWrapField(DirectVisitor visitor, QStringView f, T &obj)
+ {
+ return dvWrap<T>(visitor, PathEls::Field(f), obj);
+ }
- DomItem();
+ DomItem() = default;
DomItem(std::shared_ptr<DomEnvironment>);
DomItem(std::shared_ptr<DomUniverse>);
+ void loadFile(QString filePath, QString logicalPath,
+ std::function<void(Path, DomItem &, DomItem &)> callback,
+ LoadOptions loadOptions);
+ void loadFile(QString canonicalFilePath, QString logicalPath, QString code, QDateTime codeDate,
+ std::function<void(Path, DomItem &, DomItem &)> callback,
+ LoadOptions loadOptions);
+ void loadModuleDependency(QString uri, Version v,
+ std::function<void(Path, DomItem &, DomItem &)> callback = nullptr,
+ ErrorHandler = nullptr);
+ void loadBuiltins(std::function<void(Path, DomItem &, DomItem &)> callback = nullptr,
+ ErrorHandler = nullptr);
+ void loadPendingDependencies();
+
// --- start of potentially dangerous stuff, make private? ---
- std::shared_ptr<DomTop> topPtr() const;
- std::shared_ptr<OwningItem> owningItemPtr() const;
+ std::shared_ptr<DomTop> topPtr();
+ std::shared_ptr<OwningItem> owningItemPtr();
// keep the DomItem around to ensure that it doesn't get deleted
- template <typename T, typename std::enable_if<std::is_base_of<DomBase, T>::value, bool>::type = true>
- T const*as() const {
- InternalKind k = base()->kind();
- if (k == T::kindValue)
- return static_cast<T const*>(base());
- if (k == InternalKind::SimpleObjectWrap)
- return static_cast<SimpleObjectWrap const *>(base())->as<T>();
+ template<typename T, typename std::enable_if<std::is_base_of_v<DomBase, T>, bool>::type = true>
+ T const *as()
+ {
+ if (m_kind == T::kindValue) {
+ if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
+ return std::get<SimpleObjectWrap>(m_element)->as<T>();
+ else
+ return static_cast<T const *>(base());
+ }
return nullptr;
}
- template <typename T, typename std::enable_if<!std::is_base_of<DomBase, T>::value, bool>::type = true>
- T const*as() const {
- InternalKind k = base()->kind();
- if (k == InternalKind::SimpleObjectWrap)
- return static_cast<SimpleObjectWrap const *>(base())->as<T>();
+ template<typename T, typename std::enable_if<!std::is_base_of_v<DomBase, T>, bool>::type = true>
+ T const *as()
+ {
+ if (m_kind == T::kindValue) {
+ Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind));
+ return std::get<SimpleObjectWrap>(m_element)->as<T>();
+ }
return nullptr;
}
- template <typename T>
- std::shared_ptr<T> ownerAs() const;
+ template<typename T>
+ std::shared_ptr<T> ownerAs();
- DomItem copy(std::shared_ptr<OwningItem> owner, Path ownerPath, DomBase *base) const;
- DomItem copy(std::shared_ptr<OwningItem> owner, Path ownerPath = Path()) const;
- DomItem copy(DomBase *base) const;
-private:
- DomBase const* base() const {
- if (m_base == nullptr)
- return reinterpret_cast<DomBase const*>(&inlineEl);
- return m_base;
+ template<typename Owner, typename T>
+ DomItem copy(Owner owner, Path ownerPath, T base)
+ {
+ Q_ASSERT(m_top);
+ static_assert(IsInlineDom<std::decay_t<T>>::value, "Expected an inline item or pointer");
+ return DomItem(m_top, owner, ownerPath, base);
+ }
+
+ template<typename Owner>
+ DomItem copy(Owner owner, Path ownerPath)
+ {
+ Q_ASSERT(m_top);
+ return DomItem(m_top, owner, ownerPath, owner.get());
+ }
+
+ template<typename T>
+ DomItem copy(T base)
+ {
+ Q_ASSERT(m_top);
+ using BaseT = std::decay_t<T>;
+ static_assert(!std::is_same_v<BaseT, ElementT>,
+ "variant not supported, pass in the stored types");
+ static_assert(IsInlineDom<BaseT>::value, "expected either a pointer or an inline item");
+ if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
+ return DomItem(m_top, base, Path(), base.get());
+ } else {
+ return DomItem(m_top, m_owner, m_ownerPath, base);
+ }
}
+
+private:
+ DomBase const *base();
template <typename T, typename std::enable_if<std::is_base_of<DomBase, T>::value, bool>::type = true>
T *mutableAs() {
- InternalKind k = base()->kind();
- if (k == T::kindValue)
- return static_cast<T*>(mutableBase());
- if (k == InternalKind::SimpleObjectWrap)
- return static_cast<SimpleObjectWrap *>(mutableBase())->mutableAs<T>();
+ if (m_kind == T::kindValue) {
+ if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
+ return static_cast<SimpleObjectWrapBase *>(mutableBase())->mutableAs<T>();
+ else
+ return static_cast<T *>(mutableBase());
+ }
return nullptr;
}
template <typename T, typename std::enable_if<!std::is_base_of<DomBase, T>::value, bool>::type = true>
T *mutableAs() {
- InternalKind k = base()->kind();
- if (k == InternalKind::SimpleObjectWrap)
- return static_cast<SimpleObjectWrap *>(mutableBase())->mutableAs<T>();
+ if (m_kind == T::kindValue) {
+ Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind));
+ return static_cast<SimpleObjectWrapBase *>(mutableBase())->mutableAs<T>();
+ }
return nullptr;
}
- DomBase * mutableBase() {
- if (m_base == nullptr)
- return reinterpret_cast<DomBase*>(&inlineEl);
- return m_base;
- }
- DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, Path ownerPath, DomBase *base);
- DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, Map map);
- DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, List list);
- DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, ConstantData data);
- DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, Reference reference);
- DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, SimpleObjectWrap wrapper);
+ DomBase *mutableBase();
+ template<typename Env, typename Owner>
+ DomItem(Env, Owner, Path, std::nullptr_t) : DomItem()
+ {
+ }
+ template<typename Env, typename Owner, typename T,
+ typename = std::enable_if_t<IsInlineDom<std::decay_t<T>>::value>>
+ DomItem(Env env, Owner owner, Path ownerPath, T el)
+ : m_top(env), m_owner(owner), m_ownerPath(ownerPath), m_element(el)
+ {
+ using BaseT = std::decay_t<T>;
+ if constexpr (std::is_pointer_v<BaseT>) {
+ if (!el || el->kind() == DomType::Empty) { // avoid null ptr, and allow only a
+ // single kind of Empty
+ m_kind = DomType::Empty;
+ m_top.reset();
+ m_owner.reset();
+ m_ownerPath = Path();
+ m_element = Empty();
+ } else {
+ using DomT = std::remove_pointer_t<BaseT>;
+ m_element = el;
+ m_kind = DomT::kindValue;
+ }
+ } else {
+ static_assert(!std::is_same_v<BaseT, ElementT>,
+ "variant not supported, pass in the internal type");
+ m_kind = el->kind();
+ }
+ }
+ friend class DomBase;
friend class DomElement;
friend class Map;
friend class List;
@@ -531,212 +1148,144 @@ private:
friend class ExternalItemInfoBase;
friend class ConstantData;
friend class MutableDomItem;
+ friend class ScriptExpression;
+ friend class AstComments;
friend class AttachedInfo;
- friend bool operator ==(const DomItem &, const DomItem &);
- std::shared_ptr<DomTop> m_top;
- std::shared_ptr<OwningItem> m_owner;
+ friend class TestDomItem;
+ friend bool operator==(const DomItem &, const DomItem &);
+ DomType m_kind = DomType::Empty;
+ std::optional<TopT> m_top;
+ std::optional<OwnerT> m_owner;
Path m_ownerPath;
- DomBase *m_base;
- union InlineEl {
- // Should add optimized move ops (should be able to do a bit copy of union)
- InlineEl(): empty() { }
- InlineEl(const InlineEl &d) {
- switch (d.kind()){
- case DomType::Empty:
- Q_ASSERT((quintptr)this == (quintptr)&empty && "non C++11 compliant compiler");
- new (&empty) Empty(d.empty);
- break;
- case DomType::Map:
- Q_ASSERT((quintptr)this == (quintptr)&map && "non C++11 compliant compiler");
- new (&map) Map(d.map);
- break;
- case DomType::List:
- Q_ASSERT((quintptr)this == (quintptr)&list && "non C++11 compliant compiler");
- new (&list) List(d.list);
- break;
- case DomType::ConstantData:
- Q_ASSERT((quintptr)this == (quintptr)&data && "non C++11 compliant compiler");
- new (&data) ConstantData(d.data);
- break;
- case DomType::SimpleObjectWrap:
- Q_ASSERT((quintptr)this == (quintptr)&simpleObjectWrap && "non C++11 compliant compiler");
- new (&simpleObjectWrap) SimpleObjectWrap(d.simpleObjectWrap);
- break;
- case DomType::Reference:
- Q_ASSERT((quintptr)this == (quintptr)&reference && "non C++11 compliant compiler");
- new (&reference) Reference(d.reference);
- break;
- default:
- Q_ASSERT(false && "unexpected kind in inline element");
- break;
- }
- }
- InlineEl(const Empty &o) {
- Q_ASSERT((quintptr)this == (quintptr)&empty && "non C++11 compliant compiler");
- new (&empty) Empty(o);
- }
- InlineEl(const Map &o) {
- Q_ASSERT((quintptr)this == (quintptr)&map && "non C++11 compliant compiler");
- new (&map) Map(o);
- }
- InlineEl(const List &o){
- Q_ASSERT((quintptr)this == (quintptr)&list && "non C++11 compliant compiler");
- new (&list) List(o);
- }
- InlineEl(const ConstantData &o) {
- Q_ASSERT((quintptr)this == (quintptr)&data && "non C++11 compliant compiler");
- new (&data) ConstantData(o);
- }
- InlineEl(const SimpleObjectWrap &o) {
- Q_ASSERT((quintptr)this == (quintptr)&simpleObjectWrap && "non C++11 compliant compiler");
- new (&simpleObjectWrap) SimpleObjectWrap(o);
- }
- InlineEl(const Reference &o) {
- Q_ASSERT((quintptr)this == (quintptr)&reference && "non C++11 compliant compiler");
- new (&reference) Reference(o);
- }
- InlineEl &operator=(const InlineEl &d) {
- Q_ASSERT(this != &d);
- this->~InlineEl(); // destruct & construct new...
- new (this)InlineEl(d);
- return *this;
- }
- DomType kind() const {
- return reinterpret_cast<const DomBase*>(this)->kind();
- }
- ~InlineEl() {
- reinterpret_cast<const DomBase*>(this)->~DomBase();
- }
- Empty empty;
- Map map;
- List list;
- ConstantData data;
- SimpleObjectWrap simpleObjectWrap;
- Reference reference;
- } inlineEl;
+ ElementT m_element = Empty();
};
-bool operator ==(const DomItem &o1, const DomItem &o2);
-inline bool operator !=(const DomItem &o1, const DomItem &o2) {
+bool operator==(const DomItem &o1, const DomItem &o2);
+inline bool operator!=(const DomItem &o1, const DomItem &o2)
+{
return !(o1 == o2);
}
-Q_DECLARE_OPERATORS_FOR_FLAGS(LoadOptions)
-
-class Subpath {
-public:
- Path path;
- DomItem item;
-
- bool visit(function_ref<bool(Path, DomItem &)> visitor){
- return visitor(path, item);
- }
-};
-
template<typename T>
-Map Map::fromMultiMapRef(Path pathFromOwner, QMultiMap<QString,T> &mmap, std::function<DomItem(const DomItem &, Path, T&)> elWrapper)
+Map Map::fromMultiMapRef(
+ Path pathFromOwner, QMultiMap<QString, T> &mmap,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper)
{
- return Map(pathFromOwner, [&mmap, elWrapper](const DomItem &self, QString key) {
- auto it = mmap.find(key);
- auto end = mmap.cend();
- if (it == end)
- return DomItem();
- else {
- QList<T *> values;
- while (it != end && it.key() == key)
- values.append(&(*it++));
- return self.subList(List::fromQList<T*>(self.pathFromOwner().key(key), values, [elWrapper](const DomItem &l, Path p,T * &el) {
- return elWrapper(l,p,*el);
- }, ListOptions::Reverse)).item;
- }
- }, [&mmap](const DomItem&){
- return QSet<QString>(mmap.keyBegin(), mmap.keyEnd());
- }, QLatin1String(typeid(T).name()));
+ return Map(
+ pathFromOwner,
+ [&mmap, elWrapper](DomItem &self, QString key) {
+ auto it = mmap.find(key);
+ auto end = mmap.cend();
+ if (it == end)
+ return DomItem();
+ else {
+ // special case single element (++it == end || it.key() != key)?
+ QList<T *> values;
+ while (it != end && it.key() == key)
+ values.append(&(*it++));
+ ListP ll(self.pathFromOwner().appendComponent(PathEls::Key(key)), values,
+ QString(), ListOptions::Reverse);
+ return self.copy(ll);
+ }
+ },
+ [&mmap](DomItem &) { return QSet<QString>(mmap.keyBegin(), mmap.keyEnd()); },
+ QLatin1String(typeid(T).name()));
}
template<typename T>
-Map Map::fromMapRef(Path pathFromOwner, QMap<QString,T> &map,
- std::function<DomItem(const DomItem &, Path, T&)> elWrapper)
+Map Map::fromMapRef(
+ Path pathFromOwner, QMap<QString, T> &map,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper)
{
- return Map(pathFromOwner, [&map, elWrapper](const DomItem &self, QString key) {
- if (!map.contains(key))
- return DomItem();
- else {
- return elWrapper(self, Path::Key(key), map[key]);
- }
- }, [&map](const DomItem&){
- return QSet<QString>(map.keyBegin(), map.keyEnd());
- }, QLatin1String(typeid(T).name()));
+ return Map(
+ pathFromOwner,
+ [&map, elWrapper](DomItem &self, QString key) {
+ if (!map.contains(key))
+ return DomItem();
+ else {
+ return elWrapper(self, PathEls::Key(key), map[key]);
+ }
+ },
+ [&map](DomItem &) { return QSet<QString>(map.keyBegin(), map.keyEnd()); },
+ QLatin1String(typeid(T).name()));
}
template<typename T>
-List List::fromQList(Path pathFromOwner, QList<T> list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options)
+List List::fromQList(
+ Path pathFromOwner, QList<T> list,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ ListOptions options)
{
index_type len = list.length();
if (options == ListOptions::Reverse) {
return List(
- pathFromOwner,
- [list, elWrapper](const DomItem &self, index_type i) mutable {
- if (i < 0 || i >= list.length())
- return DomItem();
- return elWrapper(self, Path::Index(i), list[list.length() -i - 1]);
- }, [len](const DomItem &) {
- return len;
- }, nullptr, QLatin1String(typeid(T).name()));
+ pathFromOwner,
+ [list, elWrapper](DomItem &self, index_type i) mutable {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, PathEls::Index(i), list[list.length() - i - 1]);
+ },
+ [len](DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
} else {
- return List(pathFromOwner,
- [list, elWrapper](const DomItem &self, index_type i) mutable {
- if (i < 0 || i >= list.length())
- return DomItem();
- return elWrapper(self, Path::Index(i), list[i]);
- }, [len](const DomItem &) {
- return len;
- }, nullptr, QLatin1String(typeid(T).name()));
+ return List(
+ pathFromOwner,
+ [list, elWrapper](DomItem &self, index_type i) mutable {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, PathEls::Index(i), list[i]);
+ },
+ [len](DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
}
}
template<typename T>
-List List::fromQListRef(Path pathFromOwner, QList<T> &list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options)
+List List::fromQListRef(
+ Path pathFromOwner, QList<T> &list,
+ std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ ListOptions options)
{
if (options == ListOptions::Reverse) {
return List(
- pathFromOwner,
- [&list, elWrapper](const DomItem &self, index_type i) {
- if (i < 0 || i >= list.length())
- return DomItem();
- return elWrapper(self, Path::Index(i), list[list.length() -i - 1]);
- }, [&list](const DomItem &) {
- return list.length();
- }, nullptr, QLatin1String(typeid(T).name()));
+ pathFromOwner,
+ [&list, elWrapper](DomItem &self, index_type i) {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, PathEls::Index(i), list[list.length() - i - 1]);
+ },
+ [&list](DomItem &) { return list.length(); }, nullptr,
+ QLatin1String(typeid(T).name()));
} else {
- return List(pathFromOwner,
- [&list, elWrapper](const DomItem &self, index_type i) {
- if (i < 0 || i >= list.length())
- return DomItem();
- return elWrapper(self, Path::Index(i), list[i]);
- }, [&list](const DomItem &) {
- return list.length();
- }, nullptr, QLatin1String(typeid(T).name()));
+ return List(
+ pathFromOwner,
+ [&list, elWrapper](DomItem &self, index_type i) {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, PathEls::Index(i), list[i]);
+ },
+ [&list](DomItem &) { return list.length(); }, nullptr,
+ QLatin1String(typeid(T).name()));
}
}
class QMLDOM_EXPORT OwningItem: public DomBase {
protected:
- virtual std::shared_ptr<OwningItem> doCopy(const DomItem &self) const = 0;
+ virtual std::shared_ptr<OwningItem> doCopy(DomItem &self) const = 0;
+
public:
OwningItem(const OwningItem &o);
OwningItem(int derivedFrom=0);
OwningItem(int derivedFrom, QDateTime lastDataUpdateAt);
+ OwningItem(const OwningItem &&) = delete;
+ OwningItem &operator=(const OwningItem &&) = delete;
static int nextRevision();
- Path canonicalPath(const DomItem &self) const override = 0;
+ Path canonicalPath(DomItem &self) const override = 0;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)>) override;
- std::shared_ptr<OwningItem> makeCopy(const DomItem &self) {
- return doCopy(self);
- }
- Path pathFromOwner(const DomItem &self) const override;
- DomItem containingObject(const DomItem &self) const override;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ std::shared_ptr<OwningItem> makeCopy(DomItem &self) const { return doCopy(self); }
+ Path pathFromOwner() const { return Path(); }
+ Path pathFromOwner(DomItem &) const override final { return Path(); }
+ DomItem containingObject(DomItem &self) const override;
int derivedFrom() const;
virtual int revision() const;
@@ -749,18 +1298,18 @@ public:
virtual bool freeze();
QDateTime frozenAt() const;
- virtual void addError(const DomItem &self, ErrorMessage msg);
+ virtual void addError(DomItem &self, ErrorMessage msg);
void addErrorLocal(ErrorMessage msg);
void clearErrors(ErrorGroups groups = ErrorGroups({}));
// return false if a quick exit was requested
- bool iterateErrors(const DomItem &self, function_ref<bool(DomItem source, ErrorMessage msg)> visitor, Path inPath = Path());
+ bool iterateErrors(DomItem &self, function_ref<bool(DomItem source, ErrorMessage msg)> visitor,
+ Path inPath = Path());
QMultiMap<Path, ErrorMessage> localErrors() const {
QMutexLocker l(mutex());
return m_errors;
}
-
- virtual bool iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor);
+ virtual bool iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor);
QBasicMutex *mutex() const { return &m_mutex; }
private:
@@ -773,162 +1322,52 @@ private:
QMultiMap<Path, ErrorMessage> m_errors;
};
-template <typename T>
-std::shared_ptr<T> DomItem::ownerAs() const {
- if (m_owner && m_owner->kind() == T::kindValue)
- return std::static_pointer_cast<T>(m_owner);
- return nullptr;
-}
-
-template <typename T>
-SimpleObjectWrap SimpleObjectWrap::fromDataObject(
- Path pathFromOwner, T const & val,
- std::function<QCborValue(T const &)> toData,
- const SourceLocation &loc,
- DomType kind,
- DomKind domKind,
- QString typeName)
-{
- QString objectName;
- if (!typeName.isEmpty())
- objectName = typeName;
- else if (kind != kindValue)
- objectName = domTypeToStringMap()[kind];
- else
- objectName = QLatin1String("SimpleObjectWrap<%1>").arg(QLatin1String(typeid(T).name()));
- return SimpleObjectWrap(
- pathFromOwner, QVariant::fromValue(&val),
- [toData, pathFromOwner](DomItem &self, QVariant v, function_ref<bool(Path, DomItem &)> visitor){
- ConstantData data = ConstantData(pathFromOwner, toData(*v.value<T const*>()), ConstantData::Options::FirstMapIsFields);
- return data.iterateDirectSubpaths(self, visitor);
- }, kind, domKind, objectName, loc);
-}
-
-template <typename T>
-SimpleObjectWrap SimpleObjectWrap::fromObjectRef(
- Path pathFromOwner, T &value,
- std::function<bool(DomItem &, T &val, function_ref<bool(Path, DomItem &)>)> directSubpathsIterate,
- const SourceLocation &loc,
- DomType kind,
- QString typeName,
- DomKind domKind)
-{
- return SimpleObjectWrap(
- pathFromOwner, QVariant::fromValue(&value),
- [directSubpathsIterate](DomItem &self, QVariant v, function_ref<bool(Path, DomItem &)> visitor){
- return directSubpathsIterate(self, *v.value<T *>(), visitor);
- },
- kind, domKind,
- ((!typeName.isEmpty()) ? typeName :
- (kind != kindValue) ? domTypeToStringMap()[kind] :
- QStringLiteral(u"SimpleObjectWrap<%1>").arg(QLatin1String(typeid(T).name()))),
- loc);
-}
-
-template <typename T>
-Subpath DomItem::subWrapPath(Path p, T &obj, SourceLocation loc) const {
- return this->subObjectWrap(SimpleObjectWrap::fromObjectRef<T>(
- this->pathFromOwner().path(p),
- obj,
- [](DomItem &self, T &fDef, function_ref<bool(Path, DomItem &)> visitor) {
- return fDef.iterateDirectSubpaths(self, visitor);
- },loc,
- T::kindValue));
-}
-
-template <typename T>
-Subpath DomItem::subWrapField(QString p, T &obj, SourceLocation loc) const {
- return this->subWrapPath<T>(Path::Field(p), obj, loc);
-}
-template <typename T>
-Subpath DomItem::subWrapField(QStringView p, T &obj, SourceLocation loc) const {
- return this->subWrapPath<T>(Path::Field(p), obj, loc);
-}
-template <typename T>
-Subpath DomItem::subWrapKey(QString p, T &obj, SourceLocation loc) const {
- return this->subWrapPath<T>(Path::Key(p), obj, loc);
-}
-template <typename T>
-Subpath DomItem::subWrapKey(QStringView p, T &obj, SourceLocation loc) const {
- return this->subWrapPath<T>(Path::Key(p), obj, loc);
-}
-template <typename T>
-Subpath DomItem::subWrapIndex(index_type i, T &obj, SourceLocation loc) const {
- return this->subWrapPath<T>(Path::Index(i), obj, loc);
+template<typename T>
+std::shared_ptr<T> DomItem::ownerAs()
+{
+ if constexpr (domTypeIsOwningItem(T::kindValue)) {
+ if (m_owner) {
+ if constexpr (T::kindValue == DomType::AttachedInfo) {
+ if (std::holds_alternative<std::shared_ptr<AttachedInfo>>(*m_owner))
+ return std::static_pointer_cast<T>(
+ std::get<std::shared_ptr<AttachedInfo>>(*m_owner));
+ } else if constexpr (T::kindValue == DomType::ExternalItemInfo) {
+ if (std::holds_alternative<std::shared_ptr<ExternalItemInfoBase>>(*m_owner))
+ return std::static_pointer_cast<T>(
+ std::get<std::shared_ptr<ExternalItemInfoBase>>(*m_owner));
+ } else if constexpr (T::kindValue == DomType::ExternalItemPair) {
+ if (std::holds_alternative<std::shared_ptr<ExternalItemPairBase>>(*m_owner))
+ return std::static_pointer_cast<T>(
+ std::get<std::shared_ptr<ExternalItemPairBase>>(*m_owner));
+ } else {
+ if (std::holds_alternative<std::shared_ptr<T>>(*m_owner)) {
+ return std::get<std::shared_ptr<T>>(*m_owner);
+ }
+ }
+ }
+ } else {
+ Q_ASSERT_X(false, "DomItem::ownerAs", "unexpected non owning value in ownerAs");
+ }
+ return std::shared_ptr<T> {};
}
-// mainly for debugging purposes
-class GenericObject: public DomElement {
-public:
- constexpr static DomType kindValue = DomType::GenericObject;
- DomType kind() const override { return kindValue; }
-
- GenericObject(Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation(),
- QMap<QString, GenericObject> subObjects = {},
- QMap<QString, QCborValue> subValues = {}):
- DomElement(pathFromOwner, loc), subObjects(subObjects), subValues(subValues) {}
-
- GenericObject copy() const;
- std::pair<QString, GenericObject> asStringPair() const;
-
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)>) override;
-
- QMap<QString, GenericObject> subObjects;
- QMap<QString, QCborValue> subValues;
-};
-
-// mainly for debugging purposes
-class GenericOwner: public OwningItem {
-protected:
- std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
-public:
- constexpr static DomType kindValue = DomType::GenericOwner;
- DomType kind() const override { return kindValue; }
-
- GenericOwner(Path pathFromTop = Path(), int derivedFrom = 0,
- QMap<QString, GenericObject> subObjects = {},
- QMap<QString, QCborValue> subValues = {}):
- OwningItem(derivedFrom), pathFromTop(pathFromTop), subObjects(subObjects),
- subValues(subValues)
- {}
-
- GenericOwner(Path pathFromTop, int derivedFrom, QDateTime dataRefreshedAt,
- QMap<QString, GenericObject> subObjects = {},
- QMap<QString, QCborValue> subValues = {}):
- OwningItem(derivedFrom, dataRefreshedAt), pathFromTop(pathFromTop), subObjects(subObjects),
- subValues(subValues)
- {}
-
- GenericOwner(const GenericOwner &o);
-
- std::shared_ptr<GenericOwner> makeCopy(const DomItem &self);
- Path canonicalPath(const DomItem &self) const override;
-
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)>) override;
-
- Path pathFromTop;
- QMap<QString, GenericObject> subObjects;
- QMap<QString, QCborValue> subValues;
-};
-
QDebug operator<<(QDebug debug, const DomItem &c);
-
class MutableDomItem {
public:
- operator bool() const {
- return m_owner && base();
- }
- DomType internalKind() const {
- return base().internalKind();
- }
- DomKind domKind() const { return kind2domKind(internalKind()); }
+ using CopyOption = DomItem::CopyOption;
- Path canonicalPath() const
+ explicit operator bool() const
+ {
+ return bool(m_owner);
+ } // this is weaker than item(), but normally correct
+ DomType internalKind() { return item().internalKind(); }
+ QString internalKindStr() { return domTypeToString(internalKind()); }
+ DomKind domKind() { return kind2domKind(internalKind()); }
+
+ Path canonicalPath() { return m_owner.canonicalPath().path(m_pathFromOwner); }
+ MutableDomItem containingObject()
{
- return m_owner.canonicalPath().path(m_pathFromOwner);
- }
- MutableDomItem containingObject() const {
if (m_pathFromOwner)
return MutableDomItem(m_owner, m_pathFromOwner.split().pathToSource);
else {
@@ -937,126 +1376,135 @@ public:
}
}
- MutableDomItem container() const {
+ MutableDomItem container()
+ {
if (m_pathFromOwner)
return MutableDomItem(m_owner, m_pathFromOwner.dropTail());
else {
- return MutableDomItem(base().container());
+ return MutableDomItem(item().container());
}
}
- MutableDomItem component() const {
- return MutableDomItem{base().component()};
- }
- MutableDomItem owner() const {
- return MutableDomItem(m_owner);
- }
- MutableDomItem top() const {
- return MutableDomItem(base().top());
- }
- MutableDomItem environment() const {
- return MutableDomItem(base().environment());
- }
- MutableDomItem universe() const {
- return MutableDomItem(base().universe());
- }
- Path pathFromOwner() const {
- return m_pathFromOwner;
- }
- MutableDomItem operator[](const Path &path) const {
- return MutableDomItem(base()[path]);
- }
- MutableDomItem operator[](QStringView component) const {
- return MutableDomItem(base()[component]);
- }
- MutableDomItem operator[](const QString &component) const {
- return MutableDomItem(base()[component]);
+ MutableDomItem qmlObject(GoTo option = GoTo::Strict,
+ FilterUpOptions fOptions = FilterUpOptions::ReturnOuter)
+ {
+ return MutableDomItem(item().qmlObject(option, fOptions));
}
- MutableDomItem operator[](const char16_t *component) const {
- // to avoid clash with stupid builtin ptrdiff_t[MutableDomItem&], coming from C
- return MutableDomItem(base()[QStringView(component)]);
+ MutableDomItem fileObject(GoTo option = GoTo::Strict)
+ {
+ return MutableDomItem(item().fileObject(option));
}
- MutableDomItem operator[](index_type i) const {
- return MutableDomItem(base().index(i));
+ MutableDomItem rootQmlObject(GoTo option = GoTo::Strict)
+ {
+ return MutableDomItem(item().rootQmlObject(option));
}
+ MutableDomItem globalScope() { return MutableDomItem(item().globalScope()); }
+ MutableDomItem scope() { return MutableDomItem(item().scope()); }
- MutableDomItem path(const Path &p) const {
- return MutableDomItem(base().path(p));
+ MutableDomItem component(GoTo option = GoTo::Strict)
+ {
+ return MutableDomItem { item().component(option) };
}
- MutableDomItem path(const QString &p) const {
- return path(Path::fromString(p));
+ MutableDomItem owner() { return MutableDomItem(m_owner); }
+ MutableDomItem top() { return MutableDomItem(item().top()); }
+ MutableDomItem environment() { return MutableDomItem(item().environment()); }
+ MutableDomItem universe() { return MutableDomItem(item().universe()); }
+ Path pathFromOwner() { return m_pathFromOwner; }
+ MutableDomItem operator[](const Path &path) { return MutableDomItem(item()[path]); }
+ MutableDomItem operator[](QStringView component) { return MutableDomItem(item()[component]); }
+ MutableDomItem operator[](const QString &component)
+ {
+ return MutableDomItem(item()[component]);
}
- MutableDomItem path(QStringView p) const {
- return path(Path::fromString(p));
+ MutableDomItem operator[](const char16_t *component)
+ {
+ // to avoid clash with stupid builtin ptrdiff_t[MutableDomItem&], coming from C
+ return MutableDomItem(item()[QStringView(component)]);
}
+ MutableDomItem operator[](index_type i) { return MutableDomItem(item().index(i)); }
- QList<QString> const fields() const {
- return base().fields();
- }
- MutableDomItem field(QStringView name) const {
- return MutableDomItem(base().field(name));
- }
- index_type indexes() const {
- return base().indexes();
- }
- MutableDomItem index(index_type i) const {
- return MutableDomItem(base().index(i));
- }
+ MutableDomItem path(const Path &p) { return MutableDomItem(item().path(p)); }
+ MutableDomItem path(const QString &p) { return path(Path::fromString(p)); }
+ MutableDomItem path(QStringView p) { return path(Path::fromString(p)); }
- QSet<QString> const keys() const {
- return base().keys();
- }
- MutableDomItem key(QString name) const {
- return MutableDomItem(base().key(name));
- }
+ QList<QString> const fields() { return item().fields(); }
+ MutableDomItem field(QStringView name) { return MutableDomItem(item().field(name)); }
+ index_type indexes() { return item().indexes(); }
+ MutableDomItem index(index_type i) { return MutableDomItem(item().index(i)); }
- QString canonicalFilePath() const { return base().canonicalFilePath(); }
- SourceLocation location() const { return base().location(); }
+ QSet<QString> const keys() { return item().keys(); }
+ MutableDomItem key(QString name) { return MutableDomItem(item().key(name)); }
+ MutableDomItem key(QStringView name) { return key(name.toString()); }
- QCborValue value() const {
- return base().value();
+ void
+ dump(Sink s, int indent = 0,
+ function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter)
+ {
+ item().dump(s, indent, filter);
}
- void dump(Sink sink, int indent = 0) const {
- return base().dump(sink, indent);
- }
- QString toString() const {
- return base().toString();
+ MutableDomItem fileLocations() { return MutableDomItem(item().fileLocations()); }
+ MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected)
+ {
+ return item().makeCopy(option);
}
+ bool commitToBase() { return item().commitToBase(); }
+ QString canonicalFilePath() { return item().canonicalFilePath(); }
- // convenience getters
- QString name() const;
- MutableDomItem qmlChildren() const {
- return MutableDomItem(base().qmlChildren());
- }
- MutableDomItem annotations() const {
- return MutableDomItem(base().annotations());
- }
+ MutableDomItem refreshed() { return MutableDomItem(item().refreshed()); }
- QMultiMap<QString, RequiredProperty> extraRequired() const;
+ QCborValue value() { return item().value(); }
+ QString toString() { return item().toString(); }
-// // OwnigItem elements
- int derivedFrom() const {
- return m_owner.derivedFrom();
- }
- int revision() const {
- return m_owner.revision();
- }
- QDateTime createdAt() const {
- return m_owner.createdAt();
- }
- QDateTime frozenAt() const {
- return m_owner.frozenAt();
- }
- QDateTime lastDataUpdateAt() const {
- return m_owner.lastDataUpdateAt();
+ // convenience getters
+ QString name() { return item().name(); }
+ MutableDomItem pragmas() { return item().pragmas(); }
+ MutableDomItem ids() { return MutableDomItem::item().ids(); }
+ QString idStr() { return item().idStr(); }
+ MutableDomItem propertyDefs() { return MutableDomItem(item().propertyDefs()); }
+ MutableDomItem bindings() { return MutableDomItem(item().bindings()); }
+ MutableDomItem methods() { return MutableDomItem(item().methods()); }
+ MutableDomItem children() { return MutableDomItem(item().children()); }
+ MutableDomItem child(index_type i) { return MutableDomItem(item().child(i)); }
+ MutableDomItem annotations() { return MutableDomItem(item().annotations()); }
+
+ // // OwnigItem elements
+ int derivedFrom() { return m_owner.derivedFrom(); }
+ int revision() { return m_owner.revision(); }
+ QDateTime createdAt() { return m_owner.createdAt(); }
+ QDateTime frozenAt() { return m_owner.frozenAt(); }
+ QDateTime lastDataUpdateAt() { return m_owner.lastDataUpdateAt(); }
+
+ void addError(ErrorMessage msg) { item().addError(msg); }
+ ErrorHandler errorHandler();
+
+ // convenience setters
+ MutableDomItem addPrototypePath(Path prototypePath);
+ MutableDomItem setNextScopePath(Path nextScopePath);
+ MutableDomItem setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs);
+ MutableDomItem setBindings(QMultiMap<QString, Binding> bindings);
+ MutableDomItem setMethods(QMultiMap<QString, MethodInfo> functionDefs);
+ MutableDomItem setChildren(QList<QmlObject> children);
+ MutableDomItem setAnnotations(QList<QmlObject> annotations);
+ MutableDomItem setScript(std::shared_ptr<ScriptExpression> exp);
+ MutableDomItem setCode(QString code);
+ MutableDomItem addPropertyDef(PropertyDefinition propertyDef,
+ AddOption option = AddOption::Overwrite);
+ MutableDomItem addBinding(Binding binding, AddOption option = AddOption::Overwrite);
+ MutableDomItem addMethod(MethodInfo functionDef, AddOption option = AddOption::Overwrite);
+ MutableDomItem addChild(QmlObject child);
+ MutableDomItem addAnnotation(QmlObject child);
+ MutableDomItem addPreComment(const Comment &comment, QString regionName = QString());
+ MutableDomItem addPreComment(const Comment &comment, QStringView regionName)
+ {
+ return addPreComment(comment, regionName.toString());
}
-
- void addError(ErrorMessage msg) const {
- base().addError(msg);
+ MutableDomItem addPostComment(const Comment &comment, QString regionName = QString());
+ MutableDomItem addPostComment(const Comment &comment, QStringView regionName)
+ {
+ return addPostComment(comment, regionName.toString());
}
- ErrorHandler errorHandler() const;
MutableDomItem() = default;
MutableDomItem(DomItem owner, Path pathFromOwner):
@@ -1066,32 +1514,38 @@ public:
m_owner(item.owner()), m_pathFromOwner(item.pathFromOwner())
{}
- std::shared_ptr<DomTop> topPtr() const {
- return m_owner.topPtr();
- }
- std::shared_ptr<OwningItem> owningItemPtr() const {
- return m_owner.owningItemPtr();
- }
+ std::shared_ptr<DomTop> topPtr() { return m_owner.topPtr(); }
+ std::shared_ptr<OwningItem> owningItemPtr() { return m_owner.owningItemPtr(); }
- template <typename T>
- T const*as() const {
- return base().as<T>();
+ template<typename T>
+ T const *as()
+ {
+ return item().as<T>();
}
template <typename T>
T *mutableAs() {
- Q_ASSERT(!m_owner.owningItemPtr()->frozen());
- return base().mutableAs<T>();
+ Q_ASSERT(!m_owner || !m_owner.owningItemPtr()->frozen());
+ return item().mutableAs<T>();
}
- template <typename T>
- std::shared_ptr<T> ownerAs() const {
+ template<typename T>
+ std::shared_ptr<T> ownerAs()
+ {
return m_owner.ownerAs<T>();
}
// it is dangerous to assume it stays valid when updates are preformed...
- DomItem base() const {
- return m_owner.path(m_pathFromOwner);
+ DomItem item() { return m_owner.path(m_pathFromOwner); }
+
+ friend bool operator==(const MutableDomItem o1, const MutableDomItem &o2)
+ {
+ return o1.m_owner == o2.m_owner && o1.m_pathFromOwner == o2.m_pathFromOwner;
+ }
+ friend bool operator!=(const MutableDomItem &o1, const MutableDomItem &o2)
+ {
+ return !(o1 == o2);
}
+
private:
DomItem m_owner;
Path m_pathFromOwner;
@@ -1099,8 +1553,27 @@ private:
QDebug operator<<(QDebug debug, const MutableDomItem &c);
-template <typename K, typename T>
-Path insertUpdatableElementInMultiMap(Path mapPathFromOwner, QMultiMap<K, T> &mmap, K key, const T&value) {
+template<typename K, typename T>
+Path insertUpdatableElementInMultiMap(Path mapPathFromOwner, QMultiMap<K, T> &mmap, K key,
+ const T &value, AddOption option = AddOption::KeepExisting,
+ T **valuePtr = nullptr)
+{
+ if (option == AddOption::Overwrite) {
+ auto it = mmap.find(key);
+ if (it != mmap.end()) {
+ T &v = *it;
+ v = value;
+ if (++it != mmap.end() && it.key() == key) {
+ qWarning() << " requested overwrite of " << key
+ << " that contains aleready multiple entries in" << mapPathFromOwner;
+ }
+ Path newPath = mapPathFromOwner.key(key).index(0);
+ v.updatePathFromOwner(newPath);
+ if (valuePtr)
+ *valuePtr = &v;
+ return newPath;
+ }
+ }
mmap.insert(key, value);
auto it = mmap.find(key);
auto it2 = it;
@@ -1110,21 +1583,27 @@ Path insertUpdatableElementInMultiMap(Path mapPathFromOwner, QMultiMap<K, T> &mm
++it2;
}
Path newPath = mapPathFromOwner.key(key).index(nVal-1);
- T &newComp = *it;
- newComp.updatePathFromOwner(newPath);
+ T &v = *it;
+ v.updatePathFromOwner(newPath);
+ if (valuePtr)
+ *valuePtr = &v;
return newPath;
}
-template <typename T>
-Path appendUpdatableElementInQList(Path listPathFromOwner, QList<T> &list, const T&value) {
+template<typename T>
+Path appendUpdatableElementInQList(Path listPathFromOwner, QList<T> &list, const T &value,
+ T **vPtr = nullptr)
+{
int idx = list.length();
list.append(value);
Path newPath = listPathFromOwner.index(idx);
- list[idx].updatePathFromOwner(newPath);
+ T &targetV = list[idx];
+ targetV.updatePathFromOwner(newPath);
+ if (vPtr)
+ *vPtr = &targetV;
return newPath;
}
-
template <typename T, typename K = QString>
void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, Path newPath)
{
@@ -1136,7 +1615,7 @@ void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, Path newPath)
while (it != end) {
if (i > 0 && name != it.key()) {
Path pName = newPath.key(QString(name));
- foreach (T *el, els)
+ for (T *el : els)
el->updatePathFromOwner(pName.index(--i));
els.clear();
els.append(&(*it));
@@ -1150,7 +1629,7 @@ void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, Path newPath)
++it;
}
Path pName = newPath.key(name);
- foreach (T *el, els)
+ for (T *el : els)
el->updatePathFromOwner(pName.index(--i));
}
@@ -1164,6 +1643,415 @@ void updatePathFromOwnerQList(QList<T> &list, Path newPath)
(it++)->updatePathFromOwner(newPath.index(i++));
}
+constexpr bool domTypeIsObjWrap(DomType k)
+{
+ switch (k) {
+ case DomType::Binding:
+ case DomType::EnumItem:
+ case DomType::ErrorMessage:
+ case DomType::Export:
+ case DomType::Id:
+ case DomType::Import:
+ case DomType::ImportScope:
+ case DomType::MethodInfo:
+ case DomType::MethodParameter:
+ case DomType::ModuleAutoExport:
+ case DomType::Pragma:
+ case DomType::PropertyDefinition:
+ case DomType::Version:
+ case DomType::Comment:
+ case DomType::CommentedElement:
+ case DomType::RegionComments:
+ case DomType::FileLocations:
+ case DomType::UpdatedScriptExpression:
+ return true;
+ default:
+ return false;
+ }
+}
+
+constexpr bool domTypeIsValueWrap(DomType k)
+{
+ switch (k) {
+ case DomType::PropertyInfo:
+ return true;
+ default:
+ return false;
+ }
+}
+
+constexpr bool domTypeIsDomElement(DomType k)
+{
+ switch (k) {
+ case DomType::ModuleScope:
+ case DomType::QmlObject:
+ case DomType::ConstantData:
+ case DomType::SimpleObjectWrap:
+ case DomType::Reference:
+ case DomType::Map:
+ case DomType::List:
+ case DomType::ListP:
+ case DomType::EnumDecl:
+ case DomType::JsResource:
+ case DomType::QmltypesComponent:
+ case DomType::QmlComponent:
+ case DomType::GlobalComponent:
+ case DomType::MockObject:
+ return true;
+ default:
+ return false;
+ }
+}
+
+constexpr bool domTypeIsOwningItem(DomType k)
+{
+ switch (k) {
+ case DomType::ModuleIndex:
+
+ case DomType::MockOwner:
+
+ case DomType::ExternalItemInfo:
+ case DomType::ExternalItemPair:
+
+ case DomType::QmlDirectory:
+ case DomType::QmldirFile:
+ case DomType::JsFile:
+ case DomType::QmlFile:
+ case DomType::QmltypesFile:
+ case DomType::GlobalScope:
+
+ case DomType::ScriptExpression:
+ case DomType::AstComments:
+
+ case DomType::LoadInfo:
+ case DomType::AttachedInfo:
+
+ case DomType::DomEnvironment:
+ case DomType::DomUniverse:
+ return true;
+ default:
+ return false;
+ }
+}
+
+constexpr bool domTypeIsUnattachedOwningItem(DomType k)
+{
+ switch (k) {
+ case DomType::ScriptExpression:
+ case DomType::AstComments:
+ case DomType::AttachedInfo:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template<typename T>
+DomItem DomItem::subValueItem(const PathEls::PathComponent &c, T value,
+ ConstantData::Options options)
+{
+ using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
+ if constexpr (
+ std::is_base_of_v<
+ QCborValue,
+ BaseT> || std::is_base_of_v<QCborArray, BaseT> || std::is_base_of_v<QCborMap, BaseT>) {
+ return DomItem(m_top, m_owner, m_ownerPath,
+ ConstantData(pathFromOwner().appendComponent(c), value, options));
+ } else if constexpr (std::is_same_v<DomItem, BaseT>) {
+ Q_UNUSED(options);
+ return value;
+ } else if constexpr (IsList<T>::value && !std::is_convertible_v<BaseT, QStringView>) {
+ return subListItem(List::fromQList<typename BaseT::value_type>(
+ pathFromOwner().appendComponent(c), value,
+ [options](DomItem &list, const PathEls::PathComponent &p,
+ typename T::value_type &v) { return list.subValueItem(p, v, options); }));
+ } else if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
+ Q_UNUSED(options);
+ return subOwnerItem(c, value);
+ } else {
+ return subDataItem(c, value, options);
+ }
+}
+
+template<typename T>
+DomItem DomItem::subDataItem(const PathEls::PathComponent &c, T value,
+ ConstantData::Options options)
+{
+ using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
+ if constexpr (std::is_same_v<BaseT, ConstantData>) {
+ return this->copy(value);
+ } else if constexpr (std::is_base_of_v<QCborValue, BaseT>) {
+ return DomItem(m_top, m_owner, m_ownerPath,
+ ConstantData(pathFromOwner().appendComponent(c), value, options));
+ } else {
+ return DomItem(
+ m_top, m_owner, m_ownerPath,
+ ConstantData(pathFromOwner().appendComponent(c), QCborValue(value), options));
+ }
+}
+
+template<typename T>
+bool DomItem::dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, T value,
+ ConstantData::Options options)
+{
+ auto lazyWrap = [this, &c, &value, options]() {
+ return this->subValueItem<T>(c, value, options);
+ };
+ return visitor(c, lazyWrap);
+}
+
+template<typename F>
+bool DomItem::dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
+ ConstantData::Options options)
+{
+ auto lazyWrap = [this, &c, &valueF, options]() {
+ return this->subValueItem<decltype(valueF())>(c, valueF(), options);
+ };
+ return visitor(c, lazyWrap);
+}
+
+template<typename T>
+DomItem DomItem::wrap(const PathEls::PathComponent &c, T &obj)
+{
+ using BaseT = std::decay_t<T>;
+ if constexpr (std::is_same_v<QString, BaseT> || std::is_arithmetic_v<BaseT>) {
+ return this->subDataItem(c, QCborValue(obj));
+ } else if constexpr (std::is_same_v<SourceLocation, BaseT>) {
+ return this->subLocationItem(c, obj);
+ } else if constexpr (std::is_same_v<BaseT, Reference>) {
+ Q_ASSERT_X(false, "DomItem::wrap",
+ "wrapping a reference object, probably an error (wrap the target path instead)");
+ return this->copy(obj);
+ } else if constexpr (std::is_same_v<BaseT, ConstantData>) {
+ return this->subDataItem(c, obj);
+ } else if constexpr (std::is_same_v<BaseT, Map>) {
+ return this->subMapItem(obj);
+ } else if constexpr (std::is_same_v<BaseT, List>) {
+ return this->subListItem(obj);
+ } else if constexpr (std::is_base_of_v<ListPBase, BaseT>) {
+ return this->subListItem(obj);
+ } else if constexpr (std::is_same_v<BaseT, SimpleObjectWrap>) {
+ return this->subObjectWrapItem(obj);
+ } else if constexpr (IsDomObject<BaseT>::value) {
+ if constexpr (domTypeIsObjWrap(BaseT::kindValue) || domTypeIsValueWrap(BaseT::kindValue)) {
+ return this->subObjectWrapItem(
+ SimpleObjectWrap::fromObjectRef(this->pathFromOwner().appendComponent(c), obj));
+ } else if constexpr (domTypeIsDomElement(BaseT::kindValue)) {
+ return this->copy(&obj);
+ } else {
+ qCWarning(domLog) << "Unhandled object of type " << domTypeToString(BaseT::kindValue)
+ << " in DomItem::wrap, not using a shared_ptr for an "
+ << "OwningItem, or unexpected wrapped object?";
+ return DomItem();
+ }
+ } else if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
+ if constexpr (domTypeIsOwningItem(BaseT::element_type::kindValue)) {
+ return this->subOwnerItem(c, obj);
+ } else {
+ Q_ASSERT_X(false, "DomItem::wrap", "shared_ptr with non owning item");
+ return DomItem();
+ }
+ } else if constexpr (IsMultiMap<BaseT>::value) {
+ if constexpr (std::is_same_v<typename BaseT::key_type, QString>) {
+ return subMapItem(Map::fromMultiMapRef<typename BaseT::mapped_type>(
+ pathFromOwner().appendComponent(c), obj,
+ [](DomItem &map, const PathEls::PathComponent &p,
+ typename BaseT::mapped_type &el) { return map.wrap(p, el); }));
+ } else {
+ Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)");
+ }
+ } else if constexpr (IsMap<BaseT>::value) {
+ if constexpr (std::is_same_v<typename BaseT::key_type, QString>) {
+ return subMapItem(Map::fromMapRef<typename BaseT::mapped_type>(
+ pathFromOwner().appendComponent(c), obj,
+ [](DomItem &map, const PathEls::PathComponent &p,
+ typename BaseT::mapped_type &el) { return map.wrap(p, el); }));
+ } else {
+ Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)");
+ }
+ } else if constexpr (IsList<BaseT>::value) {
+ if constexpr (IsDomObject<typename BaseT::value_type>::value) {
+ return subListItem(List::fromQListRef<typename BaseT::value_type>(
+ pathFromOwner().appendComponent(c), obj,
+ [](DomItem &list, const PathEls::PathComponent &p,
+ typename BaseT::value_type &el) { return list.wrap(p, el); }));
+ } else {
+ Q_ASSERT_X(false, "DomItem::wrap", "Unsupported list type T");
+ return DomItem();
+ }
+ } else {
+ qCWarning(domLog) << "Cannot wrap " << typeid(BaseT).name();
+ Q_ASSERT_X(false, "DomItem::wrap", "Do not know how to wrap type T");
+ return DomItem();
+ }
+}
+
+template<typename T>
+bool DomItem::dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj)
+{
+ auto lazyWrap = [this, &c, &obj]() { return this->wrap<T>(c, obj); };
+ return visitor(c, lazyWrap);
+}
+
+template<typename T>
+bool ListPT<T>::iterateDirectSubpaths(DomItem &self, DirectVisitor v)
+{
+ index_type len = index_type(m_pList.size());
+ for (index_type i = 0; i < len; ++i) {
+ if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, i); }))
+ return false;
+ }
+ return true;
+}
+
+template<typename T>
+DomItem ListPT<T>::index(DomItem &self, index_type index) const
+{
+ if (index >= 0 && index < m_pList.length())
+ return self.wrap(PathEls::Index(index), *reinterpret_cast<T *>(m_pList.value(index)));
+ return DomItem();
+}
+
+// allow inlining of DomBase
+inline DomKind DomBase::domKind() const
+{
+ return kind2domKind(kind());
+}
+
+inline bool DomBase::iterateDirectSubpathsConst(DomItem &self, DirectVisitor visitor) const
+{
+ Q_ASSERT(self.base() == this);
+ return self.iterateDirectSubpaths(visitor);
+}
+
+inline DomItem DomBase::containingObject(DomItem &self) const
+{
+ Path path = pathFromOwner(self);
+ DomItem base = self.owner();
+ if (!path) {
+ path = canonicalPath(self);
+ base = self;
+ }
+ Source source = path.split();
+ return base.path(source.pathToSource);
+}
+
+inline quintptr DomBase::id() const
+{
+ return quintptr(this);
+}
+
+inline QString DomBase::typeName() const
+{
+ return domTypeToString(kind());
+}
+
+inline QList<QString> DomBase::fields(DomItem &self) const
+{
+ QList<QString> res;
+ self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
+ if (c.kind() == Path::Kind::Field)
+ res.append(c.name());
+ return true;
+ });
+ return res;
+}
+
+inline DomItem DomBase::field(DomItem &self, QStringView name) const
+{
+ DomItem res;
+ self.iterateDirectSubpaths(
+ [&res, name](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
+ if (c.kind() == Path::Kind::Field && c.checkName(name)) {
+ res = obj();
+ return false;
+ }
+ return true;
+ });
+ return res;
+}
+
+inline index_type DomBase::indexes(DomItem &self) const
+{
+ index_type res = 0;
+ self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
+ if (c.kind() == Path::Kind::Index) {
+ index_type i = c.index() + 1;
+ if (res < i)
+ res = i;
+ }
+ return true;
+ });
+ return res;
+}
+
+inline DomItem DomBase::index(DomItem &self, qint64 index) const
+{
+ DomItem res;
+ self.iterateDirectSubpaths(
+ [&res, index](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
+ if (c.kind() == Path::Kind::Index && c.index() == index) {
+ res = obj();
+ return false;
+ }
+ return true;
+ });
+ return res;
+}
+
+inline QSet<QString> const DomBase::keys(DomItem &self) const
+{
+ QSet<QString> res;
+ self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
+ if (c.kind() == Path::Kind::Key)
+ res.insert(c.name());
+ return true;
+ });
+ return res;
+}
+
+inline DomItem DomBase::key(DomItem &self, QString name) const
+{
+ DomItem res;
+ self.iterateDirectSubpaths(
+ [&res, name](const PathEls::PathComponent &c, function_ref<DomItem()> obj) {
+ if (c.kind() == Path::Kind::Key && c.checkName(name)) {
+ res = obj();
+ return false;
+ }
+ return true;
+ });
+ return res;
+}
+
+inline DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, QList<Path> paths)
+{
+ return subListItem(
+ List::fromQList<Path>(pathFromOwner().appendComponent(c), paths,
+ [](DomItem &list, const PathEls::PathComponent &p, Path &el) {
+ return list.subReferenceItem(p, el);
+ }));
+}
+
+inline DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, Path referencedObject)
+{
+ if (domTypeIsOwningItem(internalKind()))
+ return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, Path(c)));
+ else
+ return DomItem(m_top, m_owner, m_ownerPath,
+ Reference(referencedObject, pathFromOwner().appendComponent(c)));
+}
+
+inline DomItem DomItem::subListItem(const List &list)
+{
+ return DomItem(m_top, m_owner, m_ownerPath, list);
+}
+
+inline DomItem DomItem::subMapItem(const Map &map)
+{
+ return DomItem(m_top, m_owner, m_ownerPath, map);
+}
+
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldommock.cpp b/src/qmldom/qqmldommock.cpp
new file mode 100644
index 0000000000..53963b9f56
--- /dev/null
+++ b/src/qmldom/qqmldommock.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#include "qqmldommock_p.h"
+#include "qqmldomitem_p.h"
+#include "qqmldomcomments_p.h"
+
+#include <QtCore/QBasicMutex>
+#include <QtCore/QMutexLocker>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+MockObject MockObject::copy() const
+{
+ QMap<QString, MockObject> newObjs;
+ auto objs = subObjects;
+ auto itO = objs.cbegin();
+ auto endO = objs.cend();
+ while (itO != endO) {
+ newObjs.insert(itO.key(), itO->copy());
+ ++itO;
+ }
+ return MockObject(pathFromOwner(), newObjs, subValues);
+}
+
+std::pair<QString, MockObject> MockObject::asStringPair() const
+{
+ return std::make_pair(pathFromOwner().last().headName(), *this);
+}
+
+bool MockObject::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ static QHash<QString, QString> knownFields;
+ static QBasicMutex m;
+ auto toField = [](QString f) -> QStringView {
+ QMutexLocker l(&m);
+ if (!knownFields.contains(f))
+ knownFields[f] = f;
+ return knownFields[f];
+ };
+ bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
+ auto itV = subValues.begin();
+ auto endV = subValues.end();
+ while (itV != endV) {
+ cont = cont && self.dvValue(visitor, PathEls::Field(toField(itV.key())), *itV);
+ ++itV;
+ }
+ auto itO = subObjects.begin();
+ auto endO = subObjects.end();
+ while (itO != endO) {
+ cont = cont && self.dvItem(visitor, PathEls::Field(toField(itO.key())), [&self, &itO]() {
+ return self.copy(&(*itO));
+ });
+ ++itO;
+ }
+ return cont;
+}
+
+std::shared_ptr<OwningItem> MockOwner::doCopy(DomItem &) const
+{
+ return std::shared_ptr<OwningItem>(new MockOwner(*this));
+}
+
+MockOwner::MockOwner(const MockOwner &o)
+ : OwningItem(o), pathFromTop(o.pathFromTop), subValues(o.subValues)
+{
+ auto objs = o.subObjects;
+ auto itO = objs.cbegin();
+ auto endO = objs.cend();
+ while (itO != endO) {
+ subObjects.insert(itO.key(), itO->copy());
+ ++itO;
+ }
+}
+
+std::shared_ptr<MockOwner> MockOwner::makeCopy(DomItem &self) const
+{
+ return std::static_pointer_cast<MockOwner>(doCopy(self));
+}
+
+Path MockOwner::canonicalPath(DomItem &) const
+{
+ return pathFromTop;
+}
+
+bool MockOwner::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ static QHash<QString, QString> knownFields;
+ static QBasicMutex m;
+ auto toField = [](QString f) -> QStringView {
+ QMutexLocker l(&m);
+ if (!knownFields.contains(f))
+ knownFields[f] = f;
+ return knownFields[f];
+ };
+ {
+ auto itV = subValues.begin();
+ auto endV = subValues.end();
+ while (itV != endV) {
+ if (!self.dvValue(visitor, PathEls::Field(toField(itV.key())), *itV))
+ return false;
+ ++itV;
+ }
+ }
+ {
+ auto itO = subObjects.begin();
+ auto endO = subObjects.end();
+ while (itO != endO) {
+ if (!self.dvItem(visitor, PathEls::Field(toField(itO.key())),
+ [&self, &itO]() { return self.copy(&(*itO)); }))
+ return false;
+ ++itO;
+ }
+ }
+ {
+ auto it = subMaps.begin();
+ auto end = subMaps.end();
+ while (it != end) {
+ if (!self.dvWrapField(visitor, toField(it.key()), it.value()))
+ return false;
+ ++it;
+ }
+ }
+ {
+ auto it = subMultiMaps.begin();
+ auto end = subMultiMaps.end();
+ while (it != end) {
+ if (!self.dvWrapField(visitor, toField(it.key()), it.value()))
+ return false;
+ ++it;
+ }
+ }
+ {
+ auto it = subLists.begin();
+ auto end = subLists.end();
+ while (it != end) {
+ if (!self.dvWrapField(visitor, toField(it.key()), it.value()))
+ return false;
+ ++it;
+ }
+ }
+ return true;
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldommock_p.h b/src/qmldom/qqmldommock_p.h
new file mode 100644
index 0000000000..479113304e
--- /dev/null
+++ b/src/qmldom/qqmldommock_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#ifndef QQMLDOMMOCK_P_H
+#define QQMLDOMMOCK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldomitem_p.h"
+#include "qqmldomconstants_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldomcomments_p.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+
+#include <QtCore/QCborValue>
+#include <QtCore/QCborMap>
+#include <QtCore/QMutexLocker>
+#include <QtCore/QPair>
+
+#include <functional>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+// mainly for debugging purposes
+class MockObject final : public CommentableDomElement
+{
+public:
+ constexpr static DomType kindValue = DomType::MockObject;
+ DomType kind() const override { return kindValue; }
+
+ MockObject(Path pathFromOwner = Path(), QMap<QString, MockObject> subObjects = {},
+ QMap<QString, QCborValue> subValues = {})
+ : CommentableDomElement(pathFromOwner), subObjects(subObjects), subValues(subValues)
+ {
+ }
+
+ MockObject copy() const;
+ std::pair<QString, MockObject> asStringPair() const;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+
+ QMap<QString, MockObject> subObjects;
+ QMap<QString, QCborValue> subValues;
+};
+
+// mainly for debugging purposes
+class MockOwner final : public OwningItem
+{
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+
+public:
+ constexpr static DomType kindValue = DomType::MockOwner;
+ DomType kind() const override { return kindValue; }
+
+ MockOwner(Path pathFromTop = Path(), int derivedFrom = 0,
+ QMap<QString, MockObject> subObjects = {}, QMap<QString, QCborValue> subValues = {},
+ QMap<QString, QMap<QString, MockObject>> subMaps = {},
+ QMap<QString, QMultiMap<QString, MockObject>> subMultiMaps = {},
+ QMap<QString, QList<MockObject>> subLists = {})
+ : OwningItem(derivedFrom),
+ pathFromTop(pathFromTop),
+ subObjects(subObjects),
+ subValues(subValues),
+ subMaps(subMaps),
+ subMultiMaps(subMultiMaps),
+ subLists(subLists)
+ {
+ }
+
+ MockOwner(Path pathFromTop, int derivedFrom, QDateTime dataRefreshedAt,
+ QMap<QString, MockObject> subObjects = {}, QMap<QString, QCborValue> subValues = {})
+ : OwningItem(derivedFrom, dataRefreshedAt),
+ pathFromTop(pathFromTop),
+ subObjects(subObjects),
+ subValues(subValues)
+ {
+ }
+
+ MockOwner(const MockOwner &o);
+
+ std::shared_ptr<MockOwner> makeCopy(DomItem &self) const;
+ Path canonicalPath(DomItem &self) const override;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+
+ Path pathFromTop;
+ QMap<QString, MockObject> subObjects;
+ QMap<QString, QCborValue> subValues;
+ QMap<QString, QMap<QString, MockObject>> subMaps;
+ QMap<QString, QMultiMap<QString, MockObject>> subMultiMaps;
+ QMap<QString, QList<MockObject>> subLists;
+};
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
+#endif // QQMLDOMELEMENTS_P_H
diff --git a/src/qmldom/qqmldommoduleindex.cpp b/src/qmldom/qqmldommoduleindex.cpp
new file mode 100644
index 0000000000..700537d287
--- /dev/null
+++ b/src/qmldom/qqmldommoduleindex.cpp
@@ -0,0 +1,430 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#include "qqmldommoduleindex_p.h"
+#include "qqmldomtop_p.h"
+#include "qqmldomelements_p.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QScopeGuard>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+static ErrorGroups myVersioningErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Exports"),
+ NewErrorGroup("Version") } };
+ return res;
+}
+
+static ErrorGroups myExportErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Exports") } };
+ return res;
+}
+
+bool ModuleScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = true;
+ cont = cont && self.dvValueField(visitor, Fields::uri, uri);
+ cont = cont && self.dvWrapField(visitor, Fields::version, version);
+ cont = cont && self.dvItemField(visitor, Fields::exports, [this, &self]() {
+ int minorVersion = version.minorVersion;
+ return self.subMapItem(Map(
+ self.pathFromOwner().field(Fields::exports),
+ [minorVersion](DomItem &mapExp, QString name) -> DomItem {
+ DomItem mapExpOw = mapExp.owner();
+ QList<DomItem> exports =
+ mapExp.ownerAs<ModuleIndex>()->exportsWithNameAndMinorVersion(
+ mapExpOw, name, minorVersion);
+ return mapExp.subListItem(List::fromQList<DomItem>(
+ mapExp.pathFromOwner().key(name), exports,
+ [](DomItem &, const PathEls::PathComponent &, DomItem &el) {
+ return el;
+ },
+ ListOptions::Normal));
+ },
+ [](DomItem &mapExp) {
+ DomItem mapExpOw = mapExp.owner();
+ return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
+ },
+ QLatin1String("List<Exports>")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::symbols, [&self]() {
+ Path basePath = Path::Current(PathCurrent::Obj).field(Fields::exports);
+ return self.subMapItem(Map(
+ self.pathFromOwner().field(Fields::symbols),
+ [basePath](DomItem &mapExp, QString name) -> DomItem {
+ QList<Path> symb({ basePath.key(name) });
+ return mapExp.subReferencesItem(PathEls::Key(name), symb);
+ },
+ [](DomItem &mapExp) {
+ DomItem mapExpOw = mapExp.owner();
+ return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
+ },
+ QLatin1String("List<References>")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::autoExports, [this, &self]() {
+ return containingObject(self).field(Fields::autoExports);
+ });
+ return cont;
+}
+
+std::shared_ptr<OwningItem> ModuleIndex::doCopy(DomItem &) const
+{
+ return std::shared_ptr<OwningItem>(new ModuleIndex(*this));
+}
+
+ModuleIndex::ModuleIndex(const ModuleIndex &o)
+ : OwningItem(o), m_uri(o.uri()), m_majorVersion(o.majorVersion())
+{
+ QMap<int, ModuleScope *> scopes;
+ {
+ QMutexLocker l2(o.mutex());
+ m_qmltypesFilesPaths += o.m_qmltypesFilesPaths;
+ m_qmldirPaths += o.m_qmldirPaths;
+ m_directoryPaths += o.m_directoryPaths;
+ scopes = o.m_moduleScope;
+ }
+ auto it = scopes.begin();
+ auto end = scopes.end();
+ while (it != end) {
+ ensureMinorVersion((*it)->version.minorVersion);
+ ++it;
+ }
+ QMutexLocker l(mutex());
+}
+
+ModuleIndex::~ModuleIndex()
+{
+ QMap<int, ModuleScope *> scopes;
+ {
+ QMutexLocker l(mutex());
+ scopes = m_moduleScope;
+ m_moduleScope.clear();
+ }
+ auto it = scopes.begin();
+ auto end = scopes.end();
+ while (it != end) {
+ free(*it);
+ ++it;
+ }
+}
+
+bool ModuleIndex::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = self.dvValueField(visitor, Fields::uri, uri());
+ cont = cont && self.dvValueField(visitor, Fields::majorVersion, majorVersion());
+ cont = cont && self.dvItemField(visitor, Fields::moduleScope, [this, &self]() {
+ return self.subMapItem(Map(
+ pathFromOwner(self).field(Fields::moduleScope),
+ [](DomItem &map, QString minorVersionStr) {
+ bool ok;
+ int minorVersion = minorVersionStr.toInt(&ok);
+ if (minorVersionStr.isEmpty()
+ || minorVersionStr.compare(u"Latest", Qt::CaseInsensitive) == 0)
+ minorVersion = Version::Latest;
+ else if (!ok)
+ return DomItem();
+ return map.copy(map.ownerAs<ModuleIndex>()->ensureMinorVersion(minorVersion));
+ },
+ [this](DomItem &) {
+ QSet<QString> res;
+ for (int el : minorVersions())
+ if (el >= 0)
+ res.insert(QString::number(el));
+ if (!minorVersions().isEmpty())
+ res.insert(QString());
+ return res;
+ },
+ QLatin1String("Map<List<Exports>>")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::sources, [this, &self]() {
+ return self.subReferencesItem(PathEls::Field(Fields::sources), sources());
+ });
+ cont = cont && self.dvValueLazyField(visitor, Fields::autoExports, [this, &self]() {
+ return autoExports(self);
+ });
+ return cont;
+}
+
+QSet<QString> ModuleIndex::exportNames(DomItem &self) const
+{
+ QSet<QString> res;
+ QList<Path> mySources = sources();
+ for (int i = 0; i < mySources.length(); ++i) {
+ DomItem source = self.path(mySources.at(i));
+ res += source.field(Fields::exports).keys();
+ }
+ return res;
+}
+
+QList<DomItem> ModuleIndex::autoExports(DomItem &self) const
+{
+ QList<DomItem> res;
+ Path selfPath = canonicalPath(self).field(Fields::autoExports);
+ RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
+ QList<Path> cachedPaths;
+ switch (cached.cached) {
+ case RefCacheEntry::Cached::None:
+ case RefCacheEntry::Cached::First:
+ break;
+ case RefCacheEntry::Cached::All:
+ cachedPaths += cached.canonicalPaths;
+ if (cachedPaths.isEmpty())
+ return res;
+ }
+ DomItem env = self.environment();
+ if (!cachedPaths.isEmpty()) {
+ bool outdated = false;
+ for (Path p : cachedPaths) {
+ DomItem newEl = env.path(p);
+ if (!newEl) {
+ outdated = true;
+ qWarning() << "referenceCache outdated, reference at " << selfPath
+ << " leads to invalid path " << p;
+ break;
+ } else {
+ res.append(newEl);
+ }
+ }
+ if (outdated) {
+ res.clear();
+ } else {
+ return res;
+ }
+ }
+ QList<Path> mySources = sources();
+ QSet<QString> knownAutoImportUris;
+ QList<ModuleAutoExport> knownExports;
+ for (Path p : mySources) {
+ DomItem autoExports = self.path(p).field(Fields::autoExports);
+ for (DomItem i : autoExports.values()) {
+ if (const ModuleAutoExport *iPtr = i.as<ModuleAutoExport>()) {
+ if (!knownAutoImportUris.contains(iPtr->import.uri)
+ || !knownExports.contains(*iPtr)) {
+ knownAutoImportUris.insert(iPtr->import.uri);
+ knownExports.append(*iPtr);
+ res.append(i);
+ cachedPaths.append(i.canonicalPath());
+ }
+ }
+ }
+ }
+ RefCacheEntry::addForPath(self, selfPath,
+ RefCacheEntry { RefCacheEntry::Cached::All, cachedPaths });
+ return res;
+}
+
+QList<DomItem> ModuleIndex::exportsWithNameAndMinorVersion(DomItem &self, QString name,
+ int minorVersion) const
+{
+ Path myPath = Paths::moduleScopePath(uri(), Version(majorVersion(), minorVersion))
+ .field(Fields::exports)
+ .key(name);
+ QList<Path> mySources = sources();
+ QList<DomItem> res;
+ QList<DomItem> undef;
+ if (minorVersion < 0)
+ minorVersion = std::numeric_limits<int>::max();
+ int vNow = Version::Undefined;
+ for (int i = 0; i < mySources.length(); ++i) {
+ DomItem source = self.path(mySources.at(i));
+ DomItem exports = source.field(Fields::exports).key(name);
+ int nExports = exports.indexes();
+ if (nExports == 0)
+ continue;
+ for (int j = 0; j < nExports; ++j) {
+ DomItem exportItem = exports.index(j);
+ if (!exportItem)
+ continue;
+ Version const *versionPtr = exportItem.field(Fields::version).as<Version>();
+ if (versionPtr == nullptr || !versionPtr->isValid()) {
+ undef.append(exportItem);
+ } else {
+ if (majorVersion() < 0)
+ self.addError(myVersioningErrors()
+ .error(tr("Module %1 (unversioned) has versioned entries "
+ "for '%2' from %3")
+ .arg(uri(), name,
+ source.canonicalPath().toString()))
+ .withPath(myPath));
+ if ((versionPtr->majorVersion == majorVersion()
+ || versionPtr->majorVersion == Version::Undefined)
+ && versionPtr->minorVersion >= vNow
+ && versionPtr->minorVersion <= minorVersion) {
+ if (versionPtr->minorVersion > vNow)
+ res.clear();
+ res.append(exportItem);
+ vNow = versionPtr->minorVersion;
+ }
+ }
+ }
+ }
+ if (!undef.isEmpty()) {
+ if (!res.isEmpty()) {
+ self.addError(myVersioningErrors()
+ .error(tr("Module %1 (major version %2) has versioned and "
+ "unversioned entries for '%3'")
+ .arg(uri(), QString::number(majorVersion()), name))
+ .withPath(myPath));
+ return res + undef;
+ } else {
+ return undef;
+ }
+ }
+ return res;
+}
+
+QList<Path> ModuleIndex::sources() const
+{
+ QList<Path> res;
+ QMutexLocker l(mutex());
+ res += m_qmltypesFilesPaths;
+ if (!m_qmldirPaths.isEmpty())
+ res += m_qmldirPaths.first();
+ else if (!m_directoryPaths.isEmpty())
+ res += m_directoryPaths.first();
+ return res;
+}
+
+ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
+{
+ if (minorVersion < 0)
+ minorVersion = Version::Latest;
+ {
+ QMutexLocker l(mutex());
+ auto it = m_moduleScope.find(minorVersion);
+ if (it != m_moduleScope.end())
+ return *it;
+ }
+ ModuleScope *res = nullptr;
+ ModuleScope *newScope = new ModuleScope(m_uri, Version(majorVersion(), minorVersion));
+ auto cleanup = qScopeGuard([&newScope] { free(newScope); });
+ {
+ QMutexLocker l(mutex());
+ auto it = m_moduleScope.find(minorVersion);
+ if (it != m_moduleScope.end()) {
+ res = *it;
+ } else {
+ res = newScope;
+ newScope = nullptr;
+ m_moduleScope.insert(minorVersion, res);
+ }
+ }
+ return res;
+}
+
+void ModuleIndex::mergeWith(std::shared_ptr<ModuleIndex> o)
+{
+ if (o) {
+ QList<Path> qmltypesPaths;
+ QMap<int, ModuleScope *> scopes;
+ {
+ QMutexLocker l2(o->mutex());
+ qmltypesPaths = o->m_qmltypesFilesPaths;
+ scopes = o->m_moduleScope;
+ }
+ {
+ QMutexLocker l(mutex());
+ for (Path qttPath : qmltypesPaths) {
+ if (!m_qmltypesFilesPaths.contains((qttPath)))
+ m_qmltypesFilesPaths.append(qttPath);
+ }
+ }
+ auto it = scopes.begin();
+ auto end = scopes.end();
+ while (it != end) {
+ ensureMinorVersion((*it)->version.minorVersion);
+ ++it;
+ }
+ }
+}
+
+QList<Path> ModuleIndex::qmldirsToLoad(DomItem &self)
+{
+ Q_ASSERT(m_qmldirPaths.isEmpty() && "ModuleIndex::qmldirsToLoad called twice");
+ DomItem env = self.environment();
+ std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
+ QStringList subPathComponents = uri().split(u'.');
+ QString subPath = subPathComponents.join(u'/');
+ QString logicalPath;
+ QString subPathV = subPath + QChar::fromLatin1('.') + QString::number(majorVersion())
+ + QLatin1String("/qmldir");
+ QString dirPath;
+ if (majorVersion() >= 0) {
+ for (QString path : envPtr->loadPaths()) {
+ QDir dir(path);
+ QFileInfo fInfo(dir.filePath(subPathV));
+ if (fInfo.isFile()) {
+ logicalPath = subPathV;
+ dirPath = fInfo.canonicalFilePath();
+ break;
+ }
+ }
+ }
+ if (dirPath.isEmpty()) {
+ for (QString path : envPtr->loadPaths()) {
+ QDir dir(path);
+ QFileInfo fInfo(dir.filePath(subPath + QLatin1String("/qmldir")));
+ if (fInfo.isFile()) {
+ logicalPath = subPath + QLatin1String("/qmldir");
+ dirPath = fInfo.canonicalFilePath();
+ break;
+ }
+ }
+ }
+ if (!dirPath.isEmpty()) {
+ QMutexLocker l(mutex());
+ m_qmldirPaths.append(Paths::qmldirFilePath(dirPath));
+ } else if (uri() != u"QML") {
+ addErrorLocal(myExportErrors()
+ .warning(tr("Failed to find main qmldir file for %1 %2")
+ .arg(uri(), QString::number(majorVersion())))
+ .handle());
+ }
+ return qmldirPaths();
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldommoduleindex_p.h b/src/qmldom/qqmldommoduleindex_p.h
new file mode 100644
index 0000000000..ae49fb90d1
--- /dev/null
+++ b/src/qmldom/qqmldommoduleindex_p.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#ifndef QQMLDOMMODULEINDEX_P_H
+#define QQMLDOMMODULEINDEX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldomelements_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+class QMLDOM_EXPORT ModuleScope final : public DomBase
+{
+public:
+ constexpr static DomType kindValue = DomType::ModuleScope;
+ DomType kind() const override { return kindValue; }
+
+ ModuleScope(QString uri = QString(), const Version &version = Version())
+ : uri(uri), version(version)
+ {
+ }
+
+ Path pathFromOwner() const
+ {
+ return Path::Field(Fields::moduleScope)
+ .key(version.isValid() ? QString::number(version.minorVersion) : QString());
+ }
+ Path pathFromOwner(DomItem &) const override { return pathFromOwner(); }
+ Path canonicalPath(DomItem &self) const override
+ {
+ return self.owner().canonicalPath().path(pathFromOwner());
+ }
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+
+ QString uri;
+ Version version;
+};
+
+class QMLDOM_EXPORT ModuleIndex final : public OwningItem
+{
+ Q_DECLARE_TR_FUNCTIONS(ModuleIndex);
+
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+
+public:
+ enum class Status { NotLoaded, Loading, Loaded };
+ constexpr static DomType kindValue = DomType::ModuleIndex;
+ DomType kind() const override { return kindValue; }
+
+ ModuleIndex(QString uri, int majorVersion, int derivedFrom = 0,
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0))
+ : OwningItem(derivedFrom, lastDataUpdateAt), m_uri(uri), m_majorVersion(majorVersion)
+ {
+ }
+
+ ModuleIndex(const ModuleIndex &o);
+
+ ~ModuleIndex();
+
+ std::shared_ptr<ModuleIndex> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<ModuleIndex>(doCopy(self));
+ }
+
+ Path canonicalPath(DomItem &) const override
+ {
+ return Paths::moduleIndexPath(uri(), majorVersion());
+ }
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+
+ QSet<QString> exportNames(DomItem &self) const;
+
+ QList<DomItem> exportsWithNameAndMinorVersion(DomItem &self, QString name,
+ int minorVersion) const;
+
+ QString uri() const { return m_uri; }
+ int majorVersion() const { return m_majorVersion; }
+ QList<Path> sources() const;
+
+ QList<int> minorVersions() const
+ {
+ QMutexLocker l(mutex());
+ return m_moduleScope.keys();
+ }
+ ModuleScope *ensureMinorVersion(int minorVersion);
+ void mergeWith(std::shared_ptr<ModuleIndex> o);
+ void addQmltypeFilePath(Path p)
+ {
+ QMutexLocker l(mutex());
+ if (!m_qmltypesFilesPaths.contains(p))
+ m_qmltypesFilesPaths.append(p);
+ }
+
+ QList<Path> qmldirsToLoad(DomItem &self);
+ QList<Path> qmltypesFilesPaths() const
+ {
+ QMutexLocker l(mutex());
+ return m_qmltypesFilesPaths;
+ }
+ QList<Path> qmldirPaths() const
+ {
+ QMutexLocker l(mutex());
+ return m_qmldirPaths;
+ }
+ QList<Path> directoryPaths() const
+ {
+ QMutexLocker l(mutex());
+ return m_directoryPaths;
+ }
+ QList<DomItem> autoExports(DomItem &self) const;
+
+private:
+ QString m_uri;
+ int m_majorVersion;
+
+ QList<Path> m_qmltypesFilesPaths;
+ QList<Path> m_qmldirPaths;
+ QList<Path> m_directoryPaths;
+ QMap<int, ModuleScope *> m_moduleScope;
+};
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
+#endif // QQMLDOMMODULEINDEX_P_H
diff --git a/src/qmldom/qqmldompath_p.h b/src/qmldom/qqmldompath_p.h
index 7d3d751183..7fe68428d7 100644
--- a/src/qmldom/qqmldompath_p.h
+++ b/src/qmldom/qqmldompath_p.h
@@ -338,7 +338,6 @@ public:
PathComponent(const Current &o): data(o) {}
PathComponent(const Any &o): data(o) {}
PathComponent(const Filter &o): data(o) {}
-
private:
friend class QQmlJS::Dom::Path;
friend class QQmlJS::Dom::PathEls::TestPaths;
@@ -467,12 +466,13 @@ public:
// namespace, so it cam be reopened to add more entries
namespace Fields{
QMLDOM_FIELD(access);
+QMLDOM_FIELD(allSources);
QMLDOM_FIELD(annotations);
QMLDOM_FIELD(astComments);
QMLDOM_FIELD(astRelocatableDump);
QMLDOM_FIELD(attachedType);
QMLDOM_FIELD(attachedTypeName);
-QMLDOM_FIELD(autoExport);
+QMLDOM_FIELD(autoExports);
QMLDOM_FIELD(base);
QMLDOM_FIELD(bindingType);
QMLDOM_FIELD(bindings);
@@ -502,6 +502,7 @@ QMLDOM_FIELD(errors);
QMLDOM_FIELD(exportSource);
QMLDOM_FIELD(exports);
QMLDOM_FIELD(expr);
+QMLDOM_FIELD(expressionType);
QMLDOM_FIELD(fileLocationsTree);
QMLDOM_FIELD(fileName);
QMLDOM_FIELD(fullRegion);
@@ -718,7 +719,6 @@ public:
static int cmp(const Path &p1, const Path &p2);
Component component(int i) const;
-
private:
explicit Path(quint16 endOffset, quint16 length, std::shared_ptr<PathEls::PathData> data);
friend class QQmlJS::Dom::PathEls::TestPaths;
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
index 21a110598a..b1f410e102 100644
--- a/src/qmldom/qqmldomtop.cpp
+++ b/src/qmldom/qqmldomtop.cpp
@@ -36,6 +36,11 @@
** $QT_END_LICENSE$
**/
#include "qqmldomtop_p.h"
+#include "qqmldomexternalitems_p.h"
+#include "qqmldommock_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldomastcreator_p.h"
+#include "qqmldommoduleindex_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -43,14 +48,19 @@
#include <QtQml/private/qqmljsastvisitor_p.h>
#include <QtQml/private/qqmljsast_p.h>
+#include <QtCore/QAtomicInt>
+#include <QtCore/QBasicMutex>
+#include <QtCore/QCborArray>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
-#include <QtCore/QScopeGuard>
-#include <QtCore/QRegularExpression>
#include <QtCore/QPair>
-#include <QtCore/QCborArray>
-#include <QtCore/QDebug>
-#include <QtCore/QBasicMutex>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QScopeGuard>
+#if QT_FEATURE_thread
+# include <QtCore/QThread>
+#endif
#include <memory>
@@ -75,29 +85,34 @@ using std::shared_ptr;
if force is true the file is always read
*/
-Path DomTop::pathFromOwner(const DomItem &) const
-{
- return Path();
-}
-
-Path DomTop::canonicalPath(const DomItem &) const
+Path DomTop::canonicalPath(DomItem &) const
{
return canonicalPath();
}
-DomItem DomTop::containingObject(const DomItem &) const
+DomItem DomTop::containingObject(DomItem &) const
{
return DomItem();
}
-bool DomTop::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool DomTop::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
+ static QHash<QString, QString> knownFields;
+ static QBasicMutex m;
+ auto toField = [](QString f) mutable -> QStringView {
+ QMutexLocker l(&m);
+ if (!knownFields.contains(f))
+ knownFields[f] = f;
+ return knownFields[f];
+ };
bool cont = true;
auto objs = m_extraOwningItems;
auto itO = objs.cbegin();
auto endO = objs.cend();
while (itO != endO) {
- cont = cont && self.copy(*itO).toSubField(itO.key()).visit(visitor);
+ cont = cont && self.dvItemField(visitor, toField(itO.key()), [&self, &itO]() {
+ return std::visit([&self](auto &&el) { return self.copy(el); }, *itO);
+ });
++itO;
}
return cont;
@@ -109,22 +124,13 @@ void DomTop::clearExtraOwningItems()
m_extraOwningItems.clear();
}
-QMap<QString, std::shared_ptr<OwningItem> > DomTop::extraOwningItems() const
+QMap<QString, OwnerT> DomTop::extraOwningItems() const
{
QMutexLocker l(mutex());
- QMap<QString, std::shared_ptr<OwningItem> > res = m_extraOwningItems;
+ QMap<QString, OwnerT> res = m_extraOwningItems;
return res;
}
-void DomTop::setExtraOwningItem(QString fieldName, std::shared_ptr<OwningItem> item)
-{
- QMutexLocker l(mutex());
- if (!item)
- m_extraOwningItems.remove(fieldName);
- else
- m_extraOwningItems.insert(fieldName, item);
-}
-
/*!
\class QQmlJS::Dom::DomUniverse
@@ -149,34 +155,86 @@ DomUniverse::DomUniverse(QString universeName, Options options):
m_name(universeName), m_options(options)
{}
+std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(std::shared_ptr<DomUniverse> univ)
+{
+ static QAtomicInt counter(0);
+ if (univ)
+ return univ;
+ return std::shared_ptr<DomUniverse>(
+ new DomUniverse(QLatin1String("universe") + QString::number(++counter)));
+}
+
+DomItem DomUniverse::create(QString universeName, Options options)
+{
+ std::shared_ptr<DomUniverse> res(new DomUniverse(universeName, options));
+ return DomItem(res);
+}
+
Path DomUniverse::canonicalPath() const
{
return Path::Root(u"universe");
}
-bool DomUniverse::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool DomUniverse::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
bool cont = true;
cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
- cont = cont && self.subDataField(Fields::name, name()).visit(visitor);
- cont = cont && self.subDataField(Fields::options, int(options())).visit(visitor);
- QQueue<ParsingTask> q = queue();
- cont = cont && self.subList(
- List(Path::Field(Fields::queue),
- [q](const DomItem &list, index_type i){
- if (i >= 0 && i < q.length())
- return list.subDataIndex(i, q.at(i).toCbor(), ConstantData::Options::FirstMapIsFields).item;
- else
- return DomItem();
- }, [q](const DomItem &){
- return index_type(q.length());
- }, nullptr, QLatin1String("ParsingTask"))
- ).visit(visitor);
-
+ cont = cont && self.dvValueField(visitor, Fields::name, name());
+ cont = cont && self.dvValueField(visitor, Fields::options, int(options()));
+ cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::globalScopeWithName),
+ [this](DomItem &map, QString key) { return map.copy(globalScopeWithName(key)); },
+ [this](DomItem &) { return globalScopeNames(); }, QLatin1String("GlobalScope")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmlDirectoryWithPath),
+ [this](DomItem &map, QString key) { return map.copy(qmlDirectoryWithPath(key)); },
+ [this](DomItem &) { return qmlDirectoryPaths(); }, QLatin1String("QmlDirectory")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmldirFileWithPath),
+ [this](DomItem &map, QString key) { return map.copy(qmldirFileWithPath(key)); },
+ [this](DomItem &) { return qmldirFilePaths(); }, QLatin1String("QmldirFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmlFileWithPath),
+ [this](DomItem &map, QString key) { return map.copy(qmlFileWithPath(key)); },
+ [this](DomItem &) { return qmlFilePaths(); }, QLatin1String("QmlFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::jsFileWithPath),
+ [this](DomItem &map, QString key) { return map.copy(jsFileWithPath(key)); },
+ [this](DomItem &) { return jsFilePaths(); }, QLatin1String("JsFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmltypesFileWithPath),
+ [this](DomItem &map, QString key) { return map.copy(qmltypesFileWithPath(key)); },
+ [this](DomItem &) { return qmltypesFilePaths(); }, QLatin1String("QmltypesFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::queue, [this, &self]() {
+ QQueue<ParsingTask> q = queue();
+ return self.subListItem(List(
+ Path::Field(Fields::queue),
+ [q](DomItem &list, index_type i) {
+ if (i >= 0 && i < q.length())
+ return list.subDataItem(PathEls::Index(i), q.at(i).toCbor(),
+ ConstantData::Options::FirstMapIsFields);
+ else
+ return DomItem();
+ },
+ [q](DomItem &) { return index_type(q.length()); }, nullptr,
+ QLatin1String("ParsingTask")));
+ });
return cont;
}
-std::shared_ptr<OwningItem> DomUniverse::doCopy(const DomItem &) const
+std::shared_ptr<OwningItem> DomUniverse::doCopy(DomItem &) const
{
QRegularExpression r(QRegularExpression::anchoredPattern(QLatin1String(R"(.*Copy([0-9]*)$)")));
auto m = r.match(m_name);
@@ -185,16 +243,19 @@ std::shared_ptr<OwningItem> DomUniverse::doCopy(const DomItem &) const
newName = QStringLiteral(u"%1Copy%2").arg(m_name).arg(m.captured(1).toInt() + 1);
else
newName = m_name + QLatin1String("Copy");
- auto res = std::make_shared<DomUniverse>(newName);
+ auto res = std::shared_ptr<DomUniverse>(new DomUniverse(newName));
return res;
}
-void DomUniverse::loadFile(const DomItem &self, QString filePath, QString logicalPath, Callback callback, LoadOptions loadOptions)
+void DomUniverse::loadFile(DomItem &self, QString filePath, QString logicalPath, Callback callback,
+ LoadOptions loadOptions)
{
loadFile(self, filePath, logicalPath, QString(), QDateTime::fromMSecsSinceEpoch(0), callback, loadOptions);
}
-void DomUniverse::loadFile(const DomItem &self, QString canonicalFilePath, QString logicalPath, QString code, QDateTime codeDate, Callback callback, LoadOptions loadOptions)
+void DomUniverse::loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath,
+ QString code, QDateTime codeDate, Callback callback,
+ LoadOptions loadOptions)
{
if (canonicalFilePath.endsWith(u".qml", Qt::CaseInsensitive) ||
canonicalFilePath.endsWith(u".qmlannotation", Qt::CaseInsensitive) ||
@@ -231,18 +292,24 @@ void DomUniverse::loadFile(const DomItem &self, QString canonicalFilePath, QStri
codeDate,
self.ownerAs<DomUniverse>(),
callback});
+ } else if (QFileInfo(canonicalFilePath).isDir()) {
+ m_queue.enqueue(ParsingTask { QDateTime::currentDateTime(), loadOptions,
+ DomType::QmlDirectory, canonicalFilePath, logicalPath, code,
+ codeDate, self.ownerAs<DomUniverse>(), callback });
} else {
self.addError(myErrors().error(tr("Ignoring request to load file of unknown type %1, calling callback immediately").arg(canonicalFilePath)).handle());
Q_ASSERT(false && "loading non supported file type");
- callback(Path(), DomItem(), DomItem());
+ callback(Path(), DomItem::empty, DomItem::empty);
return;
}
if (m_options & Option::SingleThreaded)
execQueue(); // immediate execution in the same thread
}
-template <typename T>
-QPair<std::shared_ptr<ExternalItemPair<T>>,std::shared_ptr<ExternalItemPair<T>>> updateEntry(const DomItem &univ, std::shared_ptr<T> newItem, QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &map, QBasicMutex *mutex)
+template<typename T>
+QPair<std::shared_ptr<ExternalItemPair<T>>, std::shared_ptr<ExternalItemPair<T>>>
+updateEntry(DomItem &univ, std::shared_ptr<T> newItem,
+ QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &map, QBasicMutex *mutex)
{
std::shared_ptr<ExternalItemPair<T>> oldValue;
std::shared_ptr<ExternalItemPair<T>> newValue;
@@ -262,7 +329,8 @@ QPair<std::shared_ptr<ExternalItemPair<T>>,std::shared_ptr<ExternalItemPair<T>>>
} else if (oldValue->current->lastDataUpdateAt() > newItem->lastDataUpdateAt()) {
newValue = oldValue;
} else {
- newValue = oldValue->makeCopy(univ.copy(oldValue));
+ DomItem oldValueObj = univ.copy(oldValue);
+ newValue = oldValue->makeCopy(oldValueObj);
newValue->current = newItem;
newValue->currentExposedAt = now;
if (newItem->isValid()) {
@@ -272,8 +340,8 @@ QPair<std::shared_ptr<ExternalItemPair<T>>,std::shared_ptr<ExternalItemPair<T>>>
it = map.insert(it, canonicalPath, newValue);
}
} else {
- newValue = std::make_shared<ExternalItemPair<T>>
- (newItem->isValid() ? newItem : std::shared_ptr<T>(), newItem, now, now);
+ newValue = std::shared_ptr<ExternalItemPair<T>>(new ExternalItemPair<T>(
+ (newItem->isValid() ? newItem : std::shared_ptr<T>()), newItem, now, now));
map.insert(canonicalPath, newValue);
}
}
@@ -287,7 +355,459 @@ void DomUniverse::execQueue()
if (!topPtr) {
myErrors().error(tr("Ignoring callback for loading of %1: universe is not valid anymore").arg(t.canonicalPath)).handle();
}
- Q_ASSERT(false && "Unhandled kind in queue");
+ QString canonicalPath = t.canonicalPath;
+ QString code = t.contents;
+ QDateTime contentDate = t.contentsDate;
+ bool skipParse = false;
+ DomItem oldValue; // old ExternalItemPair (might be empty, or equal to newValue)
+ DomItem newValue; // current ExternalItemPair
+ DomItem univ = DomItem(topPtr);
+ QFileInfo path(canonicalPath);
+ QList<ErrorMessage> messages;
+
+ if (t.kind == DomType::QmlFile || t.kind == DomType::QmltypesFile
+ || t.kind == DomType::QmldirFile || t.kind == DomType::QmlDirectory) {
+ auto getValue = [&t, this, &canonicalPath]() -> std::shared_ptr<ExternalItemPairBase> {
+ if (t.kind == DomType::QmlFile)
+ return m_qmlFileWithPath.value(canonicalPath);
+ else if (t.kind == DomType::QmltypesFile)
+ return m_qmlFileWithPath.value(canonicalPath);
+ else if (t.kind == DomType::QmldirFile)
+ return m_qmlFileWithPath.value(canonicalPath);
+ else if (t.kind == DomType::QmlDirectory)
+ return m_qmlDirectoryWithPath.value(canonicalPath);
+ else
+ Q_ASSERT(false);
+ return {};
+ };
+ if (code.isEmpty()) {
+ QFile file(canonicalPath);
+ canonicalPath = path.canonicalFilePath();
+ if (canonicalPath.isEmpty()) {
+ messages.append(myErrors().error(tr("Non existing path %1").arg(t.canonicalPath)));
+ canonicalPath = t.canonicalPath;
+ }
+ {
+ QMutexLocker l(mutex());
+ auto value = getValue();
+ if (!(t.loadOptions & LoadOption::ForceLoad) && value) {
+ if (value && value->currentItem()
+ && path.lastModified() < value->currentItem()->lastDataUpdateAt()) {
+ oldValue = newValue = univ.copy(value);
+ skipParse = true;
+ }
+ }
+ }
+ if (!skipParse) {
+ contentDate = QDateTime::currentDateTime();
+ if (QFileInfo(canonicalPath).isDir()) {
+ code = QDir(canonicalPath)
+ .entryList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name)
+ .join(QLatin1Char('\n'));
+ } else if (!file.open(QIODevice::ReadOnly)) {
+ code = QStringLiteral(u"");
+ messages.append(myErrors().error(tr("Error opening path %1: %2 %3")
+ .arg(canonicalPath,
+ QString::number(file.error()),
+ file.errorString())));
+ } else {
+ code = QString::fromUtf8(file.readAll());
+ file.close();
+ }
+ }
+ }
+ if (!skipParse) {
+ QMutexLocker l(mutex());
+ if (auto value = getValue()) {
+ QString oldCode = value->currentItem()->code();
+ if (value && value->currentItem() && !oldCode.isNull() && oldCode == code) {
+ skipParse = true;
+ newValue = oldValue = univ.copy(value);
+ if (value->currentItem()->lastDataUpdateAt() < contentDate)
+ value->currentItem()->refreshedDataAt(contentDate);
+ }
+ }
+ }
+ if (!skipParse) {
+ QDateTime now(QDateTime::currentDateTime());
+ if (t.kind == DomType::QmlFile) {
+ shared_ptr<QmlFile> qmlFile(new QmlFile(canonicalPath, code, contentDate));
+ shared_ptr<DomEnvironment> envPtr(new DomEnvironment(
+ QStringList(), DomEnvironment::Option::NoDependencies, topPtr));
+ envPtr->addQmlFile(qmlFile);
+ DomItem env(envPtr);
+ if (qmlFile->isValid()) {
+ MutableDomItem qmlFileObj(env.copy(qmlFile));
+ createDom(qmlFileObj);
+ } else {
+ QString errs;
+ DomItem qmlFileObj = env.copy(qmlFile);
+ qmlFile->iterateErrors(qmlFileObj, [&errs](DomItem, ErrorMessage m) {
+ errs += m.toString();
+ errs += u"\n";
+ return true;
+ });
+ qCWarning(domLog).noquote().nospace()
+ << "Parsed invalid file " << canonicalPath << errs;
+ }
+ auto change = updateEntry<QmlFile>(univ, qmlFile, m_qmlFileWithPath, mutex());
+ oldValue = univ.copy(change.first);
+ newValue = univ.copy(change.second);
+ } else if (t.kind == DomType::QmltypesFile) {
+ shared_ptr<QmltypesFile> qmltypesFile(
+ new QmltypesFile(canonicalPath, code, contentDate));
+ auto change = updateEntry<QmltypesFile>(univ, qmltypesFile, m_qmltypesFileWithPath,
+ mutex());
+ oldValue = univ.copy(change.first);
+ newValue = univ.copy(change.second);
+ } else if (t.kind == DomType::QmldirFile) {
+ shared_ptr<QmldirFile> qmldirFile =
+ QmldirFile::fromPathAndCode(canonicalPath, code);
+ auto change =
+ updateEntry<QmldirFile>(univ, qmldirFile, m_qmldirFileWithPath, mutex());
+ oldValue = univ.copy(change.first);
+ newValue = univ.copy(change.second);
+ } else if (t.kind == DomType::QmlDirectory) {
+ shared_ptr<QmlDirectory> qmlDirectory(new QmlDirectory(
+ canonicalPath, code.split(QLatin1Char('\n')), contentDate));
+ auto change = updateEntry<QmlDirectory>(univ, qmlDirectory, m_qmlDirectoryWithPath,
+ mutex());
+ oldValue = univ.copy(change.first);
+ newValue = univ.copy(change.second);
+ } else {
+ Q_ASSERT(false);
+ }
+ }
+ for (const ErrorMessage &m : messages)
+ newValue.addError(m);
+ // to do: tell observers?
+ // execute callback
+ if (t.callback) {
+ Path p;
+ if (t.kind == DomType::QmlFile)
+ p = Paths::qmlFileInfoPath(canonicalPath);
+ else if (t.kind == DomType::QmltypesFile)
+ p = Paths::qmltypesFileInfoPath(canonicalPath);
+ else if (t.kind == DomType::QmldirFile)
+ p = Paths::qmldirFileInfoPath(canonicalPath);
+ else if (t.kind == DomType::QmlDirectory)
+ p = Paths::qmlDirectoryInfoPath(canonicalPath);
+ else
+ Q_ASSERT(false);
+ t.callback(p, oldValue, newValue);
+ }
+ } else {
+ Q_ASSERT(false && "Unhandled kind in queue");
+ }
+}
+
+std::shared_ptr<OwningItem> LoadInfo::doCopy(DomItem &self) const
+{
+ std::shared_ptr<LoadInfo> res(new LoadInfo(*this));
+ if (res->status() != Status::Done) {
+ res->addErrorLocal(DomEnvironment::myErrors().warning(
+ u"This is a copy of a LoadInfo still in progress, artificially ending it, if you "
+ u"use this you will *not* resume loading"));
+ DomEnvironment::myErrors()
+ .warning([&self](Sink sink) {
+ sink(u"Copying an in progress LoadInfo, which is most likely an error (");
+ self.dump(sink);
+ sink(u")");
+ })
+ .handle();
+ QMutexLocker l(res->mutex());
+ res->m_status = Status::Done;
+ res->m_toDo.clear();
+ res->m_inProgress.clear();
+ res->m_endCallbacks.clear();
+ }
+ return res;
+}
+
+Path LoadInfo::canonicalPath(DomItem &) const
+{
+ return Path::Root(PathRoot::Env).field(Fields::loadInfo).key(elementCanonicalPath().toString());
+}
+
+bool LoadInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+{
+ bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::status, int(status()));
+ cont = cont && self.dvValueField(visitor, Fields::nLoaded, nLoaded());
+ cont = cont
+ && self.dvValueField(visitor, Fields::elementCanonicalPath,
+ elementCanonicalPath().toString());
+ cont = cont && self.dvValueField(visitor, Fields::nNotdone, nNotDone());
+ cont = cont && self.dvValueField(visitor, Fields::nCallbacks, nCallbacks());
+ return cont;
+}
+
+void LoadInfo::addEndCallback(DomItem &self,
+ std::function<void(Path, DomItem &, DomItem &)> callback)
+{
+ if (!callback)
+ return;
+ {
+ QMutexLocker l(mutex());
+ switch (m_status) {
+ case Status::NotStarted:
+ case Status::Starting:
+ case Status::InProgress:
+ case Status::CallingCallbacks:
+ m_endCallbacks.append(callback);
+ return;
+ case Status::Done:
+ break;
+ }
+ }
+ Path p = elementCanonicalPath();
+ DomItem el = self.path(p);
+ callback(p, el, el);
+}
+
+void LoadInfo::advanceLoad(DomItem &self)
+{
+ Status myStatus;
+ Dependency dep;
+ bool depValid = false;
+ {
+ QMutexLocker l(mutex());
+ myStatus = m_status;
+ switch (myStatus) {
+ case Status::NotStarted:
+ m_status = Status::Starting;
+ break;
+ case Status::Starting:
+ case Status::InProgress:
+ if (!m_toDo.isEmpty()) {
+ dep = m_toDo.dequeue();
+ m_inProgress.append(dep);
+ depValid = true;
+ }
+ break;
+ case Status::CallingCallbacks:
+ case Status::Done:
+ break;
+ }
+ }
+ switch (myStatus) {
+ case Status::NotStarted:
+ refreshedDataAt(QDateTime::currentDateTime());
+ doAddDependencies(self);
+ refreshedDataAt(QDateTime::currentDateTime());
+ {
+ QMutexLocker l(mutex());
+ Q_ASSERT(m_status == Status::Starting);
+ if (m_toDo.isEmpty() && m_inProgress.isEmpty())
+ myStatus = m_status = Status::CallingCallbacks;
+ else
+ myStatus = m_status = Status::InProgress;
+ }
+ if (myStatus == Status::CallingCallbacks)
+ execEnd(self);
+ break;
+ case Status::Starting:
+ case Status::InProgress:
+ if (depValid) {
+ refreshedDataAt(QDateTime::currentDateTime());
+ if (!dep.uri.isEmpty()) {
+ self.loadModuleDependency(
+ dep.uri, dep.version,
+ [this, self, dep](Path, DomItem &, DomItem &) mutable {
+ finishedLoadingDep(self, dep);
+ },
+ self.errorHandler());
+ Q_ASSERT(dep.filePath.isEmpty() && "dependency with both uri and file");
+ } else if (!dep.filePath.isEmpty()) {
+ DomItem env = self.environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>())
+ envPtr->loadFile(
+ env, dep.filePath, QString(),
+ [this, self, dep](Path, DomItem &, DomItem &) mutable {
+ finishedLoadingDep(self, dep);
+ },
+ nullptr, nullptr, LoadOption::DefaultLoad, self.errorHandler());
+ else
+ Q_ASSERT(false && "missing environment");
+ } else {
+ Q_ASSERT(false && "dependency without uri and filePath");
+ }
+ } else {
+ addErrorLocal(DomEnvironment::myErrors().error(
+ tr("advanceLoad called but found no work, which should never happen")));
+ }
+ break;
+ case Status::CallingCallbacks:
+ case Status::Done:
+ addErrorLocal(DomEnvironment::myErrors().error(tr(
+ "advanceLoad called after work should have been done, which should never happen")));
+ break;
+ }
+}
+
+void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
+{
+ bool didRemove = false;
+ bool unexpectedState = false;
+ bool doEnd = false;
+ {
+ QMutexLocker l(mutex());
+ didRemove = m_inProgress.removeOne(d);
+ switch (m_status) {
+ case Status::NotStarted:
+ case Status::CallingCallbacks:
+ case Status::Done:
+ unexpectedState = true;
+ break;
+ case Status::Starting:
+ break;
+ case Status::InProgress:
+ if (m_toDo.isEmpty() && m_inProgress.isEmpty()) {
+ m_status = Status::CallingCallbacks;
+ doEnd = true;
+ }
+ break;
+ }
+ }
+ if (!didRemove) {
+ addErrorLocal(DomEnvironment::myErrors().error([&self](Sink sink) {
+ sink(u"LoadInfo::finishedLoadingDep did not find its dependency in those inProgress "
+ u"()");
+ self.dump(sink);
+ sink(u")");
+ }));
+ Q_ASSERT(false
+ && "LoadInfo::finishedLoadingDep did not find its dependency in those inProgress");
+ }
+ if (unexpectedState) {
+ addErrorLocal(DomEnvironment::myErrors().error([&self](Sink sink) {
+ sink(u"LoadInfo::finishedLoadingDep found an unexpected state (");
+ self.dump(sink);
+ sink(u")");
+ }));
+ Q_ASSERT(false && "LoadInfo::finishedLoadingDep did find an unexpected state");
+ }
+ if (doEnd)
+ execEnd(self);
+}
+
+void LoadInfo::execEnd(DomItem &self)
+{
+ QList<std::function<void(Path, DomItem &, DomItem &)>> endCallbacks;
+ bool unexpectedState = false;
+ {
+ QMutexLocker l(mutex());
+ unexpectedState = m_status != Status::CallingCallbacks;
+ endCallbacks = m_endCallbacks;
+ m_endCallbacks.clear();
+ }
+ Q_ASSERT(!unexpectedState && "LoadInfo::execEnd found an unexpected state");
+ Path p = elementCanonicalPath();
+ DomItem el = self.path(p);
+ {
+ auto cleanup = qScopeGuard([this, p, &el] {
+ QList<std::function<void(Path, DomItem &, DomItem &)>> otherCallbacks;
+ bool unexpectedState2 = false;
+ {
+ QMutexLocker l(mutex());
+ unexpectedState2 = m_status != Status::CallingCallbacks;
+ m_status = Status::Done;
+ otherCallbacks = m_endCallbacks;
+ m_endCallbacks.clear();
+ }
+ Q_ASSERT(!unexpectedState2 && "LoadInfo::execEnd found an unexpected state");
+ for (auto const &cb : otherCallbacks) {
+ if (cb)
+ cb(p, el, el);
+ }
+ });
+ for (auto const &cb : endCallbacks) {
+ if (cb)
+ cb(p, el, el);
+ }
+ }
+}
+
+void LoadInfo::doAddDependencies(DomItem &self)
+{
+ if (!elementCanonicalPath()) {
+ DomEnvironment::myErrors()
+ .error(tr("Uninitialized LoadInfo %1").arg(self.canonicalPath().toString()))
+ .handle(nullptr);
+ Q_ASSERT(false);
+ return;
+ }
+ // sychronous add of all dependencies
+ DomItem el = self.path(elementCanonicalPath());
+ if (el.internalKind() == DomType::ExternalItemInfo) {
+ DomItem currentImports = el.field(Fields::currentItem).field(Fields::imports);
+ int iEnd = currentImports.indexes();
+ for (int i = 0; i < iEnd; ++i) {
+ DomItem import = currentImports.index(i);
+ if (const Import *importPtr = import.as<Import>()) {
+ if (!importPtr->filePath().isEmpty()) {
+ addDependency(
+ self,
+ Dependency { QString(), importPtr->version, importPtr->filePath() });
+ } else {
+ addDependency(self,
+ Dependency { importPtr->uri, importPtr->version, QString() });
+ }
+ }
+ }
+ DomItem currentQmltypesFiles = el.field(Fields::currentItem).field(Fields::qmltypesFiles);
+ int qEnd = currentQmltypesFiles.indexes();
+ for (int i = 0; i < qEnd; ++i) {
+ DomItem qmltypesRef = currentQmltypesFiles.index(i);
+ if (const Reference *ref = qmltypesRef.as<Reference>()) {
+ Path canonicalPath = ref->referredObjectPath[2];
+ if (canonicalPath && !canonicalPath.headName().isEmpty())
+ addDependency(self,
+ Dependency { QString(), Version(), canonicalPath.headName() });
+ }
+ }
+ DomItem currentQmlFiles = el.field(Fields::currentItem).field(Fields::qmlFiles);
+ currentQmlFiles.visitKeys([this, &self](QString, DomItem &els) {
+ return els.visitIndexes([this, &self](DomItem &el) {
+ if (const Reference *ref = el.as<Reference>()) {
+ Path canonicalPath = ref->referredObjectPath[2];
+ if (canonicalPath && !canonicalPath.headName().isEmpty())
+ addDependency(
+ self,
+ Dependency { QString(), Version(), canonicalPath.headName() });
+ }
+ return true;
+ });
+ });
+ } else if (shared_ptr<ModuleIndex> elPtr = el.ownerAs<ModuleIndex>()) {
+ for (Path qmldirPath : elPtr->qmldirsToLoad(el)) {
+ Path canonicalPath = qmldirPath[2];
+ if (canonicalPath && !canonicalPath.headName().isEmpty())
+ addDependency(self, Dependency { QString(), Version(), canonicalPath.headName() });
+ }
+ } else if (!el) {
+ self.addError(DomEnvironment::myErrors().error(
+ tr("Ignoring dependencies for empty (invalid) type")
+ .arg(domTypeToString(el.internalKind()))));
+ } else {
+ self.addError(
+ DomEnvironment::myErrors().error(tr("dependencies of %1 (%2) not yet implemented")
+ .arg(domTypeToString(el.internalKind()),
+ elementCanonicalPath().toString())));
+ }
+}
+
+void LoadInfo::addDependency(DomItem &self, const Dependency &dep)
+{
+ bool unexpectedState = false;
+ {
+ QMutexLocker l(mutex());
+ unexpectedState = m_status != Status::Starting;
+ m_toDo.enqueue(dep);
+ }
+ Q_ASSERT(!unexpectedState && "LoadInfo::addDependency found an unexpected state");
+ DomItem env = self.environment();
+ env.ownerAs<DomEnvironment>()->addWorkForLoadInfo(elementCanonicalPath());
}
/*!
@@ -296,65 +816,632 @@ void DomUniverse::execQueue()
\brief Represents a consistent set of types organized in modules, it is the top level of the DOM
*/
+template<typename T>
+DomTop::Callback envCallbackForFile(
+ DomItem &self, QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> DomEnvironment::*map,
+ std::shared_ptr<ExternalItemInfo<T>> (DomEnvironment::*lookupF)(DomItem &, QString,
+ EnvLookup) const,
+ DomTop::Callback loadCallback, DomTop::Callback allDirectDepsCallback,
+ DomTop::Callback endCallback)
+{
+ std::shared_ptr<DomEnvironment> ePtr = self.ownerAs<DomEnvironment>();
+ std::weak_ptr<DomEnvironment> selfPtr = ePtr;
+ std::shared_ptr<DomEnvironment> basePtr = ePtr->base();
+ return [selfPtr, basePtr, map, lookupF, loadCallback, allDirectDepsCallback,
+ endCallback](Path, DomItem &, DomItem &newItem) {
+ shared_ptr<DomEnvironment> envPtr = selfPtr.lock();
+ if (!envPtr)
+ return;
+ DomItem env = DomItem(envPtr);
+ shared_ptr<ExternalItemInfo<T>> oldValue;
+ shared_ptr<ExternalItemInfo<T>> newValue;
+ shared_ptr<T> newItemPtr;
+ if (envPtr->options() & DomEnvironment::Option::KeepValid)
+ newItemPtr = newItem.field(Fields::validItem).ownerAs<T>();
+ if (!newItemPtr)
+ newItemPtr = newItem.field(Fields::currentItem).ownerAs<T>();
+ Q_ASSERT(newItemPtr && "callbackForQmlFile reached without current qmlFile");
+ {
+ QMutexLocker l(envPtr->mutex());
+ oldValue = ((*envPtr).*map).value(newItem.canonicalFilePath());
+ }
+ if (oldValue) {
+ // we do not change locally loaded files (avoid loading a file more than once)
+ newValue = oldValue;
+ } else {
+ if (basePtr) {
+ DomItem baseObj(basePtr);
+ oldValue = ((*basePtr).*lookupF)(baseObj, newItem.canonicalFilePath(),
+ EnvLookup::BaseOnly);
+ }
+ if (oldValue) {
+ DomItem oldValueObj = env.copy(oldValue);
+ newValue = oldValue->makeCopy(oldValueObj);
+ if (newValue->current != newItemPtr) {
+ newValue->current = newItemPtr;
+ newValue->setCurrentExposedAt(QDateTime::currentDateTime());
+ }
+ } else {
+ newValue = std::shared_ptr<ExternalItemInfo<T>>(
+ new ExternalItemInfo<T>(newItemPtr, QDateTime::currentDateTime()));
+ }
+ {
+ QMutexLocker l(envPtr->mutex());
+ auto value = ((*envPtr).*map).value(newItem.canonicalFilePath());
+ if (value) {
+ oldValue = newValue = value;
+ } else {
+ ((*envPtr).*map).insert(newItem.canonicalFilePath(), newValue);
+ }
+ }
+ }
+ Path p = env.copy(newValue).canonicalPath();
+ {
+ auto depLoad = qScopeGuard([p, &env, envPtr, allDirectDepsCallback, endCallback] {
+ if (!(envPtr->options() & DomEnvironment::Option::NoDependencies)) {
+ std::shared_ptr<LoadInfo> loadInfo(new LoadInfo(p));
+ if (!p)
+ Q_ASSERT(false);
+ DomItem loadInfoObj = env.copy(loadInfo);
+ loadInfo->addEndCallback(loadInfoObj, allDirectDepsCallback);
+ envPtr->addLoadInfo(env, loadInfo);
+ }
+ if (endCallback)
+ envPtr->addAllLoadedCallback(env,
+ [p, endCallback](Path, DomItem &, DomItem &env) {
+ DomItem el = env.path(p);
+ endCallback(p, el, el);
+ });
+ });
+ if (loadCallback) {
+ DomItem oldValueObj = env.copy(oldValue);
+ DomItem newValueObj = env.copy(newValue);
+ loadCallback(p, oldValueObj, newValueObj);
+ }
+ if ((envPtr->options() & DomEnvironment::Option::NoDependencies)
+ && allDirectDepsCallback) {
+ DomItem oldValueObj = env.copy(oldValue);
+ DomItem newValueObj = env.copy(newValue);
+ env.addError(DomEnvironment::myErrors().warning(
+ QLatin1String("calling allDirectDepsCallback immediately for load with "
+ "NoDependencies of %1")
+ .arg(newItem.canonicalFilePath())));
+ allDirectDepsCallback(p, oldValueObj, newValueObj);
+ }
+ }
+ };
+}
+
ErrorGroups DomEnvironment::myErrors() {
static ErrorGroups res = {{NewErrorGroup("Dom")}};
return res;
}
+DomType DomEnvironment::kind() const
+{
+ return kindValue;
+}
Path DomEnvironment::canonicalPath() const
{
return Path::Root(u"env");
}
-bool DomEnvironment::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
bool cont = true;
cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
DomItem univ = universe();
- cont = cont && visitor(Path::Field(Fields::universe), univ);
- cont = cont && self.subDataField(Fields::options, int(options())).visit(visitor);
- DomItem baseItem = base();
- cont = cont && visitor(Path::Field(Fields::base), baseItem);
- cont = cont && self.subList(List::fromQList<QString>(
- Path::Field(Fields::loadPaths), loadPaths(),
- [](const DomItem &i, Path p, const QString &el){
- return i.subDataPath(p, el).item;
- })).visit(visitor);
+ cont = cont && self.dvItemField(visitor, Fields::universe, [this]() { return universe(); });
+ cont = cont && self.dvValueField(visitor, Fields::options, int(options()));
+ cont = cont && self.dvItemField(visitor, Fields::base, [this]() { return base(); });
+ cont = cont
+ && self.dvValueLazyField(visitor, Fields::loadPaths, [this]() { return loadPaths(); });
+ cont = cont && self.dvValueField(visitor, Fields::globalScopeName, globalScopeName());
+ cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::globalScopeWithName),
+ [&self, this](DomItem &map, QString key) {
+ return map.copy(globalScopeWithName(self, key));
+ },
+ [&self, this](DomItem &) { return globalScopeNames(self); },
+ QLatin1String("GlobalScope")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmlDirectoryWithPath),
+ [&self, this](DomItem &map, QString key) {
+ return map.copy(qmlDirectoryWithPath(self, key));
+ },
+ [&self, this](DomItem &) { return qmlDirectoryPaths(self); },
+ QLatin1String("QmlDirectory")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmldirFileWithPath),
+ [&self, this](DomItem &map, QString key) {
+ return map.copy(qmldirFileWithPath(self, key));
+ },
+ [&self, this](DomItem &) { return qmldirFilePaths(self); },
+ QLatin1String("QmldirFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmldirWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmldirWithPath),
+ [&self, this](DomItem &map, QString key) {
+ return map.copy(qmlDirWithPath(self, key));
+ },
+ [&self, this](DomItem &) { return qmlDirPaths(self); }, QLatin1String("Qmldir")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmlFileWithPath),
+ [&self, this](DomItem &map, QString key) {
+ return map.copy(qmlFileWithPath(self, key));
+ },
+ [&self, this](DomItem &) { return qmlFilePaths(self); }, QLatin1String("QmlFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::jsFileWithPath),
+ [this](DomItem &map, QString key) {
+ DomItem mapOw(map.owner());
+ return map.copy(jsFileWithPath(mapOw, key));
+ },
+ [this](DomItem &map) {
+ DomItem mapOw = map.owner();
+ return jsFilePaths(mapOw);
+ },
+ QLatin1String("JsFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::qmltypesFileWithPath, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::qmltypesFileWithPath),
+ [this](DomItem &map, QString key) {
+ DomItem mapOw = map.owner();
+ return map.copy(qmltypesFileWithPath(mapOw, key));
+ },
+ [this](DomItem &map) {
+ DomItem mapOw = map.owner();
+ return qmltypesFilePaths(mapOw);
+ },
+ QLatin1String("QmltypesFile")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::moduleIndexWithUri, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::moduleIndexWithUri),
+ [this](DomItem &map, QString key) {
+ return map.subMapItem(Map(
+ map.pathFromOwner().key(key),
+ [this, key](DomItem &submap, QString subKey) {
+ bool ok;
+ int i = subKey.toInt(&ok);
+ if (!ok) {
+ if (subKey.isEmpty())
+ i = Version::Undefined;
+ else if (subKey.compare(u"Latest", Qt::CaseInsensitive) == 0)
+ i = Version::Latest;
+ else
+ return DomItem();
+ }
+ DomItem subMapOw = submap.owner();
+ std::shared_ptr<ModuleIndex> mIndex =
+ moduleIndexWithUri(subMapOw, key, i);
+ return submap.copy(mIndex);
+ },
+ [this, key](DomItem &subMap) {
+ QSet<QString> res;
+ DomItem subMapOw = subMap.owner();
+ for (int mVersion :
+ moduleIndexMajorVersions(subMapOw, key, EnvLookup::Normal))
+ if (mVersion == Version::Undefined)
+ res.insert(QString());
+ else
+ res.insert(QString::number(mVersion));
+ if (!res.isEmpty())
+ res.insert(QLatin1String("Latest"));
+ return res;
+ },
+ QLatin1String("ModuleIndex")));
+ },
+ [this](DomItem &map) {
+ DomItem mapOw = map.owner();
+ return moduleIndexUris(mapOw);
+ },
+ QLatin1String("Map<ModuleIndex>")));
+ });
+ bool loadedLoadInfo = false;
QQueue<Path> loadsWithWork;
QQueue<Path> inProgress;
int nAllLoadedCallbacks;
- {
- QMutexLocker l(mutex());
- loadsWithWork = m_loadsWithWork;
- inProgress = m_inProgress;
- nAllLoadedCallbacks = m_allLoadedCallback.length();
- }
- cont = cont && self.subList(
- List(Path::Field(Fields::loadsWithWork),
- [loadsWithWork](const DomItem &list, index_type i){
- if (i >= 0 && i < loadsWithWork.length())
- return list.subDataIndex(i, loadsWithWork.at(i).toString()).item;
- else
- return DomItem();
- }, [loadsWithWork](const DomItem &){
- return index_type(loadsWithWork.length());
- }, nullptr, QLatin1String("Path"))
- ).visit(visitor);
- cont = cont && self.subDataField(Fields::nAllLoadedCallbacks, nAllLoadedCallbacks).visit(visitor);
+ auto ensureInfo = [&]() {
+ if (!loadedLoadInfo) {
+ QMutexLocker l(mutex());
+ loadedLoadInfo = true;
+ loadsWithWork = m_loadsWithWork;
+ inProgress = m_inProgress;
+ nAllLoadedCallbacks = m_allLoadedCallback.length();
+ }
+ };
+ cont = cont
+ && self.dvItemField(
+ visitor, Fields::loadsWithWork, [&ensureInfo, &self, &loadsWithWork]() {
+ ensureInfo();
+ return self.subListItem(List(
+ Path::Field(Fields::loadsWithWork),
+ [loadsWithWork](DomItem &list, index_type i) {
+ if (i >= 0 && i < loadsWithWork.length())
+ return list.subDataItem(PathEls::Index(i),
+ loadsWithWork.at(i).toString());
+ else
+ return DomItem();
+ },
+ [loadsWithWork](DomItem &) {
+ return index_type(loadsWithWork.length());
+ },
+ nullptr, QLatin1String("Path")));
+ });
+ cont = cont
+ && self.dvItemField(visitor, Fields::inProgress, [&self, &ensureInfo, &inProgress]() {
+ ensureInfo();
+ return self.subListItem(List(
+ Path::Field(Fields::inProgress),
+ [inProgress](DomItem &list, index_type i) {
+ if (i >= 0 && i < inProgress.length())
+ return list.subDataItem(PathEls::Index(i),
+ inProgress.at(i).toString());
+ else
+ return DomItem();
+ },
+ [inProgress](DomItem &) { return index_type(inProgress.length()); },
+ nullptr, QLatin1String("Path")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::loadInfo, [&self, this]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::loadInfo),
+ [this](DomItem &map, QString pStr) {
+ bool hasErrors = false;
+ Path p = Path::fromString(pStr, [&hasErrors](ErrorMessage m) {
+ switch (m.level) {
+ case ErrorLevel::Debug:
+ case ErrorLevel::Info:
+ break;
+ case ErrorLevel::Warning:
+ case ErrorLevel::Error:
+ case ErrorLevel::Fatal:
+ hasErrors = true;
+ break;
+ }
+ });
+ if (!hasErrors)
+ return map.copy(loadInfo(p));
+ return DomItem();
+ },
+ [this](DomItem &) {
+ QSet<QString> res;
+ for (const Path &p : loadInfoPaths())
+ res.insert(p.toString());
+ return res;
+ },
+ QLatin1String("LoadInfo")));
+ });
+ cont = cont && self.dvWrapField(visitor, Fields::imports, m_implicitImports);
+ cont = cont
+ && self.dvValueLazyField(visitor, Fields::nAllLoadedCallbacks,
+ [&nAllLoadedCallbacks, &ensureInfo]() {
+ ensureInfo();
+ return nAllLoadedCallbacks;
+ });
return cont;
}
-std::shared_ptr<OwningItem> DomEnvironment::doCopy(const DomItem &) const
+DomItem DomEnvironment::field(DomItem &self, QStringView name) const
+{
+ return DomTop::field(self, name);
+}
+
+std::shared_ptr<DomEnvironment> DomEnvironment::makeCopy(DomItem &self) const
+{
+ return std::static_pointer_cast<DomEnvironment>(doCopy(self));
+}
+
+void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPath,
+ DomTop::Callback loadCallback, DomTop::Callback directDepsCallback,
+ DomTop::Callback endCallback, LoadOptions loadOptions, ErrorHandler h)
+{
+ loadFile(self, filePath, logicalPath, QString(), QDateTime::fromMSecsSinceEpoch(0),
+ loadCallback, directDepsCallback, endCallback, loadOptions, h);
+}
+
+std::shared_ptr<OwningItem> DomEnvironment::doCopy(DomItem &) const
{
shared_ptr<DomEnvironment> res;
if (m_base)
- res = std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options);
+ res = std::shared_ptr<DomEnvironment>(new DomEnvironment(m_base, m_loadPaths, m_options));
else
- res = std::make_shared<DomEnvironment>(m_universe, m_loadPaths, m_options);
+ res = std::shared_ptr<DomEnvironment>(
+ new DomEnvironment(m_loadPaths, m_options, m_universe));
return res;
}
+void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPath, QString code,
+ QDateTime codeDate, Callback loadCallback,
+ Callback directDepsCallback, Callback endCallback,
+ LoadOptions loadOptions, ErrorHandler h)
+{
+ QFileInfo fileInfo(filePath);
+ bool isDir = fileInfo.isDir();
+ QString ext = fileInfo.suffix();
+ QString canonicalFilePath = fileInfo.canonicalFilePath();
+ if (canonicalFilePath.isEmpty()) {
+ if (code.isNull()) {
+ myErrors().error(tr("Non existing path to load: '%1'").arg(filePath)).handle(h);
+ if (loadCallback)
+ loadCallback(Path(), DomItem::empty, DomItem::empty);
+ if (directDepsCallback)
+ directDepsCallback(Path(), DomItem::empty, DomItem::empty);
+ if (endCallback)
+ addAllLoadedCallback(self, [endCallback](Path, DomItem &, DomItem &) {
+ endCallback(Path(), DomItem::empty, DomItem::empty);
+ });
+ return;
+ } else {
+ canonicalFilePath = filePath;
+ }
+ }
+ shared_ptr<ExternalItemInfoBase> oldValue, newValue;
+ if (isDir) {
+ {
+ QMutexLocker l(mutex());
+ auto it = m_qmlDirectoryWithPath.find(canonicalFilePath);
+ if (it != m_qmlDirectoryWithPath.end())
+ oldValue = newValue = *it;
+ }
+ if (!newValue && (options() & Option::NoReload) && m_base) {
+ if (auto v = m_base->qmlDirectoryWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
+ oldValue = v;
+ QDateTime now = QDateTime::currentDateTime();
+ std::shared_ptr<ExternalItemInfo<QmlDirectory>> newV(
+ new ExternalItemInfo<QmlDirectory>(v->current, now, v->revision(),
+ v->lastDataUpdateAt()));
+ newValue = newV;
+ QMutexLocker l(mutex());
+ auto it = m_qmlDirectoryWithPath.find(canonicalFilePath);
+ if (it != m_qmlDirectoryWithPath.end())
+ oldValue = newValue = *it;
+ else
+ m_qmlDirectoryWithPath.insert(canonicalFilePath, newV);
+ }
+ }
+ if (!newValue) {
+ self.universe().loadFile(
+ canonicalFilePath, logicalPath, code, codeDate,
+ callbackForQmlDirectory(self, loadCallback, directDepsCallback, endCallback),
+ loadOptions);
+ return;
+ }
+ } else if (ext == u"qml" || ext == u"ui" || ext == u"qmlannotation") {
+ {
+ QMutexLocker l(mutex());
+ auto it = m_qmlFileWithPath.find(canonicalFilePath);
+ if (it != m_qmlFileWithPath.end())
+ oldValue = newValue = *it;
+ }
+ if (!newValue && (options() & Option::NoReload) && m_base) {
+ if (auto v = m_base->qmlFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
+ oldValue = v;
+ QDateTime now = QDateTime::currentDateTime();
+ std::shared_ptr<ExternalItemInfo<QmlFile>> newV(new ExternalItemInfo<QmlFile>(
+ v->current, now, v->revision(), v->lastDataUpdateAt()));
+ newValue = newV;
+ QMutexLocker l(mutex());
+ auto it = m_qmlFileWithPath.find(canonicalFilePath);
+ if (it != m_qmlFileWithPath.end())
+ oldValue = newValue = *it;
+ else
+ m_qmlFileWithPath.insert(canonicalFilePath, newV);
+ }
+ }
+ if (!newValue) {
+ self.universe().loadFile(
+ canonicalFilePath, logicalPath, code, codeDate,
+ callbackForQmlFile(self, loadCallback, directDepsCallback, endCallback),
+ loadOptions);
+ return;
+ }
+ } else if (ext == u"qmltypes") {
+ {
+ QMutexLocker l(mutex());
+ auto it = m_qmltypesFileWithPath.find(canonicalFilePath);
+ if (it != m_qmltypesFileWithPath.end())
+ oldValue = newValue = *it;
+ }
+ if (!newValue && (options() & Option::NoReload) && m_base) {
+ if (auto v = m_base->qmltypesFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
+ oldValue = v;
+ QDateTime now = QDateTime::currentDateTime();
+ std::shared_ptr<ExternalItemInfo<QmltypesFile>> newV(
+ new ExternalItemInfo<QmltypesFile>(v->current, now, v->revision(),
+ v->lastDataUpdateAt()));
+ newValue = newV;
+ QMutexLocker l(mutex());
+ auto it = m_qmltypesFileWithPath.find(canonicalFilePath);
+ if (it != m_qmltypesFileWithPath.end())
+ oldValue = newValue = *it;
+ else
+ m_qmltypesFileWithPath.insert(canonicalFilePath, newV);
+ }
+ }
+ if (!newValue) {
+ self.universe().loadFile(
+ canonicalFilePath, logicalPath, code, codeDate,
+ callbackForQmltypesFile(self, loadCallback, directDepsCallback, endCallback),
+ loadOptions);
+ return;
+ }
+ } else if (fileInfo.fileName() == u"qmldir") {
+ {
+ QMutexLocker l(mutex());
+ auto it = m_qmldirFileWithPath.find(canonicalFilePath);
+ if (it != m_qmldirFileWithPath.end())
+ oldValue = newValue = *it;
+ }
+ if (!newValue && (options() & Option::NoReload) && m_base) {
+ if (auto v = m_base->qmldirFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
+ oldValue = v;
+ QDateTime now = QDateTime::currentDateTime();
+ std::shared_ptr<ExternalItemInfo<QmldirFile>> newV(new ExternalItemInfo<QmldirFile>(
+ v->current, now, v->revision(), v->lastDataUpdateAt()));
+ newValue = newV;
+ QMutexLocker l(mutex());
+ auto it = m_qmldirFileWithPath.find(canonicalFilePath);
+ if (it != m_qmldirFileWithPath.end())
+ oldValue = newValue = *it;
+ else
+ m_qmldirFileWithPath.insert(canonicalFilePath, newV);
+ }
+ }
+ if (!newValue) {
+ self.universe().loadFile(
+ canonicalFilePath, logicalPath, code, codeDate,
+ callbackForQmldirFile(self, loadCallback, directDepsCallback, endCallback),
+ loadOptions);
+ return;
+ }
+ } else {
+ myErrors().error(tr("Unexpected file to load: '%1'").arg(filePath)).handle(h);
+ if (loadCallback)
+ loadCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
+ if (directDepsCallback)
+ directDepsCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
+ if (endCallback)
+ endCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
+ return;
+ }
+ Path p = self.copy(newValue).canonicalPath();
+ std::shared_ptr<LoadInfo> lInfo = loadInfo(p);
+ if (lInfo) {
+ if (loadCallback) {
+ DomItem oldValueObj = self.copy(oldValue);
+ DomItem newValueObj = self.copy(newValue);
+ loadCallback(p, oldValueObj, newValueObj);
+ }
+ if (directDepsCallback) {
+ DomItem lInfoObj = self.copy(lInfo);
+ lInfo->addEndCallback(lInfoObj, directDepsCallback);
+ }
+ } else {
+ self.addError(myErrors().error(tr("missing load info in ")));
+ if (loadCallback)
+ loadCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
+ if (directDepsCallback)
+ directDepsCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
+ }
+ if (endCallback)
+ addAllLoadedCallback(self, [p, endCallback](Path, DomItem &, DomItem &env) {
+ DomItem el = env.path(p);
+ endCallback(p, el, el);
+ });
+}
+
+void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
+ Callback loadCallback, Callback endCallback,
+ ErrorHandler errorHandler)
+{
+ if (uri.startsWith(u"file://") || uri.startsWith(u"http://") || uri.startsWith(u"https://")) {
+ self.addError(myErrors().error(tr("directory import not yet handled (%1)").arg(uri)));
+ return;
+ }
+ Path p = Paths::moduleIndexPath(uri, v.majorVersion);
+ if (v.majorVersion == Version::Latest) {
+ // load both the latest .<version> directory, and the common one
+ QStringList subPathComponents = uri.split(QLatin1Char('.'));
+ int maxV = -1;
+ bool commonV = false;
+ QString lastComponent = subPathComponents.last();
+ subPathComponents.removeLast();
+ QString subPathV = subPathComponents.join(u'/');
+ QRegularExpression vRe(QRegularExpression::anchoredPattern(
+ QRegularExpression::escape(lastComponent) + QStringLiteral(u"\\.([0-9]*)")));
+ for (QString path : loadPaths()) {
+ QDir dir(path + (subPathV.isEmpty() ? QStringLiteral(u"") : QStringLiteral(u"/"))
+ + subPathV);
+ for (QString dirNow : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ auto m = vRe.match(dirNow);
+ if (m.hasMatch()) {
+ int majorV = m.captured(1).toInt();
+ if (majorV > maxV) {
+ QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
+ + QStringLiteral(u"/qmldir"));
+ if (fInfo.isFile())
+ maxV = majorV;
+ }
+ }
+ if (!commonV && dirNow == lastComponent) {
+ QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
+ + QStringLiteral(u"/qmldir"));
+ if (fInfo.isFile())
+ commonV = true;
+ }
+ }
+ }
+ QAtomicInt toLoad((commonV ? 1 : 0) + ((maxV >= 0) ? 1 : 0));
+ auto loadCallback2 = (loadCallback ? [p, loadCallback, toLoad](Path, DomItem &, DomItem &elV) mutable {
+ if (--toLoad == 0) {
+ DomItem el = elV.path(p);
+ loadCallback(p, el, el);
+ }
+ }: Callback());
+ if (maxV >= 0)
+ loadModuleDependency(self, uri, Version(maxV, v.minorVersion), loadCallback2, nullptr);
+ if (commonV)
+ loadModuleDependency(self, uri, Version(Version::Undefined, v.minorVersion),
+ loadCallback2, nullptr);
+ else if (maxV < 0) {
+ if (uri != u"QML") {
+ addErrorLocal(myErrors()
+ .warning(tr("Failed to find main qmldir file for %1 %2")
+ .arg(uri, v.stringValue()))
+ .handle());
+ }
+ if (loadCallback)
+ loadCallback(p, DomItem::empty, DomItem::empty);
+ }
+ } else {
+ std::shared_ptr<ModuleIndex> mIndex = moduleIndexWithUri(
+ self, uri, v.majorVersion, EnvLookup::Normal, Changeable::Writable, errorHandler);
+ std::shared_ptr<LoadInfo> lInfo = loadInfo(p);
+ if (lInfo) {
+ DomItem lInfoObj = self.copy(lInfo);
+ lInfo->addEndCallback(lInfoObj, loadCallback);
+ } else {
+ addErrorLocal(
+ myErrors().warning(tr("Missing loadInfo for %1").arg(p.toString())).handle());
+ if (loadCallback)
+ loadCallback(p, DomItem::empty, DomItem::empty);
+ }
+ }
+ if (endCallback)
+ addAllLoadedCallback(self, [p, endCallback](Path, DomItem &, DomItem &env) {
+ DomItem el = env.path(p);
+ endCallback(p, el, el);
+ });
+}
+
+void DomEnvironment::loadBuiltins(DomItem &self, Callback callback, ErrorHandler h)
+{
+ QString builtinsName = QLatin1String("builtins.qmltypes");
+ for (QString path : loadPaths()) {
+ QDir dir(path);
+ QFileInfo fInfo(dir.filePath(builtinsName));
+ if (fInfo.isFile()) {
+ self.loadFile(fInfo.canonicalFilePath(), QString(), callback, LoadOption::DefaultLoad);
+ return;
+ }
+ }
+ myErrors().error(tr("Could not find builtins.qmltypes file")).handle(h);
+}
+
shared_ptr<DomUniverse> DomEnvironment::universe() const {
if (m_universe)
return m_universe;
@@ -364,98 +1451,804 @@ shared_ptr<DomUniverse> DomEnvironment::universe() const {
return {};
}
-DomEnvironment::DomEnvironment(shared_ptr<DomUniverse> universe, QStringList loadPaths, Options options):
- m_options(options), m_universe(universe), m_loadPaths(loadPaths)
+template<typename T>
+QSet<QString> DomEnvironment::getStrings(function_ref<QSet<QString>()> getBase,
+ const QMap<QString, T> &selfMap, EnvLookup options) const
+{
+ QSet<QString> res;
+ if (options != EnvLookup::NoBase && m_base) {
+ if (m_base)
+ res = getBase();
+ }
+ if (options != EnvLookup::BaseOnly) {
+ QMap<QString, T> map;
+ {
+ QMutexLocker l(mutex());
+ map = selfMap;
+ }
+ auto it = map.keyBegin();
+ auto end = map.keyEnd();
+ while (it != end) {
+ res += *it;
+ ++it;
+ }
+ }
+ return res;
+}
+
+QSet<QString> DomEnvironment::moduleIndexUris(DomItem &, EnvLookup lookup) const
+{
+ DomItem baseObj = DomItem(m_base);
+ return this->getStrings<QMap<int, std::shared_ptr<ModuleIndex>>>(
+ [this, &baseObj] { return m_base->moduleIndexUris(baseObj, EnvLookup::Normal); },
+ m_moduleIndexWithUri, lookup);
+}
+
+QSet<int> DomEnvironment::moduleIndexMajorVersions(DomItem &, QString uri, EnvLookup lookup) const
+{
+ QSet<int> res;
+ if (lookup != EnvLookup::NoBase && m_base) {
+ DomItem baseObj(m_base);
+ res = m_base->moduleIndexMajorVersions(baseObj, uri, EnvLookup::Normal);
+ }
+ if (lookup != EnvLookup::BaseOnly) {
+ QMap<int, std::shared_ptr<ModuleIndex>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_moduleIndexWithUri.value(uri);
+ }
+ auto it = map.keyBegin();
+ auto end = map.keyEnd();
+ while (it != end) {
+ res += *it;
+ ++it;
+ }
+ }
+ return res;
+}
+
+std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, QString uri,
+ int majorVersion, EnvLookup options,
+ Changeable changeable,
+ ErrorHandler errorHandler)
+{
+ Q_ASSERT((changeable == Changeable::ReadOnly
+ || (majorVersion >= 0 || majorVersion == Version::Undefined))
+ && "A writeable moduleIndexWithUri call should have a version (not with "
+ "Version::Latest)");
+ std::shared_ptr<ModuleIndex> res;
+ if (changeable == Changeable::Writable && (m_options & Option::Exported))
+ myErrors().error(tr("asked mutable module in Multithreaded env")).handle(errorHandler);
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ auto it = m_moduleIndexWithUri.find(uri);
+ if (it != m_moduleIndexWithUri.end()) {
+ if (majorVersion == Version::Latest) {
+ auto begin = it->begin();
+ auto end = it->end();
+ if (begin != end)
+ res = *--end;
+ } else {
+ auto it2 = it->find(majorVersion);
+ if (it2 != it->end())
+ return *it2;
+ }
+ }
+ }
+ std::shared_ptr<ModuleIndex> newModulePtr;
+ if (options != EnvLookup::NoBase && m_base) {
+ std::shared_ptr<ModuleIndex> existingMod =
+ m_base->moduleIndexWithUri(self, uri, majorVersion, options, Changeable::ReadOnly);
+ if (res && majorVersion == Version::Latest
+ && (!existingMod || res->majorVersion() >= existingMod->majorVersion()))
+ return res;
+ if (changeable == Changeable::Writable) {
+ DomItem existingModObj = self.copy(existingMod);
+ newModulePtr = existingMod->makeCopy(existingModObj);
+ } else {
+ return existingMod;
+ }
+ }
+ if (!newModulePtr && res)
+ return res;
+ if (!newModulePtr && changeable == Changeable::Writable)
+ newModulePtr = std::shared_ptr<ModuleIndex>(new ModuleIndex(uri, majorVersion));
+ if (newModulePtr) {
+ DomItem newModule = self.copy(newModulePtr);
+ Path p = newModule.canonicalPath();
+ {
+ QMutexLocker l(mutex());
+ auto &modsNow = m_moduleIndexWithUri[uri];
+ if (modsNow.contains(majorVersion))
+ return modsNow.value(majorVersion);
+ modsNow.insert(majorVersion, newModulePtr);
+ }
+ if (p) {
+ std::shared_ptr<LoadInfo> lInfo(new LoadInfo(p));
+ addLoadInfo(self, lInfo);
+ } else {
+ myErrors()
+ .error(tr("Could not get path for newly created ModuleIndex %1 %2")
+ .arg(uri)
+ .arg(majorVersion))
+ .handle(errorHandler);
+ }
+ }
+ return newModulePtr;
+}
+
+std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, QString uri,
+ int majorVersion,
+ EnvLookup options) const
+{
+ std::shared_ptr<ModuleIndex> res;
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ auto it = m_moduleIndexWithUri.find(uri);
+ if (it != m_moduleIndexWithUri.end()) {
+ if (majorVersion == Version::Latest) {
+ auto begin = it->begin();
+ auto end = it->end();
+ if (begin != end)
+ res = *--end;
+ } else {
+ auto it2 = it->find(majorVersion);
+ if (it2 != it->end())
+ return *it2;
+ }
+ }
+ }
+ if (options != EnvLookup::NoBase && m_base) {
+ std::shared_ptr existingMod =
+ m_base->moduleIndexWithUri(self, uri, majorVersion, options, Changeable::ReadOnly);
+ if (res && majorVersion == Version::Latest
+ && (!existingMod || res->majorVersion() >= existingMod->majorVersion())) {
+ return res;
+ }
+ return existingMod;
+ }
+ return res;
+}
+
+std::shared_ptr<ExternalItemInfo<QmlDirectory>>
+DomEnvironment::qmlDirectoryWithPath(DomItem &self, QString path, EnvLookup options) const
+{
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ if (m_qmlDirectoryWithPath.contains(path))
+ return m_qmlDirectoryWithPath.value(path);
+ }
+ if (options != EnvLookup::NoBase && m_base) {
+ return m_base->qmlDirectoryWithPath(self, path, options);
+ }
+ return {};
+}
+
+QSet<QString> DomEnvironment::qmlDirectoryPaths(DomItem &, EnvLookup options) const
+{
+ return getStrings<std::shared_ptr<ExternalItemInfo<QmlDirectory>>>(
+ [this] {
+ DomItem baseObj(m_base);
+ return m_base->qmlDirectoryPaths(baseObj, EnvLookup::Normal);
+ },
+ m_qmlDirectoryWithPath, options);
+}
+
+std::shared_ptr<ExternalItemInfo<QmldirFile>>
+DomEnvironment::qmldirFileWithPath(DomItem &self, QString path, EnvLookup options) const
+{
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ auto it = m_qmldirFileWithPath.find(path);
+ if (it != m_qmldirFileWithPath.end())
+ return *it;
+ }
+ if (options != EnvLookup::NoBase && m_base)
+ return m_base->qmldirFileWithPath(self, path, options);
+ return {};
+}
+
+QSet<QString> DomEnvironment::qmldirFilePaths(DomItem &, EnvLookup lOptions) const
+{
+ return getStrings<std::shared_ptr<ExternalItemInfo<QmldirFile>>>(
+ [this] {
+ DomItem baseObj(m_base);
+ return m_base->qmldirFilePaths(baseObj, EnvLookup::Normal);
+ },
+ m_qmldirFileWithPath, lOptions);
+}
+
+std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(DomItem &self, QString path,
+ EnvLookup options) const
+{
+ if (auto qmldirFile = qmldirFileWithPath(self, path + QLatin1String("/qmldir"), options))
+ return qmldirFile;
+ return qmlDirectoryWithPath(self, path, options);
+}
+
+QSet<QString> DomEnvironment::qmlDirPaths(DomItem &self, EnvLookup options) const
+{
+ QSet<QString> res = qmlDirectoryPaths(self, options);
+ for (QString p : qmldirFilePaths(self, options)) {
+ if (p.endsWith(u"/qmldir")) {
+ res.insert(p.left(p.length() - 7));
+ } else {
+ myErrors()
+ .warning(tr("Unexpected path not ending with qmldir in qmldirFilePaths: %1")
+ .arg(p))
+ .handle();
+ }
+ }
+ return res;
+}
+
+std::shared_ptr<ExternalItemInfo<QmlFile>>
+DomEnvironment::qmlFileWithPath(DomItem &self, QString path, EnvLookup options) const
+{
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ auto it = m_qmlFileWithPath.find(path);
+ if (it != m_qmlFileWithPath.end())
+ return *it;
+ }
+ if (options != EnvLookup::NoBase && m_base)
+ return m_base->qmlFileWithPath(self, path, options);
+ return {};
+}
+
+QSet<QString> DomEnvironment::qmlFilePaths(DomItem &, EnvLookup lookup) const
+{
+ return getStrings<std::shared_ptr<ExternalItemInfo<QmlFile>>>(
+ [this] {
+ DomItem baseObj(m_base);
+ return m_base->qmlFilePaths(baseObj, EnvLookup::Normal);
+ },
+ m_qmlFileWithPath, lookup);
+}
+
+std::shared_ptr<ExternalItemInfo<JsFile>>
+DomEnvironment::jsFileWithPath(DomItem &self, QString path, EnvLookup options) const
+{
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ if (m_jsFileWithPath.contains(path))
+ return m_jsFileWithPath.value(path);
+ }
+ if (options != EnvLookup::NoBase && m_base)
+ return m_base->jsFileWithPath(self, path, EnvLookup::Normal);
+ return {};
+}
+
+QSet<QString> DomEnvironment::jsFilePaths(DomItem &, EnvLookup lookup) const
+{
+ return getStrings<std::shared_ptr<ExternalItemInfo<JsFile>>>(
+ [this] {
+ DomItem baseObj(m_base);
+ return m_base->jsFilePaths(baseObj, EnvLookup::Normal);
+ },
+ m_jsFileWithPath, lookup);
+}
+
+std::shared_ptr<ExternalItemInfo<QmltypesFile>>
+DomEnvironment::qmltypesFileWithPath(DomItem &self, QString path, EnvLookup options) const
+{
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ if (m_qmltypesFileWithPath.contains(path))
+ return m_qmltypesFileWithPath.value(path);
+ }
+ if (options != EnvLookup::NoBase && m_base)
+ return m_base->qmltypesFileWithPath(self, path, EnvLookup::Normal);
+ return {};
+}
+
+QSet<QString> DomEnvironment::qmltypesFilePaths(DomItem &, EnvLookup lookup) const
+{
+ return getStrings<std::shared_ptr<ExternalItemInfo<QmltypesFile>>>(
+ [this] {
+ DomItem baseObj(m_base);
+ return m_base->qmltypesFilePaths(baseObj, EnvLookup::Normal);
+ },
+ m_qmltypesFileWithPath, lookup);
+}
+
+std::shared_ptr<ExternalItemInfo<GlobalScope>>
+DomEnvironment::globalScopeWithName(DomItem &self, QString name, EnvLookup lookupOptions) const
+{
+ if (lookupOptions != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ auto id = m_globalScopeWithName.find(name);
+ if (id != m_globalScopeWithName.end())
+ return *id;
+ }
+ if (lookupOptions != EnvLookup::NoBase && m_base)
+ return m_base->globalScopeWithName(self, name, lookupOptions);
+ return {};
+}
+
+std::shared_ptr<ExternalItemInfo<GlobalScope>>
+DomEnvironment::ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup lookupOptions)
+{
+ if (auto current = globalScopeWithName(self, name, lookupOptions))
+ return current;
+ if (auto u = universe()) {
+ if (auto newVal = u->ensureGlobalScopeWithName(name)) {
+ if (auto current = newVal->current) {
+ DomItem currentObj = DomItem(u).copy(current);
+ auto newScope = current->makeCopy(currentObj);
+ std::shared_ptr<ExternalItemInfo<GlobalScope>> newCopy(
+ new ExternalItemInfo<GlobalScope>(newScope));
+ QMutexLocker l(mutex());
+ if (auto oldVal = m_globalScopeWithName.value(name))
+ return oldVal;
+ m_globalScopeWithName.insert(name, newCopy);
+ return newCopy;
+ }
+ }
+ }
+ Q_ASSERT_X(false, "DomEnvironment::ensureGlobalScopeWithName", "could not ensure globalScope");
+ return {};
+}
+
+QSet<QString> DomEnvironment::globalScopeNames(DomItem &, EnvLookup lookupOptions) const
+{
+ QSet<QString> res;
+ if (lookupOptions != EnvLookup::NoBase && m_base) {
+ if (m_base) {
+ DomItem baseObj(m_base);
+ res = m_base->globalScopeNames(baseObj, EnvLookup::Normal);
+ }
+ }
+ if (lookupOptions != EnvLookup::BaseOnly) {
+ QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_globalScopeWithName;
+ }
+ auto it = map.keyBegin();
+ auto end = map.keyEnd();
+ while (it != end) {
+ res += *it;
+ ++it;
+ }
+ }
+ return res;
+}
+
+void DomEnvironment::addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadInfo)
+{
+ if (!loadInfo)
+ return;
+ Path p = loadInfo->elementCanonicalPath();
+ bool addWork = loadInfo->status() != LoadInfo::Status::Done;
+ std::shared_ptr<LoadInfo> oldVal;
+ {
+ QMutexLocker l(mutex());
+ oldVal = m_loadInfos.value(p);
+ m_loadInfos.insert(p, loadInfo);
+ if (addWork)
+ m_loadsWithWork.enqueue(p);
+ }
+ if (oldVal && oldVal->status() != LoadInfo::Status::Done) {
+ self.addError(myErrors()
+ .error(tr("addLoadinfo replaces a non finished load info for %1")
+ .arg(p.toString()))
+ .handle());
+ }
+}
+
+std::shared_ptr<LoadInfo> DomEnvironment::loadInfo(Path path) const
+{
+ QMutexLocker l(mutex());
+ return m_loadInfos.value(path);
+}
+
+QHash<Path, std::shared_ptr<LoadInfo>> DomEnvironment::loadInfos() const
+{
+ QMutexLocker l(mutex());
+ return m_loadInfos;
+}
+
+QList<Path> DomEnvironment::loadInfoPaths() const
+{
+ auto lInfos = loadInfos();
+ return lInfos.keys();
+}
+
+DomItem::Callback DomEnvironment::callbackForQmlDirectory(DomItem &self, Callback loadCallback,
+ Callback allDirectDepsCallback,
+ Callback endCallback)
+{
+ return envCallbackForFile<QmlDirectory>(self, &DomEnvironment::m_qmlDirectoryWithPath,
+ &DomEnvironment::qmlDirectoryWithPath, loadCallback,
+ allDirectDepsCallback, endCallback);
+}
+
+DomItem::Callback DomEnvironment::callbackForQmlFile(DomItem &self, Callback loadCallback,
+ Callback allDirectDepsCallback,
+ Callback endCallback)
+{
+ return envCallbackForFile<QmlFile>(self, &DomEnvironment::m_qmlFileWithPath,
+ &DomEnvironment::qmlFileWithPath, loadCallback,
+ allDirectDepsCallback, endCallback);
+}
+
+DomTop::Callback DomEnvironment::callbackForQmltypesFile(DomItem &self,
+ DomTop::Callback loadCallback,
+ Callback allDirectDepsCallback,
+ DomTop::Callback endCallback)
+{
+ return envCallbackForFile<QmltypesFile>(
+ self, &DomEnvironment::m_qmltypesFileWithPath, &DomEnvironment::qmltypesFileWithPath,
+ [loadCallback](Path p, DomItem &oldV, DomItem &newV) {
+ DomItem newFile = newV.field(Fields::currentItem);
+ if (std::shared_ptr<QmltypesFile> newFilePtr = newFile.ownerAs<QmltypesFile>())
+ newFilePtr->ensureInModuleIndex(newFile);
+ if (loadCallback)
+ loadCallback(p, oldV, newV);
+ },
+ allDirectDepsCallback, endCallback);
+}
+
+DomTop::Callback DomEnvironment::callbackForQmldirFile(DomItem &self, DomTop::Callback loadCallback,
+ Callback allDirectDepsCallback,
+ DomTop::Callback endCallback)
+{
+ return envCallbackForFile<QmldirFile>(self, &DomEnvironment::m_qmldirFileWithPath,
+ &DomEnvironment::qmldirFileWithPath, loadCallback,
+ allDirectDepsCallback, endCallback);
+}
+
+DomEnvironment::DomEnvironment(QStringList loadPaths, Options options,
+ shared_ptr<DomUniverse> universe)
+ : m_options(options),
+ m_universe(DomUniverse::guaranteeUniverse(universe)),
+ m_loadPaths(loadPaths),
+ m_implicitImports(defaultImplicitImports())
{}
-DomEnvironment::DomEnvironment(shared_ptr<DomEnvironment> parent, QStringList loadPaths, Options options):
- m_options(options), m_base(parent), m_loadPaths(loadPaths)
+DomItem DomEnvironment::create(QStringList loadPaths, Options options, DomItem &universe)
+{
+ std::shared_ptr<DomUniverse> universePtr = universe.ownerAs<DomUniverse>();
+ std::shared_ptr<DomEnvironment> envPtr(new DomEnvironment(loadPaths, options, universePtr));
+ return DomItem(envPtr);
+}
+
+DomEnvironment::DomEnvironment(shared_ptr<DomEnvironment> parent, QStringList loadPaths,
+ Options options)
+ : m_options(options),
+ m_base(parent),
+ m_loadPaths(loadPaths),
+ m_implicitImports(defaultImplicitImports())
{}
-Path ExternalItemInfoBase::canonicalPath(const DomItem &self) const
+template<typename T>
+std::shared_ptr<ExternalItemInfo<T>>
+addExternalItem(std::shared_ptr<T> file, QString key,
+ QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &map, AddOption option,
+ QBasicMutex *mutex)
{
- shared_ptr<ExternalOwningItem> current = currentItem();
- return current->canonicalPath(self.copy(current, Path(), current.get())).dropTail();
+ if (!file)
+ return {};
+ std::shared_ptr<ExternalItemInfo<T>> eInfo(
+ new ExternalItemInfo<T>(file, QDateTime::currentDateTime()));
+ {
+ QMutexLocker l(mutex);
+ auto it = map.find(key);
+ if (it != map.end()) {
+ switch (option) {
+ case AddOption::KeepExisting:
+ eInfo = *it;
+ break;
+ case AddOption::Overwrite:
+ map.insert(key, eInfo);
+ break;
+ }
+ } else {
+ map.insert(key, eInfo);
+ }
+ }
+ return eInfo;
}
-QString ExternalItemInfoBase::canonicalFilePath(const DomItem &self) const
+std::shared_ptr<ExternalItemInfo<QmlFile>> DomEnvironment::addQmlFile(std::shared_ptr<QmlFile> file,
+ AddOption options)
{
- shared_ptr<ExternalOwningItem> current = currentItem();
- return current->canonicalFilePath(self.copy(current, Path(), current.get()));
+ return addExternalItem<QmlFile>(file, file->canonicalFilePath(), m_qmlFileWithPath, options,
+ mutex());
+}
+
+std::shared_ptr<ExternalItemInfo<QmlDirectory>>
+DomEnvironment::addQmlDirectory(std::shared_ptr<QmlDirectory> file, AddOption options)
+{
+ return addExternalItem<QmlDirectory>(file, file->canonicalFilePath(), m_qmlDirectoryWithPath,
+ options, mutex());
+}
+
+std::shared_ptr<ExternalItemInfo<QmldirFile>>
+DomEnvironment::addQmldirFile(std::shared_ptr<QmldirFile> file, AddOption options)
+{
+ return addExternalItem<QmldirFile>(file, file->canonicalFilePath(), m_qmldirFileWithPath,
+ options, mutex());
+}
+
+std::shared_ptr<ExternalItemInfo<QmltypesFile>>
+DomEnvironment::addQmltypesFile(std::shared_ptr<QmltypesFile> file, AddOption options)
+{
+ return addExternalItem<QmltypesFile>(file, file->canonicalFilePath(), m_qmltypesFileWithPath,
+ options, mutex());
+}
+
+std::shared_ptr<ExternalItemInfo<JsFile>> DomEnvironment::addJsFile(std::shared_ptr<JsFile> file,
+ AddOption options)
+{
+ return addExternalItem<JsFile>(file, file->canonicalFilePath(), m_jsFileWithPath, options,
+ mutex());
+}
+
+std::shared_ptr<ExternalItemInfo<GlobalScope>>
+DomEnvironment::addGlobalScope(std::shared_ptr<GlobalScope> scope, AddOption options)
+{
+ return addExternalItem<GlobalScope>(scope, scope->name(), m_globalScopeWithName, options,
+ mutex());
+}
+
+bool DomEnvironment::commitToBase(DomItem &self)
+{
+ if (!base())
+ return false;
+ QMap<QString, QMap<int, std::shared_ptr<ModuleIndex>>> my_moduleIndexWithUri;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> my_globalScopeWithName;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmlDirectory>>> my_qmlDirectoryWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmldirFile>>> my_qmldirFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmlFile>>> my_qmlFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> my_jsFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmltypesFile>>> my_qmltypesFileWithPath;
+ QHash<Path, std::shared_ptr<LoadInfo>> my_loadInfos;
+ {
+ QMutexLocker l(mutex());
+ my_moduleIndexWithUri = m_moduleIndexWithUri;
+ my_globalScopeWithName = m_globalScopeWithName;
+ my_qmlDirectoryWithPath = m_qmlDirectoryWithPath;
+ my_qmldirFileWithPath = m_qmldirFileWithPath;
+ my_qmlFileWithPath = m_qmlFileWithPath;
+ my_jsFileWithPath = m_jsFileWithPath;
+ my_qmltypesFileWithPath = m_qmltypesFileWithPath;
+ my_loadInfos = m_loadInfos;
+ }
+ {
+ QMutexLocker lBase(base()->mutex()); // be more careful about makeCopy calls with lock?
+ m_base->m_globalScopeWithName.insert(my_globalScopeWithName);
+ m_base->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
+ m_base->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
+ m_base->m_qmlFileWithPath.insert(my_qmlFileWithPath);
+ m_base->m_jsFileWithPath.insert(my_jsFileWithPath);
+ m_base->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
+ m_base->m_loadInfos.insert(my_loadInfos);
+ {
+ auto it = my_moduleIndexWithUri.cbegin();
+ auto end = my_moduleIndexWithUri.cend();
+ while (it != end) {
+ QMap<int, shared_ptr<ModuleIndex>> &myVersions =
+ m_base->m_moduleIndexWithUri[it.key()];
+ auto it2 = it.value().cbegin();
+ auto end2 = it.value().cend();
+ while (it2 != end2) {
+ auto oldV = myVersions.value(it2.key());
+ DomItem it2Obj = self.copy(it2.value());
+ auto newV = it2.value()->makeCopy(it2Obj);
+ newV->mergeWith(oldV);
+ myVersions.insert(it2.key(), newV);
+ ++it2;
+ }
+ ++it;
+ }
+ }
+ }
+ return true;
}
-Path ExternalItemInfoBase::pathFromOwner(const DomItem &self) const
+void DomEnvironment::loadPendingDependencies(DomItem &self)
+{
+ while (true) {
+ Path elToDo;
+ std::shared_ptr<LoadInfo> loadInfo;
+ {
+ QMutexLocker l(mutex());
+ if (m_loadsWithWork.isEmpty())
+ break;
+ elToDo = m_loadsWithWork.dequeue();
+ m_inProgress.append(elToDo);
+ loadInfo = m_loadInfos.value(elToDo);
+ }
+ if (loadInfo) {
+ auto cleanup = qScopeGuard([this, elToDo, &self] {
+ QList<Callback> endCallbakcs;
+ {
+ QMutexLocker l(mutex());
+ m_inProgress.removeOne(elToDo);
+ if (m_inProgress.isEmpty() && m_loadsWithWork.isEmpty()) {
+ endCallbakcs = m_allLoadedCallback;
+ m_allLoadedCallback.clear();
+ }
+ }
+ for (Callback cb : endCallbakcs)
+ cb(self.canonicalPath(), self, self);
+ });
+ DomItem loadInfoObj = self.copy(loadInfo);
+ loadInfo->advanceLoad(loadInfoObj);
+ } else {
+ self.addError(myErrors().error(u"DomEnvironment::loadPendingDependencies could not "
+ u"find loadInfo listed in m_loadsWithWork"));
+ {
+ QMutexLocker l(mutex());
+ m_inProgress.removeOne(elToDo);
+ }
+ Q_ASSERT(false
+ && "DomEnvironment::loadPendingDependencies could not find loadInfo listed in "
+ "m_loadsWithWork");
+ }
+ }
+}
+
+bool DomEnvironment::finishLoadingDependencies(DomItem &self, int waitMSec)
+{
+ bool hasPendingLoads = true;
+ QDateTime endTime = QDateTime::currentDateTime().addMSecs(waitMSec);
+ for (int i = 0; i < waitMSec / 10 + 2; ++i) {
+ loadPendingDependencies(self);
+ auto lInfos = loadInfos();
+ auto it = lInfos.cbegin();
+ auto end = lInfos.cend();
+ hasPendingLoads = false;
+ while (it != end) {
+ if (*it && (*it)->status() != LoadInfo::Status::Done)
+ hasPendingLoads = true;
+ }
+ if (!hasPendingLoads)
+ break;
+ auto missing = QDateTime::currentDateTime().msecsTo(endTime);
+ if (missing < 0)
+ break;
+ if (missing > 100)
+ missing = 100;
+#if QT_FEATURE_thread
+ QThread::msleep(missing);
+#endif
+ }
+ return !hasPendingLoads;
+}
+
+void DomEnvironment::addWorkForLoadInfo(Path elementCanonicalPath)
+{
+ QMutexLocker l(mutex());
+ m_loadsWithWork.enqueue(elementCanonicalPath);
+}
+
+DomEnvironment::Options DomEnvironment::options() const
+{
+ return m_options;
+}
+
+std::shared_ptr<DomEnvironment> DomEnvironment::base() const
+{
+ return m_base;
+}
+
+QStringList DomEnvironment::loadPaths() const
+{
+ QMutexLocker l(mutex());
+ return m_loadPaths;
+}
+
+QString DomEnvironment::globalScopeName() const
+{
+ return m_globalScopeName;
+}
+
+QList<Import> DomEnvironment::defaultImplicitImports()
+{
+ return QList<Import>({ Import::fromUriString(QLatin1String("QML"), Version(1, 0)),
+ Import(QLatin1String("QtQml"), Version(6, 0)) });
+}
+
+QList<Import> DomEnvironment::implicitImports() const
+{
+ return m_implicitImports;
+}
+
+void DomEnvironment::addAllLoadedCallback(DomItem &self, DomTop::Callback c)
+{
+ if (c) {
+ bool immediate = false;
+ {
+ QMutexLocker l(mutex());
+ if (m_loadsWithWork.isEmpty() && m_inProgress.isEmpty())
+ immediate = true;
+ else
+ m_allLoadedCallback.append(c);
+ }
+ if (immediate)
+ c(Path(), self, self);
+ }
+}
+
+void DomEnvironment::clearReferenceCache()
+{
+ m_referenceCache.clear();
+}
+
+QString ExternalItemInfoBase::canonicalFilePath(DomItem &self) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
- return current->pathFromOwner(self.copy(current, Path(), current.get())).dropTail();
+ DomItem currentObj = currentItem(self);
+ return current->canonicalFilePath(currentObj);
}
-bool ExternalItemInfoBase::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool ExternalItemInfoBase::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
- if (!self.subDataField(Fields::currentRevision, currentRevision(self)).visit(visitor))
+ if (!self.dvValueLazyField(visitor, Fields::currentRevision,
+ [this, &self]() { return currentRevision(self); }))
return false;
- if (!self.subDataField(Fields::lastRevision, lastRevision(self)).visit(visitor))
+ if (!self.dvValueLazyField(visitor, Fields::lastRevision,
+ [this, &self]() { return lastRevision(self); }))
return false;
- if (!self.subDataField(Fields::lastValidRevision, QCborValue(lastValidRevision(self))).visit(visitor))
+ if (!self.dvValueLazyField(visitor, Fields::lastValidRevision,
+ [this, &self]() { return lastValidRevision(self); }))
return false;
- DomItem cItem = self.copy(currentItem(), Path(), currentItem().get());
- if (!visitor(Path::Field(Fields::currentItem), cItem))
+ if (!visitor(PathEls::Field(Fields::currentItem),
+ [&self, this]() { return currentItem(self); }))
return false;
- if (!self.subDataField(Fields::currentExposedAt, QCborValue(currentExposedAt())).visit(visitor))
+ if (!self.dvValueLazyField(visitor, Fields::currentExposedAt,
+ [this]() { return currentExposedAt(); }))
return false;
return true;
}
-int ExternalItemInfoBase::currentRevision(const DomItem &) const
+int ExternalItemInfoBase::currentRevision(DomItem &) const
{
return currentItem()->revision();
}
-int ExternalItemInfoBase::lastRevision(const DomItem &self) const
+int ExternalItemInfoBase::lastRevision(DomItem &self) const
{
Path p = currentItem()->canonicalPath();
DomItem lastValue = self.universe()[p.mid(1, p.length() - 1)].field(u"revision");
return static_cast<int>(lastValue.value().toInteger(0));
}
-int ExternalItemInfoBase::lastValidRevision(const DomItem &self) const
+int ExternalItemInfoBase::lastValidRevision(DomItem &self) const
{
Path p = currentItem()->canonicalPath();
DomItem lastValidValue = self.universe()[p.mid(1, p.length() - 2)].field(u"validItem").field(u"revision");
return static_cast<int>(lastValidValue.value().toInteger(0));
}
-QString ExternalItemPairBase::canonicalFilePath(const DomItem &self) const
-{
- shared_ptr<ExternalOwningItem> current = currentItem();
- return current->canonicalFilePath(self.copy(current, Path(), current.get()));
-}
-
-Path ExternalItemPairBase::pathFromOwner(const DomItem &self) const
+QString ExternalItemPairBase::canonicalFilePath(DomItem &) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
- return current->pathFromOwner(self.copy(current, Path(), current.get())).dropTail();
+ return current->canonicalFilePath();
}
-Path ExternalItemPairBase::canonicalPath(const DomItem &) const
+Path ExternalItemPairBase::canonicalPath(DomItem &) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
return current->canonicalPath().dropTail();
}
-bool ExternalItemPairBase::iterateDirectSubpaths(DomItem &self, function_ref<bool (Path, DomItem &)> visitor)
+bool ExternalItemPairBase::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
{
- if (!self.subDataField(Fields::currentIsValid, currentIsValid()).visit(visitor))
+ if (!self.dvValueLazyField(visitor, Fields::currentIsValid,
+ [this]() { return currentIsValid(); }))
return false;
- DomItem vItem = self.copy(validItem(), Path(), validItem().get());
- if (!visitor(Path::Field(Fields::validItem), vItem))
+ if (!visitor(PathEls::Field(Fields::validItem), [this, &self]() { return validItem(self); }))
return false;
- DomItem cItem = self.copy(currentItem(), Path(), currentItem().get());
- if (!visitor(Path::Field(Fields::currentItem), cItem))
+ if (!visitor(PathEls::Field(Fields::currentItem),
+ [this, &self]() { return currentItem(self); }))
return false;
- if (!self.subDataField(Fields::validExposedAt, QCborValue(validExposedAt)).visit(visitor))
+ if (!self.dvValueField(visitor, Fields::validExposedAt, validExposedAt))
return false;
- if (!self.subDataField(Fields::currentExposedAt, QCborValue(currentExposedAt)).visit(visitor))
+ if (!self.dvValueField(visitor, Fields::currentExposedAt, currentExposedAt))
return false;
return true;
}
@@ -465,6 +2258,54 @@ bool ExternalItemPairBase::currentIsValid() const
return currentItem() == validItem();
}
+RefCacheEntry RefCacheEntry::forPath(DomItem &el, Path canonicalPath)
+{
+ DomItem env = el.environment();
+ std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
+ RefCacheEntry cached;
+ if (envPtr) {
+ QMutexLocker l(envPtr->mutex());
+ cached = envPtr->m_referenceCache.value(canonicalPath, {});
+ } else {
+ Q_ASSERT(false);
+ }
+ return cached;
+}
+
+bool RefCacheEntry::addForPath(DomItem &el, Path canonicalPath, const RefCacheEntry &entry,
+ AddOption addOption)
+{
+ DomItem env = el.environment();
+ std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
+ bool didSet = false;
+ if (envPtr) {
+ QMutexLocker l(envPtr->mutex());
+ RefCacheEntry &cached = envPtr->m_referenceCache[canonicalPath];
+ switch (cached.cached) {
+ case RefCacheEntry::Cached::None:
+ cached = entry;
+ didSet = true;
+ break;
+ case RefCacheEntry::Cached::First:
+ if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
+ cached = entry;
+ didSet = true;
+ }
+ break;
+ case RefCacheEntry::Cached::All:
+ if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
+ cached = entry;
+ didSet = true;
+ }
+ }
+ if (cached.cached == RefCacheEntry::Cached::First && cached.canonicalPaths.isEmpty())
+ cached.cached = RefCacheEntry::Cached::All;
+ } else {
+ Q_ASSERT(false);
+ }
+ return didSet;
+}
+
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomtop_p.h b/src/qmldom/qqmldomtop_p.h
index 4143cc3ba9..4c905991af 100644
--- a/src/qmldom/qqmldomtop_p.h
+++ b/src/qmldom/qqmldomtop_p.h
@@ -50,7 +50,7 @@
//
#include "qqmldomitem_p.h"
-
+#include "qqmldomelements_p.h"
#include "qqmldomexternalitems_p.h"
#include <QtCore/QQueue>
@@ -68,7 +68,6 @@ namespace QQmlJS {
namespace Dom {
class QMLDOM_EXPORT ParsingTask {
- Q_GADGET
public:
QCborMap toCbor() const {
return QCborMap(
@@ -90,14 +89,14 @@ public:
QString contents;
QDateTime contentsDate;
std::weak_ptr<DomUniverse> requestingUniverse; // make it a shared_ptr?
- function<void(Path, DomItem, DomItem)> callback;
+ function<void(Path, DomItem &, DomItem &)> callback;
};
class QMLDOM_EXPORT ExternalItemPairBase: public OwningItem { // all access should have the lock of the DomUniverse containing this
Q_DECLARE_TR_FUNCTIONS(ExternalItemPairBase);
public:
constexpr static DomType kindValue = DomType::ExternalItemPair;
- DomType kind() const override { return kindValue; }
+ DomType kind() const final override { return kindValue; }
ExternalItemPairBase(QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0),
QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
int derivedFrom=0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0)):
@@ -107,26 +106,34 @@ public:
OwningItem(o), validExposedAt(o.validExposedAt), currentExposedAt(o.currentExposedAt)
{}
virtual std::shared_ptr<ExternalOwningItem> validItem() const = 0;
+ virtual DomItem validItem(DomItem &self) const = 0;
virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
+ virtual DomItem currentItem(DomItem &self) const = 0;
- QString canonicalFilePath(const DomItem &) const override;
- Path pathFromOwner(const DomItem &self) const override;
- Path canonicalPath(const DomItem &self) const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
+ QString canonicalFilePath(DomItem &) const final override;
+ Path canonicalPath(DomItem &self) const final override;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) final override;
+ DomItem field(DomItem &self, QStringView name) const final override
+ {
+ return OwningItem::field(self, name);
+ }
bool currentIsValid() const;
- std::shared_ptr<ExternalItemPairBase> makeCopy(const DomItem &self) {
+ std::shared_ptr<ExternalItemPairBase> makeCopy(DomItem &self) const
+ {
return std::static_pointer_cast<ExternalItemPairBase>(doCopy(self));
}
- QDateTime lastDataUpdateAt() const override {
+ QDateTime lastDataUpdateAt() const final override
+ {
if (currentItem())
return currentItem()->lastDataUpdateAt();
return ExternalItemPairBase::lastDataUpdateAt();
}
- void refreshedDataAt(QDateTime tNew) override {
+ void refreshedDataAt(QDateTime tNew) final override
+ {
return OwningItem::refreshedDataAt(tNew);
if (currentItem())
currentItem()->refreshedDataAt(tNew);
@@ -139,13 +146,16 @@ public:
};
template<class T>
-class QMLDOM_EXPORT ExternalItemPair: public ExternalItemPairBase { // all access should have the lock of the DomUniverse containing this
+class QMLDOM_EXPORT ExternalItemPair final : public ExternalItemPairBase
+{ // all access should have the lock of the DomUniverse containing this
protected:
- std::shared_ptr<OwningItem> doCopy(const DomItem &) const override {
- return std::make_shared<ExternalItemPair>(*this);
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ return std::shared_ptr<OwningItem>(new ExternalItemPair(*this));
}
public:
+ constexpr static DomType kindValue = DomType::ExternalItemPair;
friend class DomUniverse;
ExternalItemPair(std::shared_ptr<T> valid = {}, std::shared_ptr<T> current = {},
QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0),
@@ -160,8 +170,11 @@ public:
QMutexLocker l(mutex());
}
std::shared_ptr<ExternalOwningItem> validItem() const override { return valid; }
+ DomItem validItem(DomItem &self) const override { return self.copy(valid); }
std::shared_ptr<ExternalOwningItem> currentItem() const override { return current; }
- std::shared_ptr<ExternalItemPair> makeCopy(const DomItem &self) {
+ DomItem currentItem(DomItem &self) const override { return self.copy(current); }
+ std::shared_ptr<ExternalItemPair> makeCopy(DomItem &self) const
+ {
return std::static_pointer_cast<ExternalItemPair>(doCopy(self));
}
@@ -171,13 +184,13 @@ public:
class QMLDOM_EXPORT DomTop: public OwningItem {
public:
- DomTop(QMap<QString, std::shared_ptr<OwningItem>> extraOwningItems = {}, int derivedFrom=0):
- OwningItem(derivedFrom), m_extraOwningItems(extraOwningItems)
+ DomTop(QMap<QString, OwnerT> extraOwningItems = {}, int derivedFrom = 0)
+ : OwningItem(derivedFrom), m_extraOwningItems(extraOwningItems)
{}
DomTop(const DomTop &o):
OwningItem(o)
{
- QMap<QString, std::shared_ptr<OwningItem>> items = o.extraOwningItems();
+ QMap<QString, OwnerT> items = o.extraOwningItems();
{
QMutexLocker l(mutex());
m_extraOwningItems = items;
@@ -187,27 +200,39 @@ public:
virtual Path canonicalPath() const = 0;
- Path pathFromOwner(const DomItem &) const override;
- Path canonicalPath(const DomItem &) const override;
- DomItem containingObject(const DomItem&) const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
+ Path canonicalPath(DomItem &) const override;
+ DomItem containingObject(DomItem &) const override;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ template<typename T>
+ void setExtraOwningItem(QString fieldName, std::shared_ptr<T> item)
+ {
+ QMutexLocker l(mutex());
+ if (!item)
+ m_extraOwningItems.remove(fieldName);
+ else
+ m_extraOwningItems.insert(fieldName, item);
+ }
- void setExtraOwningItem(QString fieldName, std::shared_ptr<OwningItem> item);
void clearExtraOwningItems();
- QMap<QString, std::shared_ptr<OwningItem>> extraOwningItems() const;
+ QMap<QString, OwnerT> extraOwningItems() const;
+
private:
- QMap<QString, std::shared_ptr<OwningItem>> m_extraOwningItems;
+ QMap<QString, OwnerT> m_extraOwningItems;
};
-class QMLDOM_EXPORT DomUniverse: public DomTop {
+class QMLDOM_EXPORT DomUniverse final : public DomTop
+{
+ Q_GADGET
Q_DECLARE_TR_FUNCTIONS(DomUniverse);
protected:
- std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+
public:
enum class Option{
Default,
SingleThreaded
};
+ Q_ENUM(Option)
Q_DECLARE_FLAGS(Options, Option);
constexpr static DomType kindValue = DomType::DomUniverse;
DomType kind() const override { return kindValue; }
@@ -216,19 +241,128 @@ public:
DomUniverse(QString universeName, Options options = Option::SingleThreaded);
DomUniverse(const DomUniverse &) = delete;
+ static std::shared_ptr<DomUniverse> guaranteeUniverse(std::shared_ptr<DomUniverse> univ);
+ static DomItem create(QString universeName, Options options = Option::SingleThreaded);
Path canonicalPath() const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
- std::shared_ptr<DomUniverse> makeCopy(const DomItem &self) {
+ using DomTop::canonicalPath;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ std::shared_ptr<DomUniverse> makeCopy(DomItem &self) const
+ {
return std::static_pointer_cast<DomUniverse>(doCopy(self));
}
- void loadFile(const DomItem &self, QString filePath, QString logicalPath,
- Callback callback, LoadOptions loadOptions);
- void loadFile(const DomItem &self, QString canonicalFilePath, QString logicalPath,
- QString code, QDateTime codeDate, Callback callback, LoadOptions loadOptions);
+ void loadFile(DomItem &self, QString filePath, QString logicalPath, Callback callback,
+ LoadOptions loadOptions);
+ void loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath, QString code,
+ QDateTime codeDate, Callback callback, LoadOptions loadOptions);
void execQueue();
+ std::shared_ptr<ExternalItemPair<GlobalScope>> globalScopeWithName(QString name) const
+ {
+ QMutexLocker l(mutex());
+ return m_globalScopeWithName.value(name);
+ }
+
+ std::shared_ptr<ExternalItemPair<GlobalScope>> ensureGlobalScopeWithName(QString name)
+ {
+ if (auto current = globalScopeWithName(name))
+ return current;
+ std::shared_ptr<GlobalScope> newScope(new GlobalScope(name));
+ std::shared_ptr<ExternalItemPair<GlobalScope>> newValue(
+ new ExternalItemPair<GlobalScope>(newScope, newScope));
+ QMutexLocker l(mutex());
+ if (auto current = m_globalScopeWithName.value(name))
+ return current;
+ m_globalScopeWithName.insert(name, newValue);
+ return newValue;
+ }
+
+ QSet<QString> globalScopeNames() const
+ {
+ QMap<QString, std::shared_ptr<ExternalItemPair<GlobalScope>>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_globalScopeWithName;
+ }
+ return QSet<QString>(map.keyBegin(), map.keyEnd());
+ }
+
+ std::shared_ptr<ExternalItemPair<QmlDirectory>> qmlDirectoryWithPath(QString path) const
+ {
+ QMutexLocker l(mutex());
+ return m_qmlDirectoryWithPath.value(path);
+ }
+ QSet<QString> qmlDirectoryPaths() const
+ {
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmlDirectory>>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_qmlDirectoryWithPath;
+ }
+ return QSet<QString>(map.keyBegin(), map.keyEnd());
+ }
+
+ std::shared_ptr<ExternalItemPair<QmldirFile>> qmldirFileWithPath(QString path) const
+ {
+ QMutexLocker l(mutex());
+ return m_qmldirFileWithPath.value(path);
+ }
+ QSet<QString> qmldirFilePaths() const
+ {
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmldirFile>>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_qmldirFileWithPath;
+ }
+ return QSet<QString>(map.keyBegin(), map.keyEnd());
+ }
+
+ std::shared_ptr<ExternalItemPair<QmlFile>> qmlFileWithPath(QString path) const
+ {
+ QMutexLocker l(mutex());
+ return m_qmlFileWithPath.value(path);
+ }
+ QSet<QString> qmlFilePaths() const
+ {
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmlFile>>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_qmlFileWithPath;
+ }
+ return QSet<QString>(map.keyBegin(), map.keyEnd());
+ }
+
+ std::shared_ptr<ExternalItemPair<JsFile>> jsFileWithPath(QString path) const
+ {
+ QMutexLocker l(mutex());
+ return m_jsFileWithPath.value(path);
+ }
+ QSet<QString> jsFilePaths() const
+ {
+ QMap<QString, std::shared_ptr<ExternalItemPair<JsFile>>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_jsFileWithPath;
+ }
+ return QSet<QString>(map.keyBegin(), map.keyEnd());
+ }
+
+ std::shared_ptr<ExternalItemPair<QmltypesFile>> qmltypesFileWithPath(QString path) const
+ {
+ QMutexLocker l(mutex());
+ return m_qmltypesFileWithPath.value(path);
+ }
+ QSet<QString> qmltypesFilePaths() const
+ {
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmltypesFile>>> map;
+ {
+ QMutexLocker l(mutex());
+ map = m_qmltypesFileWithPath;
+ }
+ return QSet<QString>(map.keyBegin(), map.keyEnd());
+ }
+
QString name() const {
return m_name;
}
@@ -243,6 +377,12 @@ public:
private:
QString m_name;
Options m_options;
+ QMap<QString, std::shared_ptr<ExternalItemPair<GlobalScope>>> m_globalScopeWithName;
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmlDirectory>>> m_qmlDirectoryWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmldirFile>>> m_qmldirFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmlFile>>> m_qmlFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemPair<JsFile>>> m_jsFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemPair<QmltypesFile>>> m_qmltypesFileWithPath;
QQueue<ParsingTask> m_queue;
};
@@ -252,38 +392,47 @@ class QMLDOM_EXPORT ExternalItemInfoBase: public OwningItem {
Q_DECLARE_TR_FUNCTIONS(ExternalItemInfoBase);
public:
constexpr static DomType kindValue = DomType::ExternalItemInfo;
- DomType kind() const override { return kindValue; }
- ExternalItemInfoBase(QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
- int derivedFrom=0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0)):
- OwningItem(derivedFrom, lastDataUpdateAt), m_currentExposedAt(currentExposedAt)
- {}
- ExternalItemInfoBase(const ExternalItemInfoBase &o):
- OwningItem(o), m_currentExposedAt(o.currentExposedAt()),
- m_logicalFilePaths(o.logicalFilePaths())
+ DomType kind() const final override { return kindValue; }
+ ExternalItemInfoBase(Path canonicalPath,
+ QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
+ int derivedFrom = 0,
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0))
+ : OwningItem(derivedFrom, lastDataUpdateAt),
+ m_canonicalPath(canonicalPath),
+ m_currentExposedAt(currentExposedAt)
{}
+ ExternalItemInfoBase(const ExternalItemInfoBase &o) = default;
virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
+ virtual DomItem currentItem(DomItem &) const = 0;
- QString canonicalFilePath(const DomItem &) const override;
- Path canonicalPath(const DomItem &) const override;
- Path pathFromOwner(const DomItem &self) const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
+ QString canonicalFilePath(DomItem &) const final override;
+ Path canonicalPath() const { return m_canonicalPath; }
+ Path canonicalPath(DomItem &) const final override { return canonicalPath(); }
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) final override;
+ DomItem field(DomItem &self, QStringView name) const final override
+ {
+ return OwningItem::field(self, name);
+ }
- int currentRevision(const DomItem &self) const;
- int lastRevision(const DomItem &self) const;
- int lastValidRevision(const DomItem &self) const;
+ int currentRevision(DomItem &self) const;
+ int lastRevision(DomItem &self) const;
+ int lastValidRevision(DomItem &self) const;
- std::shared_ptr<ExternalItemInfoBase> makeCopy(const DomItem &self) {
+ std::shared_ptr<ExternalItemInfoBase> makeCopy(DomItem &self) const
+ {
return std::static_pointer_cast<ExternalItemInfoBase>(doCopy(self));
}
- QDateTime lastDataUpdateAt() const override {
+ QDateTime lastDataUpdateAt() const final override
+ {
if (currentItem())
return currentItem()->lastDataUpdateAt();
return OwningItem::lastDataUpdateAt();
}
- void refreshedDataAt(QDateTime tNew) override {
+ void refreshedDataAt(QDateTime tNew) final override
+ {
return OwningItem::refreshedDataAt(tNew);
if (currentItem())
currentItem()->refreshedDataAt(tNew);
@@ -313,51 +462,201 @@ public:
private:
friend class DomEnvironment;
+ Path m_canonicalPath;
QDateTime m_currentExposedAt;
QStringList m_logicalFilePaths;
};
-template <typename T>
-class ExternalItemInfo: public ExternalItemInfoBase {
+template<typename T>
+class ExternalItemInfo final : public ExternalItemInfoBase
+{
protected:
- std::shared_ptr<OwningItem> doCopy(const DomItem &) const override {
- return std::make_shared<ExternalItemInfo>(*this);
+ std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ {
+ return std::shared_ptr<ExternalItemInfo>(new ExternalItemInfo(*this));
}
+
public:
+ constexpr static DomType kindValue = DomType::ExternalItemInfo;
ExternalItemInfo(std::shared_ptr<T> current = std::shared_ptr<T>(),
QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
- int derivedFrom = 0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0)):
- ExternalItemInfoBase(currentExposedAt, derivedFrom, lastDataUpdateAt), current(current)
- {}
- ExternalItemInfo(QString canonicalPath):
- current(std::make_shared<T>(canonicalPath))
+ int derivedFrom = 0,
+ QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0))
+ : ExternalItemInfoBase(current->canonicalPath().dropTail(), currentExposedAt, derivedFrom,
+ lastDataUpdateAt),
+ current(current)
{}
+ ExternalItemInfo(QString canonicalPath) : current(new T(canonicalPath)) { }
ExternalItemInfo(const ExternalItemInfo &o):
ExternalItemInfoBase(o), current(o.current)
{
QMutexLocker l(mutex());
}
- std::shared_ptr<ExternalItemInfo> makeCopy(const DomItem &self) {
+ std::shared_ptr<ExternalItemInfo> makeCopy(DomItem &self) const
+ {
return std::static_pointer_cast<ExternalItemInfo>(doCopy(self));
}
std::shared_ptr<ExternalOwningItem> currentItem() const override {
return current;
}
+ DomItem currentItem(DomItem &self) const override { return self.copy(current); }
std::shared_ptr<T> current;
};
+class Dependency
+{ // internal, should be cleaned, but nobody should use this...
+public:
+ bool operator==(Dependency const &o) const
+ {
+ return uri == o.uri && version.majorVersion == o.version.majorVersion
+ && version.minorVersion == o.version.minorVersion && filePath == o.filePath;
+ }
+ QString uri; // either dotted uri or file:, http: https: uri
+ Version version;
+ QString filePath; // for file deps
+};
+
+class QMLDOM_EXPORT LoadInfo final : public OwningItem
+{
+ Q_DECLARE_TR_FUNCTIONS(LoadInfo);
+
+protected:
+ std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+
+public:
+ constexpr static DomType kindValue = DomType::LoadInfo;
+ DomType kind() const override { return kindValue; }
+
+ enum class Status {
+ NotStarted, // dependencies non checked yet
+ Starting, // adding deps
+ InProgress, // waiting for all deps to be loaded
+ CallingCallbacks, // calling callbacks
+ Done // fully loaded
+ };
+
+ LoadInfo(Path elPath = Path(), Status status = Status::NotStarted, int nLoaded = 0,
+ int derivedFrom = 0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0))
+ : OwningItem(derivedFrom, lastDataUpdateAt),
+ m_elementCanonicalPath(elPath),
+ m_status(status),
+ m_nLoaded(nLoaded)
+ {
+ }
+ LoadInfo(const LoadInfo &o) : OwningItem(o), m_elementCanonicalPath(o.elementCanonicalPath())
+ {
+ {
+ QMutexLocker l(o.mutex());
+ m_status = o.m_status;
+ m_nLoaded = o.m_nLoaded;
+ m_toDo = o.m_toDo;
+ m_inProgress = o.m_inProgress;
+ m_endCallbacks = o.m_endCallbacks;
+ }
+ QMutexLocker l(mutex());
+ }
+
+ Path canonicalPath(DomItem &self) const override;
+
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ std::shared_ptr<LoadInfo> makeCopy(DomItem &self) const
+ {
+ return std::static_pointer_cast<LoadInfo>(doCopy(self));
+ }
+ void addError(DomItem &self, ErrorMessage msg) override
+ {
+ self.path(elementCanonicalPath()).addError(msg);
+ }
+
+ void addEndCallback(DomItem &self, std::function<void(Path, DomItem &, DomItem &)> callback);
+
+ void advanceLoad(DomItem &self);
+ void finishedLoadingDep(DomItem &self, const Dependency &d);
+ void execEnd(DomItem &self);
+
+ Status status() const
+ {
+ QMutexLocker l(mutex());
+ return m_status;
+ }
+
+ int nLoaded() const
+ {
+ QMutexLocker l(mutex());
+ return m_nLoaded;
+ }
+
+ Path elementCanonicalPath() const
+ {
+ QMutexLocker l(mutex()); // we should never change this, remove lock?
+ return m_elementCanonicalPath;
+ }
+
+ int nNotDone() const
+ {
+ QMutexLocker l(mutex());
+ return m_toDo.length() + m_inProgress.length();
+ }
+
+ QList<Dependency> inProgress() const
+ {
+ QMutexLocker l(mutex());
+ return m_inProgress;
+ }
+
+ QList<Dependency> toDo() const
+ {
+ QMutexLocker l(mutex());
+ return m_toDo;
+ }
+
+ int nCallbacks() const
+ {
+ QMutexLocker l(mutex());
+ return m_endCallbacks.length();
+ }
+
+private:
+ void doAddDependencies(DomItem &self);
+ void addDependency(DomItem &self, const Dependency &dep);
+
+ Path m_elementCanonicalPath;
+ Status m_status;
+ int m_nLoaded;
+ QQueue<Dependency> m_toDo;
+ QList<Dependency> m_inProgress;
+ QList<std::function<void(Path, DomItem &, DomItem &)>> m_endCallbacks;
+};
+
enum class EnvLookup { Normal, NoBase, BaseOnly };
enum class Changeable { ReadOnly, Writable };
-class QMLDOM_EXPORT DomEnvironment: public DomTop
+class QMLDOM_EXPORT RefCacheEntry
{
+ Q_GADGET
+public:
+ enum class Cached { None, First, All };
+ Q_ENUM(Cached)
+
+ static RefCacheEntry forPath(DomItem &el, Path canonicalPath);
+ static bool addForPath(DomItem &el, Path canonicalPath, const RefCacheEntry &entry,
+ AddOption addOption = AddOption::KeepExisting);
+
+ Cached cached = Cached::None;
+ QList<Path> canonicalPaths;
+};
+
+class QMLDOM_EXPORT DomEnvironment final : public DomTop
+{
+ Q_GADGET
Q_DECLARE_TR_FUNCTIONS(DomEnvironment);
protected:
- std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+
public:
enum class Option {
Default = 0x0,
@@ -368,50 +667,144 @@ public:
SingleThreaded = 0x10, // do all operations in a single thread
NoDependencies = 0x20 // will not load dependencies (useful when editing)
};
+ Q_ENUM(Option)
Q_DECLARE_FLAGS(Options, Option);
static ErrorGroups myErrors();
constexpr static DomType kindValue = DomType::DomEnvironment;
- DomType kind() const override { return kindValue; }
+ DomType kind() const override;
Path canonicalPath() const override;
- bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem &)>) override;
- std::shared_ptr<DomEnvironment> makeCopy(const DomItem &self) {
- return std::static_pointer_cast<DomEnvironment>(doCopy(self));
- }
+ using DomTop::canonicalPath;
+ bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ DomItem field(DomItem &self, QStringView name) const final override;
+
+ std::shared_ptr<DomEnvironment> makeCopy(DomItem &self) const;
+
+ void loadFile(DomItem &self, QString filePath, QString logicalPath, Callback loadCallback,
+ Callback directDepsCallback, Callback endCallback, LoadOptions loadOptions,
+ ErrorHandler h = nullptr);
+ void loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath, QString code,
+ QDateTime codeDate, Callback loadCallback, Callback directDepsCallback,
+ Callback endCallback, LoadOptions loadOptions, ErrorHandler h = nullptr);
+ void loadModuleDependency(DomItem &self, QString uri, Version v,
+ Callback loadCallback = nullptr, Callback endCallback = nullptr,
+ ErrorHandler = nullptr);
+ void loadBuiltins(DomItem &self, Callback callback = nullptr, ErrorHandler h = nullptr);
std::shared_ptr<DomUniverse> universe() const;
- DomEnvironment(std::shared_ptr<DomUniverse> universe, QStringList loadPaths, Options options = Option::SingleThreaded);
- DomEnvironment(std::shared_ptr<DomEnvironment> parent, QStringList loadPaths, Options options = Option::SingleThreaded);
+ QSet<QString> moduleIndexUris(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ QSet<int> moduleIndexMajorVersions(DomItem &self, QString uri,
+ EnvLookup lookup = EnvLookup::Normal) const;
+ std::shared_ptr<ModuleIndex> moduleIndexWithUri(DomItem &self, QString uri, int majorVersion,
+ EnvLookup lookup, Changeable changeable,
+ ErrorHandler errorHandler = nullptr);
+ std::shared_ptr<ModuleIndex> moduleIndexWithUri(DomItem &self, QString uri, int majorVersion,
+ EnvLookup lookup = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfo<QmlDirectory>>
+ qmlDirectoryWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmlDirectoryPaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfo<QmldirFile>>
+ qmldirFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmldirFilePaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfoBase>
+ qmlDirWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmlDirPaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfo<QmlFile>>
+ qmlFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmlFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfo<JsFile>>
+ jsFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> jsFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfo<QmltypesFile>>
+ qmltypesFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmltypesFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfo<GlobalScope>>
+ globalScopeWithName(DomItem &self, QString name, EnvLookup lookup = EnvLookup::Normal) const;
+ std::shared_ptr<ExternalItemInfo<GlobalScope>>
+ ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup lookup = EnvLookup::Normal);
+ QSet<QString> globalScopeNames(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+
+ explicit DomEnvironment(QStringList loadPaths, Options options = Option::SingleThreaded,
+ std::shared_ptr<DomUniverse> universe = nullptr);
+ explicit DomEnvironment(std::shared_ptr<DomEnvironment> parent, QStringList loadPaths,
+ Options options = Option::SingleThreaded);
DomEnvironment(const DomEnvironment &o) = delete;
+ static DomItem create(QStringList loadPaths, Options options = Option::SingleThreaded,
+ DomItem &universe = DomItem::empty);
- std::shared_ptr<ExternalItemInfo<QmlFile>> addQmlFile(std::shared_ptr<QmlFile> file);
+ std::shared_ptr<ExternalItemInfo<QmlFile>>
+ addQmlFile(std::shared_ptr<QmlFile> file, AddOption option = AddOption::KeepExisting);
+ std::shared_ptr<ExternalItemInfo<QmlDirectory>>
+ addQmlDirectory(std::shared_ptr<QmlDirectory> file, AddOption option = AddOption::KeepExisting);
+ std::shared_ptr<ExternalItemInfo<QmldirFile>>
+ addQmldirFile(std::shared_ptr<QmldirFile> file, AddOption option = AddOption::KeepExisting);
+ std::shared_ptr<ExternalItemInfo<QmltypesFile>>
+ addQmltypesFile(std::shared_ptr<QmltypesFile> file, AddOption option = AddOption::KeepExisting);
+ std::shared_ptr<ExternalItemInfo<JsFile>> addJsFile(std::shared_ptr<JsFile> file,
+ AddOption option = AddOption::KeepExisting);
+ std::shared_ptr<ExternalItemInfo<GlobalScope>>
+ addGlobalScope(std::shared_ptr<GlobalScope> file, AddOption option = AddOption::KeepExisting);
- void commitToBase(const DomItem &self);
+ bool commitToBase(DomItem &self);
- Options options() const {
- return m_options;
- }
+ void addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadInfo);
+ std::shared_ptr<LoadInfo> loadInfo(Path path) const;
+ QList<Path> loadInfoPaths() const;
+ QHash<Path, std::shared_ptr<LoadInfo>> loadInfos() const;
+ void loadPendingDependencies(DomItem &self);
+ bool finishLoadingDependencies(DomItem &self, int waitMSec = 30000);
+ void addWorkForLoadInfo(Path elementCanonicalPath);
- std::shared_ptr<DomEnvironment> base() const {
- return m_base;
- }
+ Options options() const;
- QStringList loadPaths() const {
- QMutexLocker l(mutex());
- return m_loadPaths;
- }
+ std::shared_ptr<DomEnvironment> base() const;
+
+ QStringList loadPaths() const;
+
+ QString globalScopeName() const;
+
+ static QList<Import> defaultImplicitImports();
+ QList<Import> implicitImports() const;
+
+ void addAllLoadedCallback(DomItem &self, Callback c);
+
+ void clearReferenceCache();
private:
+ friend class RefCacheEntry;
+ template<typename T>
+ QSet<QString> getStrings(function_ref<QSet<QString>()> getBase, const QMap<QString, T> &selfMap,
+ EnvLookup lookup) const;
+
+ Callback callbackForQmlDirectory(DomItem &self, Callback loadCallback,
+ Callback directDepsCallback, Callback endCallback);
+ Callback callbackForQmlFile(DomItem &self, Callback loadCallback, Callback directDepsCallback,
+ Callback endCallback);
+ Callback callbackForQmltypesFile(DomItem &self, Callback loadCallback,
+ Callback directDepsCallback, Callback endCallback);
+ Callback callbackForQmldirFile(DomItem &self, Callback loadCallback,
+ Callback directDepsCallback, Callback endCallback);
+
const Options m_options;
const std::shared_ptr<DomEnvironment> m_base;
const std::shared_ptr<DomUniverse> m_universe;
QStringList m_loadPaths; // paths for qml
- bool m_singleThreadedLoadInProgress = false;
+ QString m_globalScopeName;
+ QMap<QString, QMap<int, std::shared_ptr<ModuleIndex>>> m_moduleIndexWithUri;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> m_globalScopeWithName;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmlDirectory>>> m_qmlDirectoryWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmldirFile>>> m_qmldirFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmlFile>>> m_qmlFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> m_jsFileWithPath;
+ QMap<QString, std::shared_ptr<ExternalItemInfo<QmltypesFile>>> m_qmltypesFileWithPath;
QQueue<Path> m_loadsWithWork;
QQueue<Path> m_inProgress;
+ QHash<Path, std::shared_ptr<LoadInfo>> m_loadInfos;
+ QList<Import> m_implicitImports;
QList<Callback> m_allLoadedCallback;
+ QHash<Path, RefCacheEntry> m_referenceCache;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options)