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