1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQMLDOMSCRIPTELEMENTS_P_H
5#define QQMLDOMSCRIPTELEMENTS_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qqmldomitem_p.h"
19#include "qqmldomelements_p.h"
20#include "qqmldomfilelocations_p.h"
21#include "qqmldompath_p.h"
22#include <algorithm>
23#include <limits>
24#include <type_traits>
25#include <utility>
26#include <variant>
27
28QT_BEGIN_NAMESPACE
29
30namespace QQmlJS {
31namespace Dom {
32
33namespace ScriptElements {
34
35template<DomType type>
36class ScriptElementBase : public ScriptElement
37{
38public:
39 using BaseT = ScriptElementBase<type>;
40 static constexpr DomType kindValue = type;
41 static constexpr DomKind domKindValue = DomKind::ScriptElement;
42
43 ScriptElementBase(QQmlJS::SourceLocation combinedLocation = QQmlJS::SourceLocation{})
44 : ScriptElement(), m_locations({ { FileLocationRegion::MainRegion, combinedLocation } })
45 {
46 }
47 ScriptElementBase(QQmlJS::SourceLocation first, QQmlJS::SourceLocation last)
48 : ScriptElementBase(combine(l1: first, l2: last))
49 {
50 }
51 DomType kind() const override { return type; }
52 DomKind domKind() const override { return domKindValue; }
53
54 void createFileLocations(const FileLocations::Tree &base) override
55 {
56 FileLocations::Tree res = FileLocations::ensure(base, basePath: pathFromOwner());
57 for (auto location: m_locations) {
58 FileLocations::addRegion(fLoc: res, region: location.first, loc: location.second);
59 }
60 }
61
62 /*
63 Pretty prints the current DomItem. Currently, for script elements, this is done entirely on
64 the parser representation (via the AST classes), but it could be moved here if needed.
65 */
66 // void writeOut(const DomItem &self, OutWriter &lw) const override;
67
68 /*!
69 All of the following overloads are only required for optimization purposes.
70 The base implementation will work fine, but might be slightly slower.
71 You can override dump(), fields(), field(), indexes(), index(), keys() or key() if the
72 performance of the base class becomes problematic.
73 */
74
75 // // needed for debug
76 // void dump(const DomItem &, const Sink &sink, int indent, FilterT filter) const override;
77
78 // // just required for optimization if iterateDirectSubpaths is slow
79 // QList<QString> fields(const DomItem &self) const override;
80 // DomItem field(const DomItem &self, QStringView name) const override;
81
82 // index_type indexes(const DomItem &self) const override;
83 // DomItem index(const DomItem &self, index_type index) const override;
84
85 // QSet<QString> const keys(const DomItem &self) const override;
86 // DomItem key(const DomItem &self, const QString &name) const override;
87
88 QQmlJS::SourceLocation mainRegionLocation() const
89 {
90 Q_ASSERT(m_locations.size() > 0);
91 Q_ASSERT(m_locations.front().first == FileLocationRegion::MainRegion);
92
93 auto current = m_locations.front();
94 return current.second;
95 }
96 void setMainRegionLocation(const QQmlJS::SourceLocation &location)
97 {
98 Q_ASSERT(m_locations.size() > 0);
99 Q_ASSERT(m_locations.front().first == FileLocationRegion::MainRegion);
100
101 m_locations.front().second = location;
102 }
103 void addLocation(FileLocationRegion region, QQmlJS::SourceLocation location)
104 {
105 Q_ASSERT_X(region != FileLocationRegion::MainRegion, "ScriptElementBase::addLocation",
106 "use the setCombinedLocation instead!");
107 m_locations.emplace_back(args&: region, args&: location);
108 }
109
110protected:
111 std::vector<std::pair<FileLocationRegion, QQmlJS::SourceLocation>> m_locations;
112};
113
114class ScriptList : public ScriptElementBase<DomType::List>
115{
116public:
117 using typename ScriptElementBase<DomType::List>::BaseT;
118
119 using BaseT::BaseT;
120
121 // minimal required overload for this to be wrapped as DomItem:
122 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
123 {
124 bool cont =
125 asList(path: self.pathFromOwner().withKey(name: QString())).iterateDirectSubpaths(self, visitor);
126 return cont;
127 }
128 void updatePathFromOwner(const Path &p) override
129 {
130 BaseT::updatePathFromOwner(newPath: p);
131 for (int i = 0; i < m_list.size(); ++i) {
132 Q_ASSERT(m_list[i].base());
133 m_list[i].base()->updatePathFromOwner(newPath: p.withIndex(i));
134 }
135 }
136 void createFileLocations(const FileLocations::Tree &base) override
137 {
138 BaseT::createFileLocations(base);
139
140 for (int i = 0; i < m_list.size(); ++i) {
141 Q_ASSERT(m_list[i].base());
142 m_list[i].base()->createFileLocations(fileLocationOfOwner: base);
143 }
144 }
145
146 List asList(const Path &path) const
147 {
148 auto asList = List::fromQList<ScriptElementVariant>(
149 pathFromOwner: path, list: m_list,
150 elWrapper: [](const DomItem &list, const PathEls::PathComponent &, const ScriptElementVariant &wrapped)
151 -> DomItem { return list.subScriptElementWrapperItem(obj: wrapped); });
152
153 return asList;
154 }
155
156 void append(const ScriptElementVariant &statement) { m_list.push_back(t: statement); }
157 void append(const ScriptList &list) { m_list.append(l: list.m_list); }
158 void reverse() { std::reverse(first: m_list.begin(), last: m_list.end()); }
159 void replaceKindForGenericChildren(DomType oldType, DomType newType);
160 const QList<ScriptElementVariant> &qList() const { return std::as_const(t: m_list); };
161
162private:
163 QList<ScriptElementVariant> m_list;
164};
165
166class GenericScriptElement : public ScriptElementBase<DomType::ScriptGenericElement>
167{
168public:
169 using BaseT::BaseT;
170 using VariantT = std::variant<ScriptElementVariant, ScriptList>;
171
172 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
173 void updatePathFromOwner(const Path &p) override;
174 void createFileLocations(const FileLocations::Tree &base) override;
175
176 DomType kind() const override { return m_kind; }
177 void setKind(DomType kind) { m_kind = kind; }
178
179 decltype(auto) insertChild(QStringView name, VariantT v)
180 {
181 return m_children.insert(x: std::make_pair(x&: name, y&: v));
182 }
183
184 ScriptElementVariant elementChild(const QQmlJS::Dom::FieldType &field)
185 {
186 auto it = m_children.find(x: field);
187 if (it == m_children.end())
188 return {};
189 if (!std::holds_alternative<ScriptElementVariant>(v: it->second))
190 return {};
191 return std::get<ScriptElementVariant>(v&: it->second);
192 }
193
194 void insertValue(QStringView name, const QCborValue &v)
195 {
196 m_values.insert(x: std::make_pair(x&: name, y: v));
197 }
198
199 QCborValue value() const override
200 {
201 auto it = m_values.find(x: Fields::value);
202 if (it == m_values.cend())
203 return {};
204
205 return it->second;
206 }
207
208private:
209 /*!
210 \internal
211 The DomItem interface will use iterateDirectSubpaths for all kinds of operations on the
212 GenericScriptElement. Therefore, to avoid bad surprises when using the DomItem interface, use
213 a sorted map to always iterate the children in the same order.
214 */
215 std::map<QQmlJS::Dom::FieldType, VariantT> m_children;
216 // value fields
217 std::map<QQmlJS::Dom::FieldType, QCborValue> m_values;
218 DomType m_kind = DomType::Empty;
219};
220
221class BlockStatement : public ScriptElementBase<DomType::ScriptBlockStatement>
222{
223public:
224 using BaseT::BaseT;
225
226 // minimal required overload for this to be wrapped as DomItem:
227 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
228 void updatePathFromOwner(const Path &p) override;
229 void createFileLocations(const FileLocations::Tree &base) override;
230
231 ScriptList statements() const { return m_statements; }
232 void setStatements(const ScriptList &statements) { m_statements = statements; }
233
234private:
235 ScriptList m_statements;
236};
237
238class IdentifierExpression : public ScriptElementBase<DomType::ScriptIdentifierExpression>
239{
240public:
241 using BaseT::BaseT;
242 void setName(QStringView name) { m_name = name.toString(); }
243 QString name() { return m_name; }
244
245 // minimal required overload for this to be wrapped as DomItem:
246 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
247
248 QCborValue value() const override { return QCborValue(m_name); }
249
250private:
251 QString m_name;
252};
253
254class Literal : public ScriptElementBase<DomType::ScriptLiteral>
255{
256public:
257 using BaseT::BaseT;
258
259 using VariantT = std::variant<QString, double, bool, std::nullptr_t>;
260
261 void setLiteralValue(VariantT value) { m_value = value; }
262 VariantT literalValue() const { return m_value; }
263
264 // minimal required overload for this to be wrapped as DomItem:
265 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
266
267 QCborValue value() const override
268 {
269 return std::visit(visitor: [](auto &&e) -> QCborValue { return e; }, variants: m_value);
270 }
271
272private:
273 VariantT m_value;
274};
275
276// TODO: test this method + implement foreach etc
277class ForStatement : public ScriptElementBase<DomType::ScriptForStatement>
278{
279public:
280 using BaseT::BaseT;
281
282 // minimal required overload for this to be wrapped as DomItem:
283 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
284 void updatePathFromOwner(const Path &p) override;
285 void createFileLocations(const FileLocations::Tree &base) override;
286
287 ScriptElementVariant initializer() const { return m_initializer; }
288 void setInitializer(const ScriptElementVariant &newInitializer)
289 {
290 m_initializer = newInitializer;
291 }
292
293 ScriptElementVariant declarations() const { return m_declarations; }
294 void setDeclarations(const ScriptElementVariant &newDeclaration)
295 {
296 m_declarations = newDeclaration;
297 }
298 ScriptElementVariant condition() const { return m_condition; }
299 void setCondition(const ScriptElementVariant &newCondition) { m_condition = newCondition; }
300 ScriptElementVariant expression() const { return m_expression; }
301 void setExpression(const ScriptElementVariant &newExpression) { m_expression = newExpression; }
302 ScriptElementVariant body() const { return m_body; }
303 void setBody(const ScriptElementVariant &newBody) { m_body = newBody; }
304
305private:
306 ScriptElementVariant m_initializer;
307 ScriptElementVariant m_declarations;
308 ScriptElementVariant m_condition;
309 ScriptElementVariant m_expression;
310 ScriptElementVariant m_body;
311};
312
313class IfStatement : public ScriptElementBase<DomType::ScriptIfStatement>
314{
315public:
316 using BaseT::BaseT;
317
318 // minimal required overload for this to be wrapped as DomItem:
319 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
320 void updatePathFromOwner(const Path &p) override;
321 void createFileLocations(const FileLocations::Tree &base) override;
322
323 ScriptElementVariant condition() const { return m_condition; }
324 void setCondition(const ScriptElementVariant &condition) { m_condition = condition; }
325 ScriptElementVariant consequence() { return m_consequence; }
326 void setConsequence(const ScriptElementVariant &consequence) { m_consequence = consequence; }
327 ScriptElementVariant alternative() { return m_alternative; }
328 void setAlternative(const ScriptElementVariant &alternative) { m_alternative = alternative; }
329
330private:
331 ScriptElementVariant m_condition;
332 ScriptElementVariant m_consequence;
333 ScriptElementVariant m_alternative;
334};
335
336class ReturnStatement : public ScriptElementBase<DomType::ScriptReturnStatement>
337{
338public:
339 using BaseT::BaseT;
340
341 // minimal required overload for this to be wrapped as DomItem:
342 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
343 void updatePathFromOwner(const Path &p) override;
344 void createFileLocations(const FileLocations::Tree &base) override;
345
346 ScriptElementVariant expression() const { return m_expression; }
347 void setExpression(ScriptElementVariant expression) { m_expression = expression; }
348
349private:
350 ScriptElementVariant m_expression;
351};
352
353class BinaryExpression : public ScriptElementBase<DomType::ScriptBinaryExpression>
354{
355public:
356 using BaseT::BaseT;
357
358 enum Operator : char {
359 FieldMemberAccess,
360 ArrayMemberAccess,
361 TO_BE_IMPLEMENTED = std::numeric_limits<char>::max(), // not required by qmlls
362 };
363
364 // minimal required overload for this to be wrapped as DomItem:
365 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
366 void updatePathFromOwner(const Path &p) override;
367 void createFileLocations(const FileLocations::Tree &base) override;
368
369 ScriptElementVariant left() const { return m_left; }
370 void setLeft(const ScriptElementVariant &newLeft) { m_left = newLeft; }
371 ScriptElementVariant right() const { return m_right; }
372 void setRight(const ScriptElementVariant &newRight) { m_right = newRight; }
373 int op() const { return m_operator; }
374 void setOp(Operator op) { m_operator = op; }
375
376private:
377 ScriptElementVariant m_left;
378 ScriptElementVariant m_right;
379 Operator m_operator = TO_BE_IMPLEMENTED;
380};
381
382class VariableDeclarationEntry : public ScriptElementBase<DomType::ScriptVariableDeclarationEntry>
383{
384public:
385 using BaseT::BaseT;
386
387 enum ScopeType { Var, Let, Const };
388
389 ScopeType scopeType() const { return m_scopeType; }
390 void setScopeType(ScopeType scopeType) { m_scopeType = scopeType; }
391
392 ScriptElementVariant identifier() const { return m_identifier; }
393 void setIdentifier(const ScriptElementVariant &identifier) { m_identifier = identifier; }
394
395 ScriptElementVariant initializer() const { return m_initializer; }
396 void setInitializer(const ScriptElementVariant &initializer) { m_initializer = initializer; }
397
398 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
399 void updatePathFromOwner(const Path &p) override;
400 void createFileLocations(const FileLocations::Tree &base) override;
401
402private:
403 ScopeType m_scopeType;
404 ScriptElementVariant m_identifier;
405 ScriptElementVariant m_initializer;
406};
407
408class VariableDeclaration : public ScriptElementBase<DomType::ScriptVariableDeclaration>
409{
410public:
411 using BaseT::BaseT;
412
413 // minimal required overload for this to be wrapped as DomItem:
414 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
415 void updatePathFromOwner(const Path &p) override;
416 void createFileLocations(const FileLocations::Tree &base) override;
417
418 void setDeclarations(const ScriptList &list) { m_declarations = list; }
419 ScriptList declarations() { return m_declarations; }
420
421private:
422 ScriptList m_declarations;
423};
424
425} // namespace ScriptElements
426} // end namespace Dom
427} // end namespace QQmlJS
428
429QT_END_NAMESPACE
430
431#endif // QQMLDOMSCRIPTELEMENTS_P_H
432

source code of qtdeclarative/src/qmldom/qqmldomscriptelements_p.h