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 | |
12 | using namespace QQmlJS::Dom::ScriptElements; |
13 | using QQmlJS::Dom::DomType; |
14 | using QQmlJS::Dom::ScriptElement; |
15 | using 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 | */ |
74 | static 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 | */ |
92 | static 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 | |
103 | bool 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 | |
114 | void 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 | |
128 | void 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 | |
140 | bool 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 | |
148 | void BlockStatement::updatePathFromOwner(Path p) |
149 | { |
150 | BaseT::updatePathFromOwner(newPath: p); |
151 | m_statements.updatePathFromOwner(p: p.field(name: Fields::statements)); |
152 | } |
153 | |
154 | void BlockStatement::createFileLocations(FileLocations::Tree base) |
155 | { |
156 | BaseT::createFileLocations(base); |
157 | m_statements.createFileLocations(base); |
158 | } |
159 | |
160 | bool 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 | |
167 | bool 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 | |
176 | bool 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 | |
186 | void 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 | |
197 | void 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 | |
208 | bool 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 | |
219 | void 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 | |
234 | void 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 | |
249 | bool 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 | |
258 | void 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 | |
267 | void 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 | |
276 | bool 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 | |
285 | void 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 | |
294 | void 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 | |
303 | bool 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 | |
310 | void VariableDeclaration::updatePathFromOwner(Path p) |
311 | { |
312 | BaseT::updatePathFromOwner(newPath: p); |
313 | m_declarations.updatePathFromOwner(p: p.field(name: Fields::declarations)); |
314 | } |
315 | |
316 | void VariableDeclaration::createFileLocations(FileLocations::Tree base) |
317 | { |
318 | BaseT::createFileLocations(base); |
319 | m_declarations.createFileLocations(base); |
320 | } |
321 | |
322 | bool 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 | |
329 | void ReturnStatement::updatePathFromOwner(Path p) |
330 | { |
331 | BaseT::updatePathFromOwner(newPath: p); |
332 | m_expression.base()->updatePathFromOwner(newPath: p.field(name: Fields::expression)); |
333 | } |
334 | |
335 | void ReturnStatement::createFileLocations(FileLocations::Tree base) |
336 | { |
337 | BaseT::createFileLocations(base); |
338 | m_expression.base()->createFileLocations(fileLocationOfOwner: base); |
339 | } |
340 | |
341 | void 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 | |