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#include "qqmldom_utils_p.h"
5#include "qqmldomitem_p.h"
6#include "qqmldompath_p.h"
7#include "qqmldomscriptelements_p.h"
8#include <memory>
9#include <utility>
10#include <variant>
11
12using namespace QQmlJS::Dom::ScriptElements;
13using QQmlJS::Dom::DomType;
14using QQmlJS::Dom::ScriptElement;
15using QQmlJS::Dom::ScriptElementVariant;
16
17/*!
18 \internal
19 \class ScriptElementBase
20
21 The base class for all script elements.
22
23 Derived classes should implement createFileLocations, DomElement::updatePathFromOwner and
24 DomBase::iterateDirectSubpaths. Furthermore, they need their own DomType enum.
25
26 updatePathFromOwner and createFileLocations should be called on the script element root node
27 after it was constructed for the DomItem-wrapping to work correctly. Without it, methods like
28 iterateDirectSubpaths and all the stuff in DomItem will not work.
29
30 createFileLocations does not work without having the pathFromOwner set
31 first via updatePathFromOwner.
32
33 In derived classes, the updatePathFromOwner-implementation should call the base implementation
34 and also call recursively updatePathFromOwner on the derived class's children.
35
36 See \l ScriptElementBase::createFileLocations for the createFileLocations implementation in
37 derived classes.
38
39 Derived classes need to implement iterateDirectSubpaths to comply with the DomItem interface.
40*/
41
42/*!
43 \internal
44 \fn ScriptElementBase::createFileLocations
45
46 Usually, all the visits/recursive calls to DOM elements can be done using the DomItem interface,
47 once all the DOM has been constructed.
48
49 During construction, createFileLocations can be used to annotate the DOM representation with the
50 corresponding source locations, which are needed, e.g., to find the corresponding DOM element
51 from a certain text position. When called, createFileLocations sets an entry for itself in the
52 FileLocationsTree.
53
54 Derived classes should call the base implemenatation and recursively call createFileLocations on
55 all their children.
56
57 Usually, only the root of the script DOM element requires one createFileLocations call after
58 construction \b{and} after a pathFromOwner was set using updatePathFromOwner.
59
60*/
61
62/*!
63 \internal
64 \class ScriptList
65
66 A Helper class for writing script elements that contain lists, helps for implementing the
67 recursive calls of iterateDirectSubpaths, updatePathFromOwner and createFileLocations.
68*/
69
70/*!
71 \internal
72 Helper for fields with elements in iterateDirectSubpaths.
73 */
74static bool wrap(QQmlJS::Dom::DomItem &self, QQmlJS::Dom::DirectVisitor visitor, QStringView field,
75 const ScriptElementVariant &value)
76{
77 if (!value)
78 return true;
79
80 const bool b =
81 self.dvItemField(visitor, f: field, it: [&self, field, &value]() -> QQmlJS::Dom::DomItem {
82 const QQmlJS::Dom::Path pathFromOwner{ self.pathFromOwner().field(name: field) };
83 return self.subScriptElementWrapperItem(obj: value);
84 });
85 return b;
86}
87
88/*!
89 \internal
90 Helper for fields with lists in iterateDirectSubpaths.
91 */
92static bool wrap(QQmlJS::Dom::DomItem &self, QQmlJS::Dom::DirectVisitor visitor, QStringView field,
93 const ScriptList &value)
94{
95 const bool b =
96 self.dvItemField(visitor, f: field, it: [&self, field, &value]() -> QQmlJS::Dom::DomItem {
97 const QQmlJS::Dom::Path pathFromOwner{ self.pathFromOwner().field(name: field) };
98 return self.subListItem(list: value.asList(path: pathFromOwner));
99 });
100 return b;
101}
102
103bool GenericScriptElement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
104{
105 bool cont = true;
106 for (auto it = m_children.begin(); it != m_children.end(); ++it) {
107 cont &= std::visit(
108 visitor: [&self, &visitor, &it](auto &&e) { return wrap(self, visitor, it->first, e); },
109 variants&: it->second);
110 }
111 return cont;
112}
113
114void GenericScriptElement::updatePathFromOwner(Path p)
115{
116 BaseT::updatePathFromOwner(newPath: p);
117 for (auto it = m_children.begin(); it != m_children.end(); ++it) {
118 std::visit(visitor: qOverloadedVisitor{ [&p, &it](ScriptElementVariant &e) {
119 e.base()->updatePathFromOwner(newPath: p.field(name: it->first));
120 },
121 [&p, &it](ScriptList &list) {
122 list.updatePathFromOwner(p: p.field(name: it->first));
123 } },
124 variants&: it->second);
125 }
126}
127
128void GenericScriptElement::createFileLocations(FileLocations::Tree base)
129{
130 BaseT::createFileLocations(base);
131 for (auto it = m_children.begin(); it != m_children.end(); ++it) {
132 std::visit(
133 visitor: qOverloadedVisitor{
134 [&base](ScriptElementVariant &e) { e.base()->createFileLocations(fileLocationOfOwner: base); },
135 [&base](ScriptList &list) { list.createFileLocations(base); } },
136 variants&: it->second);
137 }
138}
139
140bool BlockStatement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
141{
142 // TODO: test me
143 bool cont = true;
144 cont &= wrap(self, visitor, field: Fields::statements, value: m_statements);
145 return cont;
146}
147
148void BlockStatement::updatePathFromOwner(Path p)
149{
150 BaseT::updatePathFromOwner(newPath: p);
151 m_statements.updatePathFromOwner(p: p.field(name: Fields::statements));
152}
153
154void BlockStatement::createFileLocations(FileLocations::Tree base)
155{
156 BaseT::createFileLocations(base);
157 m_statements.createFileLocations(base);
158}
159
160bool IdentifierExpression::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
161{
162 bool cont = true;
163 cont &= self.dvValueField(visitor, f: Fields::identifier, value: m_name);
164 return cont;
165}
166
167bool Literal::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
168{
169 bool cont = true;
170 std::visit(visitor: [&cont, &visitor,
171 &self](auto &&e) { cont &= self.dvValueField(visitor, Fields::value, e); },
172 variants&: m_value);
173 return cont;
174}
175
176bool IfStatement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
177{
178 // TODO: test me
179 bool cont = true;
180 cont &= wrap(self, visitor, field: Fields::condition, value: m_condition);
181 cont &= wrap(self, visitor, field: Fields::consequence, value: m_consequence);
182 cont &= wrap(self, visitor, field: Fields::alternative, value: m_alternative);
183 return cont;
184}
185
186void IfStatement::updatePathFromOwner(Path p)
187{
188 BaseT::updatePathFromOwner(newPath: p);
189 if (auto ptr = m_condition.base())
190 ptr->updatePathFromOwner(newPath: p.field(name: Fields::condition));
191 if (auto ptr = m_consequence.base())
192 ptr->updatePathFromOwner(newPath: p.field(name: Fields::consequence));
193 if (auto ptr = m_alternative.base())
194 ptr->updatePathFromOwner(newPath: p.field(name: Fields::alternative));
195}
196
197void IfStatement::createFileLocations(FileLocations::Tree base)
198{
199 BaseT::createFileLocations(base);
200 if (auto ptr = m_condition.base())
201 ptr->createFileLocations(fileLocationOfOwner: base);
202 if (auto ptr = m_consequence.base())
203 ptr->createFileLocations(fileLocationOfOwner: base);
204 if (auto ptr = m_alternative.base())
205 ptr->createFileLocations(fileLocationOfOwner: base);
206}
207
208bool ForStatement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
209{
210 bool cont = true;
211 cont &= wrap(self, visitor, field: Fields::initializer, value: m_initializer);
212 cont &= wrap(self, visitor, field: Fields::declarations, value: m_declarations);
213 cont &= wrap(self, visitor, field: Fields::condition, value: m_condition);
214 cont &= wrap(self, visitor, field: Fields::expression, value: m_expression);
215 cont &= wrap(self, visitor, field: Fields::body, value: m_body);
216 return cont;
217}
218
219void ForStatement::updatePathFromOwner(Path p)
220{
221 BaseT::updatePathFromOwner(newPath: p);
222 if (auto ptr = m_initializer.base())
223 ptr->updatePathFromOwner(newPath: p.field(name: Fields::initializer));
224 if (auto ptr = m_declarations.base())
225 ptr->updatePathFromOwner(newPath: p.field(name: Fields::declarations));
226 if (auto ptr = m_condition.base())
227 ptr->updatePathFromOwner(newPath: p.field(name: Fields::condition));
228 if (auto ptr = m_expression.base())
229 ptr->updatePathFromOwner(newPath: p.field(name: Fields::expression));
230 if (auto ptr = m_body.base())
231 ptr->updatePathFromOwner(newPath: p.field(name: Fields::body));
232}
233
234void ForStatement::createFileLocations(FileLocations::Tree base)
235{
236 BaseT::createFileLocations(base);
237 if (auto ptr = m_initializer.base())
238 ptr->createFileLocations(fileLocationOfOwner: base);
239 if (auto ptr = m_declarations.base())
240 ptr->createFileLocations(fileLocationOfOwner: base);
241 if (auto ptr = m_condition.base())
242 ptr->createFileLocations(fileLocationOfOwner: base);
243 if (auto ptr = m_expression.base())
244 ptr->createFileLocations(fileLocationOfOwner: base);
245 if (auto ptr = m_body.base())
246 ptr->createFileLocations(fileLocationOfOwner: base);
247}
248
249bool BinaryExpression::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
250{
251 bool cont = true;
252 cont &= wrap(self, visitor, field: Fields::left, value: m_left);
253 cont &= self.dvValueField(visitor, f: Fields::operation, value: m_operator);
254 cont &= wrap(self, visitor, field: Fields::right, value: m_right);
255 return cont;
256}
257
258void BinaryExpression::updatePathFromOwner(Path p)
259{
260 BaseT::updatePathFromOwner(newPath: p);
261 if (auto ptr = m_left.base())
262 ptr->updatePathFromOwner(newPath: p.field(name: Fields::left));
263 if (auto ptr = m_right.base())
264 ptr->updatePathFromOwner(newPath: p.field(name: Fields::right));
265}
266
267void BinaryExpression::createFileLocations(FileLocations::Tree base)
268{
269 BaseT::createFileLocations(base);
270 if (auto ptr = m_left.base())
271 ptr->createFileLocations(fileLocationOfOwner: base);
272 if (auto ptr = m_right.base())
273 ptr->createFileLocations(fileLocationOfOwner: base);
274}
275
276bool VariableDeclarationEntry::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
277{
278 bool cont = true;
279 cont &= self.dvValueField(visitor, f: Fields::scopeType, value: m_scopeType);
280 cont &= wrap(self, visitor, field: Fields::identifier, value: m_identifier);
281 cont &= wrap(self, visitor, field: Fields::initializer, value: m_initializer);
282 return cont;
283}
284
285void VariableDeclarationEntry::updatePathFromOwner(Path p)
286{
287 BaseT::updatePathFromOwner(newPath: p);
288 if (auto ptr = m_identifier.base())
289 ptr->updatePathFromOwner(newPath: p.field(name: Fields::identifier));
290 if (auto ptr = m_initializer.base())
291 ptr->updatePathFromOwner(newPath: p.field(name: Fields::initializer));
292}
293
294void VariableDeclarationEntry::createFileLocations(FileLocations::Tree base)
295{
296 BaseT::createFileLocations(base);
297 if (auto ptr = m_identifier.base())
298 ptr->createFileLocations(fileLocationOfOwner: base);
299 if (auto ptr = m_initializer.base())
300 ptr->createFileLocations(fileLocationOfOwner: base);
301}
302
303bool VariableDeclaration::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
304{
305 bool cont = true;
306 cont &= wrap(self, visitor, field: Fields::declarations, value: m_declarations);
307 return cont;
308}
309
310void VariableDeclaration::updatePathFromOwner(Path p)
311{
312 BaseT::updatePathFromOwner(newPath: p);
313 m_declarations.updatePathFromOwner(p: p.field(name: Fields::declarations));
314}
315
316void VariableDeclaration::createFileLocations(FileLocations::Tree base)
317{
318 BaseT::createFileLocations(base);
319 m_declarations.createFileLocations(base);
320}
321
322bool ReturnStatement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
323{
324 bool cont = true;
325 cont &= wrap(self, visitor, field: Fields::expression, value: m_expression);
326 return cont;
327}
328
329void ReturnStatement::updatePathFromOwner(Path p)
330{
331 BaseT::updatePathFromOwner(newPath: p);
332 m_expression.base()->updatePathFromOwner(newPath: p.field(name: Fields::expression));
333}
334
335void ReturnStatement::createFileLocations(FileLocations::Tree base)
336{
337 BaseT::createFileLocations(base);
338 m_expression.base()->createFileLocations(fileLocationOfOwner: base);
339}
340
341void ScriptList::replaceKindForGenericChildren(DomType oldType, DomType newType)
342{
343 for (auto &it : m_list) {
344 if (auto current = it.data()) {
345 if (auto genericElement =
346 std::get_if<std::shared_ptr<ScriptElements::GenericScriptElement>>(
347 ptr: &*current)) {
348 if ((*genericElement)->kind() == oldType)
349 (*genericElement)->setKind(newType);
350 }
351 }
352 }
353}
354

source code of qtdeclarative/src/qmldom/qqmldomscriptelements.cpp