Revision control

++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #ifndef QQMLDOMSCRIPTELEMENTS_P_H
++ #define QQMLDOMSCRIPTELEMENTS_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 "qqmldomelements_p.h"
++ #include "qqmldomfilelocations_p.h"
++ #include "qqmldompath_p.h"
++ #include <algorithm>
++ #include <limits>
++ #include <type_traits>
++ #include <utility>
++ #include <variant>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QQmlJS {
++ namespace Dom {
++
++ namespace ScriptElements {
++
++ template<DomType type>
++ class ScriptElementBase : public ScriptElement
++ {
++ public:
++ using BaseT = ScriptElementBase<type>;
++ static constexpr DomType kindValue = type;
++ static constexpr DomKind domKindValue = DomKind::ScriptElement;
++
++ ScriptElementBase(QQmlJS::SourceLocation combinedLocation = QQmlJS::SourceLocation{})
++ : ScriptElement(), m_locations({ { FileLocationRegion::MainRegion, combinedLocation } })
++ {
++ }
++ ScriptElementBase(QQmlJS::SourceLocation first, QQmlJS::SourceLocation last)
++ : ScriptElementBase(combine(first, last))
++ {
++ }
++ DomType kind() const override { return type; }
++ DomKind domKind() const override { return domKindValue; }
++
++ void createFileLocations(const FileLocations::Tree &base) override
++ {
++ FileLocations::Tree res = FileLocations::ensure(base, pathFromOwner());
++ for (auto location: m_locations) {
++ FileLocations::addRegion(res, location.first, location.second);
++ }
++ }
++
++ /*
++ Pretty prints the current DomItem. Currently, for script elements, this is done entirely on
++ the parser representation (via the AST classes), but it could be moved here if needed.
++ */
++ // void writeOut(const DomItem &self, OutWriter &lw) const override;
++
++ /*!
++ All of the following overloads are only required for optimization purposes.
++ The base implementation will work fine, but might be slightly slower.
++ You can override dump(), fields(), field(), indexes(), index(), keys() or key() if the
++ performance of the base class becomes problematic.
++ */
++
++ // // needed for debug
++ // void dump(const DomItem &, const Sink &sink, int indent, FilterT filter) const override;
++
++ // // just required for optimization if iterateDirectSubpaths is slow
++ // QList<QString> fields(const DomItem &self) const override;
++ // DomItem field(const DomItem &self, QStringView name) const override;
++
++ // index_type indexes(const DomItem &self) const override;
++ // DomItem index(const DomItem &self, index_type index) const override;
++
++ // QSet<QString> const keys(const DomItem &self) const override;
++ // DomItem key(const DomItem &self, const QString &name) const override;
++
++ QQmlJS::SourceLocation mainRegionLocation() const
++ {
++ Q_ASSERT(m_locations.size() > 0);
++ Q_ASSERT(m_locations.front().first == FileLocationRegion::MainRegion);
++
++ auto current = m_locations.front();
++ return current.second;
++ }
++ void setMainRegionLocation(const QQmlJS::SourceLocation &location)
++ {
++ Q_ASSERT(m_locations.size() > 0);
++ Q_ASSERT(m_locations.front().first == FileLocationRegion::MainRegion);
++
++ m_locations.front().second = location;
++ }
++ void addLocation(FileLocationRegion region, QQmlJS::SourceLocation location)
++ {
++ Q_ASSERT_X(region != FileLocationRegion::MainRegion, "ScriptElementBase::addLocation",
++ "use the setCombinedLocation instead!");
++ m_locations.emplace_back(region, location);
++ }
++
++ protected:
++ std::vector<std::pair<FileLocationRegion, QQmlJS::SourceLocation>> m_locations;
++ };
++
++ class ScriptList : public ScriptElementBase<DomType::List>
++ {
++ public:
++ using typename ScriptElementBase<DomType::List>::BaseT;
++
++ using BaseT::BaseT;
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
++ {
++ bool cont =
++ asList(self.pathFromOwner().withKey(QString())).iterateDirectSubpaths(self, visitor);
++ return cont;
++ }
++ void updatePathFromOwner(const Path &p) override
++ {
++ BaseT::updatePathFromOwner(p);
++ for (int i = 0; i < m_list.size(); ++i) {
++ Q_ASSERT(m_list[i].base());
++ m_list[i].base()->updatePathFromOwner(p.withIndex(i));
++ }
++ }
++ void createFileLocations(const FileLocations::Tree &base) override
++ {
++ BaseT::createFileLocations(base);
++
++ for (int i = 0; i < m_list.size(); ++i) {
++ Q_ASSERT(m_list[i].base());
++ m_list[i].base()->createFileLocations(base);
++ }
++ }
++
++ List asList(const Path &path) const
++ {
++ auto asList = List::fromQList<ScriptElementVariant>(
++ path, m_list,
++ [](const DomItem &list, const PathEls::PathComponent &, const ScriptElementVariant &wrapped)
++ -> DomItem { return list.subScriptElementWrapperItem(wrapped); });
++
++ return asList;
++ }
++
++ void append(const ScriptElementVariant &statement) { m_list.push_back(statement); }
++ void append(const ScriptList &list) { m_list.append(list.m_list); }
++ void reverse() { std::reverse(m_list.begin(), m_list.end()); }
++ void replaceKindForGenericChildren(DomType oldType, DomType newType);
++ const QList<ScriptElementVariant> &qList() const { return std::as_const(m_list); };
++
++ private:
++ QList<ScriptElementVariant> m_list;
++ };
++
++ class GenericScriptElement : public ScriptElementBase<DomType::ScriptGenericElement>
++ {
++ public:
++ using BaseT::BaseT;
++ using VariantT = std::variant<ScriptElementVariant, ScriptList>;
++
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ DomType kind() const override { return m_kind; }
++ void setKind(DomType kind) { m_kind = kind; }
++
++ decltype(auto) insertChild(QStringView name, VariantT v)
++ {
++ return m_children.insert(std::make_pair(name, v));
++ }
++
++ ScriptElementVariant elementChild(const QQmlJS::Dom::FieldType &field)
++ {
++ auto it = m_children.find(field);
++ if (it == m_children.end())
++ return {};
++ if (!std::holds_alternative<ScriptElementVariant>(it->second))
++ return {};
++ return std::get<ScriptElementVariant>(it->second);
++ }
++
++ void insertValue(QStringView name, const QCborValue &v)
++ {
++ m_values.insert(std::make_pair(name, v));
++ }
++
++ QCborValue value() const override
++ {
++ auto it = m_values.find(Fields::value);
++ if (it == m_values.cend())
++ return {};
++
++ return it->second;
++ }
++
++ private:
++ /*!
++ \internal
++ The DomItem interface will use iterateDirectSubpaths for all kinds of operations on the
++ GenericScriptElement. Therefore, to avoid bad surprises when using the DomItem interface, use
++ a sorted map to always iterate the children in the same order.
++ */
++ std::map<QQmlJS::Dom::FieldType, VariantT> m_children;
++ // value fields
++ std::map<QQmlJS::Dom::FieldType, QCborValue> m_values;
++ DomType m_kind = DomType::Empty;
++ };
++
++ class BlockStatement : public ScriptElementBase<DomType::ScriptBlockStatement>
++ {
++ public:
++ using BaseT::BaseT;
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ ScriptList statements() const { return m_statements; }
++ void setStatements(const ScriptList &statements) { m_statements = statements; }
++
++ private:
++ ScriptList m_statements;
++ };
++
++ class IdentifierExpression : public ScriptElementBase<DomType::ScriptIdentifierExpression>
++ {
++ public:
++ using BaseT::BaseT;
++ void setName(QStringView name) { m_name = name.toString(); }
++ QString name() { return m_name; }
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++
++ QCborValue value() const override { return QCborValue(m_name); }
++
++ private:
++ QString m_name;
++ };
++
++ class Literal : public ScriptElementBase<DomType::ScriptLiteral>
++ {
++ public:
++ using BaseT::BaseT;
++
++ using VariantT = std::variant<QString, double, bool, std::nullptr_t>;
++
++ void setLiteralValue(VariantT value) { m_value = value; }
++ VariantT literalValue() const { return m_value; }
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++
++ QCborValue value() const override
++ {
++ return std::visit([](auto &&e) -> QCborValue { return e; }, m_value);
++ }
++
++ private:
++ VariantT m_value;
++ };
++
++ // TODO: test this method + implement foreach etc
++ class ForStatement : public ScriptElementBase<DomType::ScriptForStatement>
++ {
++ public:
++ using BaseT::BaseT;
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ ScriptElementVariant initializer() const { return m_initializer; }
++ void setInitializer(const ScriptElementVariant &newInitializer)
++ {
++ m_initializer = newInitializer;
++ }
++
++ ScriptElementVariant declarations() const { return m_declarations; }
++ void setDeclarations(const ScriptElementVariant &newDeclaration)
++ {
++ m_declarations = newDeclaration;
++ }
++ ScriptElementVariant condition() const { return m_condition; }
++ void setCondition(const ScriptElementVariant &newCondition) { m_condition = newCondition; }
++ ScriptElementVariant expression() const { return m_expression; }
++ void setExpression(const ScriptElementVariant &newExpression) { m_expression = newExpression; }
++ ScriptElementVariant body() const { return m_body; }
++ void setBody(const ScriptElementVariant &newBody) { m_body = newBody; }
++
++ private:
++ ScriptElementVariant m_initializer;
++ ScriptElementVariant m_declarations;
++ ScriptElementVariant m_condition;
++ ScriptElementVariant m_expression;
++ ScriptElementVariant m_body;
++ };
++
++ class IfStatement : public ScriptElementBase<DomType::ScriptIfStatement>
++ {
++ public:
++ using BaseT::BaseT;
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ ScriptElementVariant condition() const { return m_condition; }
++ void setCondition(const ScriptElementVariant &condition) { m_condition = condition; }
++ ScriptElementVariant consequence() { return m_consequence; }
++ void setConsequence(const ScriptElementVariant &consequence) { m_consequence = consequence; }
++ ScriptElementVariant alternative() { return m_alternative; }
++ void setAlternative(const ScriptElementVariant &alternative) { m_alternative = alternative; }
++
++ private:
++ ScriptElementVariant m_condition;
++ ScriptElementVariant m_consequence;
++ ScriptElementVariant m_alternative;
++ };
++
++ class ReturnStatement : public ScriptElementBase<DomType::ScriptReturnStatement>
++ {
++ public:
++ using BaseT::BaseT;
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ ScriptElementVariant expression() const { return m_expression; }
++ void setExpression(ScriptElementVariant expression) { m_expression = expression; }
++
++ private:
++ ScriptElementVariant m_expression;
++ };
++
++ class BinaryExpression : public ScriptElementBase<DomType::ScriptBinaryExpression>
++ {
++ public:
++ using BaseT::BaseT;
++
++ enum Operator : char {
++ FieldMemberAccess,
++ ArrayMemberAccess,
++ TO_BE_IMPLEMENTED = std::numeric_limits<char>::max(), // not required by qmlls
++ };
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ ScriptElementVariant left() const { return m_left; }
++ void setLeft(const ScriptElementVariant &newLeft) { m_left = newLeft; }
++ ScriptElementVariant right() const { return m_right; }
++ void setRight(const ScriptElementVariant &newRight) { m_right = newRight; }
++ int op() const { return m_operator; }
++ void setOp(Operator op) { m_operator = op; }
++
++ private:
++ ScriptElementVariant m_left;
++ ScriptElementVariant m_right;
++ Operator m_operator = TO_BE_IMPLEMENTED;
++ };
++
++ class VariableDeclarationEntry : public ScriptElementBase<DomType::ScriptVariableDeclarationEntry>
++ {
++ public:
++ using BaseT::BaseT;
++
++ enum ScopeType { Var, Let, Const };
++
++ ScopeType scopeType() const { return m_scopeType; }
++ void setScopeType(ScopeType scopeType) { m_scopeType = scopeType; }
++
++ ScriptElementVariant identifier() const { return m_identifier; }
++ void setIdentifier(const ScriptElementVariant &identifier) { m_identifier = identifier; }
++
++ ScriptElementVariant initializer() const { return m_initializer; }
++ void setInitializer(const ScriptElementVariant &initializer) { m_initializer = initializer; }
++
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ private:
++ ScopeType m_scopeType;
++ ScriptElementVariant m_identifier;
++ ScriptElementVariant m_initializer;
++ };
++
++ class VariableDeclaration : public ScriptElementBase<DomType::ScriptVariableDeclaration>
++ {
++ public:
++ using BaseT::BaseT;
++
++ // minimal required overload for this to be wrapped as DomItem:
++ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
++ void updatePathFromOwner(const Path &p) override;
++ void createFileLocations(const FileLocations::Tree &base) override;
++
++ void setDeclarations(const ScriptList &list) { m_declarations = list; }
++ ScriptList declarations() { return m_declarations; }
++
++ private:
++ ScriptList m_declarations;
++ };
++
++ } // namespace ScriptElements
++ } // end namespace Dom
++ } // end namespace QQmlJS
++
++ QT_END_NAMESPACE
++
++ #endif // QQMLDOMSCRIPTELEMENTS_P_H