1 | // Copyright (C) 2021 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 QQMLDOMASTCREATOR_P_H |
5 | #define QQMLDOMASTCREATOR_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 "qqmldomelements_p.h" |
19 | #include "qqmldomitem_p.h" |
20 | #include "qqmldompath_p.h" |
21 | #include "qqmldomscriptelements_p.h" |
22 | |
23 | #include <QtQmlCompiler/private/qqmljsimportvisitor_p.h> |
24 | |
25 | #include <QtQml/private/qqmljsastvisitor_p.h> |
26 | #include <memory> |
27 | #include <type_traits> |
28 | #include <variant> |
29 | |
30 | QT_BEGIN_NAMESPACE |
31 | |
32 | namespace QQmlJS { |
33 | namespace Dom { |
34 | |
35 | class QQmlDomAstCreator final : public AST::Visitor |
36 | { |
37 | Q_DECLARE_TR_FUNCTIONS(QQmlDomAstCreator) |
38 | using AST::Visitor::endVisit; |
39 | using AST::Visitor::visit; |
40 | |
41 | static constexpr const auto className = "QmlDomAstCreator"; |
42 | |
43 | class DomValue |
44 | { |
45 | public: |
46 | template<typename T> |
47 | DomValue(const T &obj) : kind(T::kindValue), value(obj) |
48 | { |
49 | } |
50 | DomType kind; |
51 | std::variant<QmlObject, MethodInfo, QmlComponent, PropertyDefinition, Binding, EnumDecl, |
52 | EnumItem, ConstantData, Id> |
53 | value; |
54 | }; |
55 | |
56 | class QmlStackElement |
57 | { |
58 | public: |
59 | Path path; |
60 | DomValue item; |
61 | FileLocations::Tree fileLocations; |
62 | }; |
63 | |
64 | /*! |
65 | \internal |
66 | Contains a ScriptElementVariant, that can be used everywhere in the DOM representation, or a |
67 | List that should always be inside of something else, e.g., that cannot be the root of the |
68 | script element DOM representation. |
69 | |
70 | Also, it makes sure you do not mistreat a list as a regular script element and vice versa. |
71 | |
72 | The reason for this is that Lists can get pretty unintuitive, as a List could be a Block of |
73 | statements or a list of variable declarations (let i = 3, j = 4, ...) or something completely |
74 | different. Instead, always put lists inside named construct (BlockStatement, |
75 | VariableDeclaration, ...). |
76 | */ |
77 | class ScriptStackElement |
78 | { |
79 | public: |
80 | template<typename T> |
81 | static ScriptStackElement from(const T &obj) |
82 | { |
83 | if constexpr (std::is_same_v<T, ScriptElements::ScriptList>) { |
84 | ScriptStackElement s{ ScriptElements::ScriptList::kindValue, obj }; |
85 | return s; |
86 | } else { |
87 | ScriptStackElement s{ obj->kind(), ScriptElementVariant::fromElement(obj) }; |
88 | return s; |
89 | } |
90 | Q_UNREACHABLE(); |
91 | } |
92 | |
93 | DomType kind; |
94 | using Variant = std::variant<ScriptElementVariant, ScriptElements::ScriptList>; |
95 | Variant value; |
96 | |
97 | ScriptElementVariant takeVariant() |
98 | { |
99 | Q_ASSERT_X(std::holds_alternative<ScriptElementVariant>(value), "takeVariant", |
100 | "Should be a variant, did the parser change?"); |
101 | return std::get<ScriptElementVariant>(v: std::move(value)); |
102 | } |
103 | |
104 | bool isList() const { return std::holds_alternative<ScriptElements::ScriptList>(v: value); }; |
105 | |
106 | ScriptElements::ScriptList takeList() |
107 | { |
108 | Q_ASSERT_X(std::holds_alternative<ScriptElements::ScriptList>(value), "takeList", |
109 | "Should be a List, did the parser change?"); |
110 | return std::get<ScriptElements::ScriptList>(v: std::move(value)); |
111 | } |
112 | |
113 | void setSemanticScope(const QQmlJSScope::ConstPtr &scope) |
114 | { |
115 | if (auto x = std::get_if<ScriptElementVariant>(ptr: &value)) { |
116 | x->base()->setSemanticScope(scope); |
117 | return; |
118 | } else if (auto x = std::get_if<ScriptElements::ScriptList>(ptr: &value)) { |
119 | x->setSemanticScope(scope); |
120 | return; |
121 | } |
122 | Q_UNREACHABLE(); |
123 | } |
124 | }; |
125 | |
126 | public: |
127 | void enableScriptExpressions(bool enable = true) { m_enableScriptExpressions = enable; } |
128 | void enableLoadFileLazily(bool enable = true) { m_loadFileLazily = enable; } |
129 | |
130 | private: |
131 | |
132 | MutableDomItem qmlFile; |
133 | std::shared_ptr<QmlFile> qmlFilePtr; |
134 | QVector<QmlStackElement> nodeStack; |
135 | QList<ScriptStackElement> scriptNodeStack; |
136 | QVector<int> arrayBindingLevels; |
137 | FileLocations::Tree rootMap; |
138 | int m_nestedFunctionDepth = 0; |
139 | bool m_enableScriptExpressions = false; |
140 | bool m_loadFileLazily = false; |
141 | |
142 | // A Binding inside a UiPublicMember (= a Property definition) will shadow the |
143 | // propertydefinition's binding identifiers with its own binding identifiers. Therefore, disable |
144 | // bindingIdentifiers for the Binding inside a Property definition by using this flag. |
145 | bool m_skipBindingIdentifiers = false; |
146 | |
147 | void setBindingIdentifiers(const Path &pathFromOwner, const AST::UiQualifiedId *identifiers, |
148 | Binding *bindingPtr); |
149 | template<typename T> |
150 | QmlStackElement ¤tEl(int idx = 0) |
151 | { |
152 | Q_ASSERT_X(idx < nodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl", |
153 | "Stack does not contain enough elements!"); |
154 | int i = nodeStack.size() - idx; |
155 | while (i-- > 0) { |
156 | DomType k = nodeStack.at(i).item.kind; |
157 | if (k == T::kindValue) |
158 | return nodeStack[i]; |
159 | } |
160 | Q_ASSERT_X(false, "currentEl", "Stack does not contan object of type "); |
161 | return nodeStack.last(); |
162 | } |
163 | |
164 | template<typename T> |
165 | ScriptStackElement ¤tScriptEl(int idx = 0) |
166 | { |
167 | Q_ASSERT_X(m_enableScriptExpressions, "currentScriptEl", |
168 | "Cannot access script elements when they are disabled!"); |
169 | |
170 | Q_ASSERT_X(idx < scriptNodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl", |
171 | "Stack does not contain enough elements!"); |
172 | int i = scriptNodeStack.size() - idx; |
173 | while (i-- > 0) { |
174 | DomType k = scriptNodeStack.at(i).kind; |
175 | if (k == T::element_type::kindValue) |
176 | return scriptNodeStack[i]; |
177 | } |
178 | Q_ASSERT_X(false, "currentEl", "Stack does not contain object of type "); |
179 | return scriptNodeStack.last(); |
180 | } |
181 | |
182 | template<typename T> |
183 | T ¤t(int idx = 0) |
184 | { |
185 | return std::get<T>(currentEl<T>(idx).item.value); |
186 | } |
187 | |
188 | index_type currentIndex() { return currentNodeEl().path.last().headIndex(); } |
189 | |
190 | QmlStackElement ¤tQmlObjectOrComponentEl(int idx = 0); |
191 | |
192 | QmlStackElement ¤tNodeEl(int i = 0); |
193 | ScriptStackElement ¤tScriptNodeEl(int i = 0); |
194 | |
195 | DomValue ¤tNode(int i = 0); |
196 | |
197 | void removeCurrentNode(std::optional<DomType> expectedType); |
198 | void removeCurrentScriptNode(std::optional<DomType> expectedType); |
199 | |
200 | void pushEl(const Path &p, const DomValue &it, AST::Node *n) |
201 | { |
202 | nodeStack.append(t: { .path: p, .item: it, .fileLocations: createMap(k: it.kind, p, n) }); |
203 | } |
204 | |
205 | FileLocations::Tree createMap(const FileLocations::Tree &base, const Path &p, AST::Node *n); |
206 | |
207 | FileLocations::Tree createMap(DomType k, const Path &p, AST::Node *n); |
208 | |
209 | const ScriptElementVariant & |
210 | finalizeScriptExpression(const ScriptElementVariant &element, const Path &pathFromOwner, |
211 | const FileLocations::Tree &ownerFileLocations); |
212 | |
213 | void setScriptExpression (const std::shared_ptr<ScriptExpression>& value); |
214 | |
215 | Path pathOfLastScriptNode() const; |
216 | |
217 | /*! |
218 | \internal |
219 | Helper to create string literals from AST nodes. |
220 | */ |
221 | template<typename AstNodeT> |
222 | static std::shared_ptr<ScriptElements::Literal> makeStringLiteral(QStringView value, |
223 | AstNodeT *ast) |
224 | { |
225 | auto myExp = std::make_shared<ScriptElements::Literal>(ast->firstSourceLocation(), |
226 | ast->lastSourceLocation()); |
227 | myExp->setLiteralValue(value.toString()); |
228 | return myExp; |
229 | } |
230 | |
231 | static std::shared_ptr<ScriptElements::Literal> makeStringLiteral(QStringView value, |
232 | QQmlJS::SourceLocation loc) |
233 | { |
234 | auto myExp = std::make_shared<ScriptElements::Literal>(args&: loc); |
235 | myExp->setLiteralValue(value.toString()); |
236 | return myExp; |
237 | } |
238 | |
239 | /*! |
240 | \internal |
241 | Helper to create script elements from AST nodes, as the DOM classes should be completely |
242 | dependency-free from AST and parser classes. Using the AST classes in qqmldomastcreator is |
243 | fine because it needs them for the construction/visit. \sa makeScriptList |
244 | */ |
245 | template<typename ScriptElementT, typename AstNodeT, |
246 | typename Enable = |
247 | std::enable_if_t<!std::is_same_v<ScriptElementT, ScriptElements::ScriptList>>> |
248 | static decltype(auto) makeScriptElement(AstNodeT *ast) |
249 | { |
250 | auto myExp = std::make_shared<ScriptElementT>(ast->firstSourceLocation(), |
251 | ast->lastSourceLocation()); |
252 | return myExp; |
253 | } |
254 | |
255 | /*! |
256 | \internal |
257 | Helper to create generic script elements from AST nodes. |
258 | \sa makeScriptElement |
259 | */ |
260 | template<typename AstNodeT> |
261 | static std::shared_ptr<ScriptElements::GenericScriptElement> |
262 | makeGenericScriptElement(AstNodeT *ast, DomType kind) |
263 | { |
264 | auto myExp = std::make_shared<ScriptElements::GenericScriptElement>( |
265 | ast->firstSourceLocation(), ast->lastSourceLocation()); |
266 | myExp->setKind(kind); |
267 | return myExp; |
268 | } |
269 | |
270 | enum UnaryExpressionKind { Prefix, Postfix }; |
271 | std::shared_ptr<ScriptElements::GenericScriptElement> |
272 | makeUnaryExpression(AST::Node *expression, QQmlJS::SourceLocation operatorToken, |
273 | bool hasExpression, UnaryExpressionKind type); |
274 | |
275 | static std::shared_ptr<ScriptElements::GenericScriptElement> |
276 | makeGenericScriptElement(SourceLocation location, DomType kind) |
277 | { |
278 | auto myExp = std::make_shared<ScriptElements::GenericScriptElement>(args&: location); |
279 | myExp->setKind(kind); |
280 | return myExp; |
281 | } |
282 | |
283 | /*! |
284 | \internal |
285 | Helper to create script lists from AST nodes. |
286 | \sa makeScriptElement |
287 | */ |
288 | template<typename AstNodeT> |
289 | static decltype(auto) makeScriptList(AstNodeT *ast) |
290 | { |
291 | auto myExp = |
292 | ScriptElements::ScriptList(ast->firstSourceLocation(), ast->lastSourceLocation()); |
293 | return myExp; |
294 | } |
295 | |
296 | template<typename ScriptElementT> |
297 | void pushScriptElement(const ScriptElementT &element) |
298 | { |
299 | Q_ASSERT_X(m_enableScriptExpressions, "pushScriptElement", |
300 | "Cannot create script elements when they are disabled!"); |
301 | scriptNodeStack.append(ScriptStackElement::from(element)); |
302 | } |
303 | |
304 | void disableScriptElements() |
305 | { |
306 | m_enableScriptExpressions = false; |
307 | scriptNodeStack.clear(); |
308 | } |
309 | |
310 | ScriptElementVariant scriptElementForQualifiedId(AST::UiQualifiedId *expression); |
311 | |
312 | public: |
313 | explicit QQmlDomAstCreator(const MutableDomItem &qmlFile); |
314 | |
315 | bool visit(AST::UiProgram *program) override; |
316 | void endVisit(AST::UiProgram *) override; |
317 | |
318 | bool visit(AST::UiPragma *el) override; |
319 | |
320 | bool visit(AST::UiImport *el) override; |
321 | |
322 | bool visit(AST::UiPublicMember *el) override; |
323 | void endVisit(AST::UiPublicMember *el) override; |
324 | |
325 | private: |
326 | ScriptElementVariant prepareBodyForFunction(AST::FunctionExpression *fExpression); |
327 | |
328 | public: |
329 | bool visit(AST::FunctionExpression *el) override; |
330 | void endVisit(AST::FunctionExpression *) override; |
331 | |
332 | bool visit(AST::FunctionDeclaration *el) override; |
333 | void endVisit(AST::FunctionDeclaration *) override; |
334 | |
335 | bool visit(AST::UiSourceElement *el) override; |
336 | void endVisit(AST::UiSourceElement *) override; |
337 | |
338 | void loadAnnotations(AST::UiObjectMember *el) { AST::Node::accept(node: el->annotations, visitor: this); } |
339 | |
340 | bool visit(AST::UiObjectDefinition *el) override; |
341 | void endVisit(AST::UiObjectDefinition *) override; |
342 | |
343 | bool visit(AST::UiObjectBinding *el) override; |
344 | void endVisit(AST::UiObjectBinding *) override; |
345 | |
346 | bool visit(AST::UiScriptBinding *el) override; |
347 | void endVisit(AST::UiScriptBinding *) override; |
348 | |
349 | bool visit(AST::UiArrayBinding *el) override; |
350 | void endVisit(AST::UiArrayBinding *) override; |
351 | |
352 | bool visit(AST::UiQualifiedId *) override; |
353 | |
354 | bool visit(AST::UiEnumDeclaration *el) override; |
355 | void endVisit(AST::UiEnumDeclaration *) override; |
356 | |
357 | bool visit(AST::UiEnumMemberList *el) override; |
358 | void endVisit(AST::UiEnumMemberList *el) override; |
359 | |
360 | bool visit(AST::UiInlineComponent *el) override; |
361 | void endVisit(AST::UiInlineComponent *) override; |
362 | |
363 | bool visit(AST::UiRequired *el) override; |
364 | |
365 | bool visit(AST::UiAnnotation *el) override; |
366 | void endVisit(AST::UiAnnotation *) override; |
367 | |
368 | // for Script elements: |
369 | bool visit(AST::BinaryExpression *exp) override; |
370 | void endVisit(AST::BinaryExpression *exp) override; |
371 | |
372 | bool visit(AST::Block *block) override; |
373 | void endVisit(AST::Block *) override; |
374 | |
375 | bool visit(AST::YieldExpression *block) override; |
376 | void endVisit(AST::YieldExpression *) override; |
377 | |
378 | bool visit(AST::ReturnStatement *block) override; |
379 | void endVisit(AST::ReturnStatement *) override; |
380 | |
381 | bool visit(AST::ForStatement *forStatement) override; |
382 | void endVisit(AST::ForStatement *forStatement) override; |
383 | |
384 | bool visit(AST::PatternElement *pe) override; |
385 | void endVisit(AST::PatternElement *pe) override; |
386 | void endVisitHelper(AST::PatternElement *pe, |
387 | const std::shared_ptr<ScriptElements::GenericScriptElement> &element); |
388 | |
389 | bool visit(AST::IfStatement *) override; |
390 | void endVisit(AST::IfStatement *) override; |
391 | |
392 | bool visit(AST::FieldMemberExpression *) override; |
393 | void endVisit(AST::FieldMemberExpression *) override; |
394 | |
395 | bool visit(AST::ArrayMemberExpression *) override; |
396 | void endVisit(AST::ArrayMemberExpression *) override; |
397 | |
398 | bool visit(AST::CallExpression *) override; |
399 | void endVisit(AST::CallExpression *) override; |
400 | |
401 | bool visit(AST::ArrayPattern *) override; |
402 | void endVisit(AST::ArrayPattern *) override; |
403 | |
404 | bool visit(AST::ObjectPattern *) override; |
405 | void endVisit(AST::ObjectPattern *) override; |
406 | |
407 | bool visit(AST::PatternProperty *) override; |
408 | void endVisit(AST::PatternProperty *) override; |
409 | |
410 | bool visit(AST::VariableStatement *) override; |
411 | void endVisit(AST::VariableStatement *) override; |
412 | |
413 | bool visit(AST::Type *expression) override; |
414 | void endVisit(AST::Type *expression) override; |
415 | |
416 | bool visit(AST::DefaultClause *) override; |
417 | void endVisit(AST::DefaultClause *) override; |
418 | |
419 | bool visit(AST::CaseClause *) override; |
420 | void endVisit(AST::CaseClause *) override; |
421 | |
422 | bool visit(AST::CaseClauses *) override; |
423 | void endVisit(AST::CaseClauses *) override; |
424 | |
425 | bool visit(AST::CaseBlock *) override; |
426 | void endVisit(AST::CaseBlock *) override; |
427 | |
428 | bool visit(AST::SwitchStatement *) override; |
429 | void endVisit(AST::SwitchStatement *) override; |
430 | |
431 | bool visit(AST::WhileStatement *) override; |
432 | void endVisit(AST::WhileStatement *) override; |
433 | |
434 | bool visit(AST::DoWhileStatement *) override; |
435 | void endVisit(AST::DoWhileStatement *) override; |
436 | |
437 | bool visit(AST::ForEachStatement *) override; |
438 | void endVisit(AST::ForEachStatement *) override; |
439 | |
440 | bool visit(AST::ClassExpression *) override; |
441 | void endVisit(AST::ClassExpression *) override; |
442 | |
443 | bool visit(AST::TryStatement *) override; |
444 | void endVisit(AST::TryStatement *) override; |
445 | |
446 | bool visit(AST::Catch *) override; |
447 | void endVisit(AST::Catch *) override; |
448 | |
449 | bool visit(AST::Finally *) override; |
450 | void endVisit(AST::Finally *) override; |
451 | |
452 | bool visit(AST::ThrowStatement *) override; |
453 | void endVisit(AST::ThrowStatement *) override; |
454 | |
455 | bool visit(AST::LabelledStatement *) override; |
456 | void endVisit(AST::LabelledStatement *) override; |
457 | |
458 | bool visit(AST::ContinueStatement *) override; |
459 | void endVisit(AST::ContinueStatement *) override; |
460 | |
461 | bool visit(AST::BreakStatement *) override; |
462 | void endVisit(AST::BreakStatement *) override; |
463 | |
464 | bool visit(AST::Expression *) override; |
465 | void endVisit(AST::Expression *) override; |
466 | |
467 | bool visit(AST::ConditionalExpression *) override; |
468 | void endVisit(AST::ConditionalExpression *) override; |
469 | |
470 | bool visit(AST::UnaryMinusExpression *) override; |
471 | void endVisit(AST::UnaryMinusExpression *) override; |
472 | |
473 | bool visit(AST::UnaryPlusExpression *) override; |
474 | void endVisit(AST::UnaryPlusExpression *) override; |
475 | |
476 | bool visit(AST::TildeExpression *) override; |
477 | void endVisit(AST::TildeExpression *) override; |
478 | |
479 | bool visit(AST::NotExpression *) override; |
480 | void endVisit(AST::NotExpression *) override; |
481 | |
482 | bool visit(AST::TypeOfExpression *) override; |
483 | void endVisit(AST::TypeOfExpression *) override; |
484 | |
485 | bool visit(AST::DeleteExpression *) override; |
486 | void endVisit(AST::DeleteExpression *) override; |
487 | |
488 | bool visit(AST::VoidExpression *) override; |
489 | void endVisit(AST::VoidExpression *) override; |
490 | |
491 | bool visit(AST::PostDecrementExpression *) override; |
492 | void endVisit(AST::PostDecrementExpression *) override; |
493 | |
494 | bool visit(AST::PostIncrementExpression *) override; |
495 | void endVisit(AST::PostIncrementExpression *) override; |
496 | |
497 | bool visit(AST::PreDecrementExpression *) override; |
498 | void endVisit(AST::PreDecrementExpression *) override; |
499 | |
500 | bool visit(AST::PreIncrementExpression *) override; |
501 | void endVisit(AST::PreIncrementExpression *) override; |
502 | |
503 | bool visit(AST::EmptyStatement *) override; |
504 | void endVisit(AST::EmptyStatement *) override; |
505 | |
506 | bool visit(AST::NestedExpression *) override; |
507 | void endVisit(AST::NestedExpression *) override; |
508 | |
509 | bool visit(AST::NewExpression *) override; |
510 | void endVisit(AST::NewExpression *) override; |
511 | |
512 | bool visit(AST::NewMemberExpression *) override; |
513 | void endVisit(AST::NewMemberExpression *) override; |
514 | |
515 | // lists of stuff whose children don't need a qqmljsscope: visitation order can be custom |
516 | bool visit(AST::UiParameterList *) override; |
517 | bool visit(AST::Elision *elision) override; |
518 | |
519 | |
520 | // lists of stuff whose children need a qqmljsscope: visitation order cannot be custom |
521 | void endVisit(AST::StatementList *list) override; |
522 | void endVisit(AST::VariableDeclarationList *vdl) override; |
523 | void endVisit(AST::ArgumentList *) override; |
524 | void endVisit(AST::PatternElementList *) override; |
525 | void endVisit(AST::PatternPropertyList *) override; |
526 | void endVisit(AST::FormalParameterList *el) override; |
527 | void endVisit(AST::TemplateLiteral *) override; |
528 | void endVisit(AST::TaggedTemplate *) override; |
529 | |
530 | |
531 | // literals and ids |
532 | bool visit(AST::IdentifierExpression *expression) override; |
533 | bool visit(AST::NumericLiteral *expression) override; |
534 | bool visit(AST::StringLiteral *expression) override; |
535 | bool visit(AST::NullExpression *expression) override; |
536 | bool visit(AST::TrueLiteral *expression) override; |
537 | bool visit(AST::FalseLiteral *expression) override; |
538 | bool visit(AST::ComputedPropertyName *expression) override; |
539 | bool visit(AST::IdentifierPropertyName *expression) override; |
540 | bool visit(AST::NumericLiteralPropertyName *expression) override; |
541 | bool visit(AST::StringLiteralPropertyName *expression) override; |
542 | bool visit(AST::TypeAnnotation *expression) override; |
543 | bool visit(AST::RegExpLiteral *) override; |
544 | bool visit(AST::ThisExpression *) override; |
545 | bool visit(AST::SuperLiteral *) override; |
546 | |
547 | void throwRecursionDepthError() override; |
548 | |
549 | bool stackHasScriptVariant() const |
550 | { |
551 | return !scriptNodeStack.isEmpty() && !scriptNodeStack.last().isList(); |
552 | } |
553 | bool stackHasScriptList() const |
554 | { |
555 | return !scriptNodeStack.isEmpty() && scriptNodeStack.last().isList(); |
556 | } |
557 | |
558 | private: |
559 | template<typename T> |
560 | void endVisitForLists(T *list, const std::function<int(T *)> &scriptElementsPerEntry = {}); |
561 | |
562 | public: |
563 | friend class QQmlDomAstCreatorWithQQmlJSScope; |
564 | }; |
565 | |
566 | class QQmlDomAstCreatorWithQQmlJSScope : public AST::Visitor |
567 | { |
568 | public: |
569 | QQmlDomAstCreatorWithQQmlJSScope(const QQmlJSScope::Ptr ¤t, MutableDomItem &qmlFile, |
570 | QQmlJSLogger *logger, QQmlJSImporter *importer); |
571 | |
572 | #define X(name) \ |
573 | bool visit(AST::name *) override; \ |
574 | void endVisit(AST::name *) override; |
575 | QQmlJSASTClassListToVisit |
576 | #undef X |
577 | |
578 | virtual void throwRecursionDepthError() override; |
579 | /*! |
580 | \internal |
581 | Disable the DOM for scriptexpressions, as not yet unimplemented script elements might crash |
582 | the construction. |
583 | */ |
584 | void enableScriptExpressions(bool enable = true) |
585 | { |
586 | m_enableScriptExpressions = enable; |
587 | m_domCreator.enableScriptExpressions(enable); |
588 | } |
589 | |
590 | void enableLoadFileLazily(bool enable = true) |
591 | { |
592 | m_loadFileLazily = enable; |
593 | m_domCreator.enableLoadFileLazily(enable); |
594 | } |
595 | |
596 | QQmlJSImportVisitor &scopeCreator() { return m_scopeCreator; } |
597 | |
598 | private: |
599 | void setScopeInDomAfterEndvisit(); |
600 | void setScopeInDomBeforeEndvisit(); |
601 | |
602 | template<typename U, typename... V> |
603 | using IsInList = std::disjunction<std::is_same<U, V>...>; |
604 | template<typename U> |
605 | using RequiresCustomIteration = |
606 | IsInList<U, AST::PatternElementList, AST::PatternPropertyList, AST::FormalParameterList, |
607 | AST::VariableDeclarationList, AST::TemplateLiteral>; |
608 | |
609 | enum VisitorKind : bool { DomCreator, ScopeCreator }; |
610 | /*! \internal |
611 | \brief Holds the information to reactivate a visitor |
612 | This struct tracks a visitor during its inactive phases |
613 | and holds the information needed to reactivate the visitor. |
614 | */ |
615 | struct InactiveVisitorMarker |
616 | { |
617 | qsizetype count; |
618 | AST::Node::Kind nodeKind; |
619 | VisitorKind inactiveVisitorKind; |
620 | |
621 | VisitorKind stillActiveVisitorKind() const |
622 | { |
623 | return inactiveVisitorKind == DomCreator ? ScopeCreator : DomCreator; |
624 | } |
625 | }; |
626 | |
627 | template<typename T> |
628 | void customListIteration(T *t) |
629 | { |
630 | static_assert(RequiresCustomIteration<T>::value); |
631 | for (T* it = t; it; it = it->next) { |
632 | if constexpr (std::is_same_v<T, AST::PatternElementList>) { |
633 | AST::Node::accept(it->elision, this); |
634 | AST::Node::accept(it->element, this); |
635 | } else if constexpr (std::is_same_v<T, AST::PatternPropertyList>) { |
636 | AST::Node::accept(it->property, this); |
637 | } else if constexpr (std::is_same_v<T, AST::FormalParameterList>) { |
638 | AST::Node::accept(it->element, this); |
639 | } else if constexpr (std::is_same_v<T, AST::VariableDeclarationList>) { |
640 | AST::Node::accept(it->declaration, this); |
641 | } else if constexpr (std::is_same_v<T, AST::ArgumentList>) { |
642 | AST::Node::accept(it->expression, this); |
643 | } else if constexpr (std::is_same_v<T, AST::PatternElementList>) { |
644 | AST::Node::accept(it->elision, this); |
645 | AST::Node::accept(it->element, this); |
646 | } else if constexpr (std::is_same_v<T, AST::TemplateLiteral>) { |
647 | AST::Node::accept(it->expression, this); |
648 | } else { |
649 | Q_UNREACHABLE(); |
650 | } |
651 | } |
652 | } |
653 | |
654 | static void initMarkerForActiveVisitor(std::optional<InactiveVisitorMarker> &inactiveVisitorMarker, |
655 | AST::Node::Kind nodeKind, bool continueForDom) |
656 | { |
657 | inactiveVisitorMarker.emplace(); |
658 | inactiveVisitorMarker->inactiveVisitorKind = continueForDom ? ScopeCreator : DomCreator; |
659 | inactiveVisitorMarker->count = 1; |
660 | inactiveVisitorMarker->nodeKind = nodeKind; |
661 | }; |
662 | |
663 | template<typename T> |
664 | bool performListIterationIfRequired(T *t) |
665 | { |
666 | if constexpr (RequiresCustomIteration<T>::value) { |
667 | customListIteration(t); |
668 | return false; |
669 | } |
670 | Q_UNUSED(t); |
671 | return true; |
672 | } |
673 | |
674 | template<typename T> |
675 | bool visitT(T *t) |
676 | { |
677 | const auto handleVisitResult = [this, t](const bool continueVisit) { |
678 | if (m_inactiveVisitorMarker && m_inactiveVisitorMarker->nodeKind == t->kind) |
679 | m_inactiveVisitorMarker->count += 1; |
680 | |
681 | if (continueVisit) |
682 | return performListIterationIfRequired(t); |
683 | return continueVisit; |
684 | }; |
685 | |
686 | // first case: no marker, both can visit |
687 | if (!m_inactiveVisitorMarker) { |
688 | bool continueForDom = m_domCreator.visit(t); |
689 | bool continueForScope = m_scopeCreator.visit(t); |
690 | if (!continueForDom && !continueForScope) |
691 | return false; |
692 | else if (continueForDom ^ continueForScope) { |
693 | initMarkerForActiveVisitor(inactiveVisitorMarker&: m_inactiveVisitorMarker, nodeKind: AST::Node::Kind(t->kind), |
694 | continueForDom); |
695 | return performListIterationIfRequired(t); |
696 | } else { |
697 | Q_ASSERT(continueForDom && continueForScope); |
698 | return performListIterationIfRequired(t); |
699 | } |
700 | Q_UNREACHABLE(); |
701 | } |
702 | |
703 | // second case: a marker, just one visit |
704 | switch (m_inactiveVisitorMarker->stillActiveVisitorKind()) { |
705 | case DomCreator: |
706 | return handleVisitResult(m_domCreator.visit(t)); |
707 | case ScopeCreator: |
708 | return handleVisitResult(m_scopeCreator.visit(t)); |
709 | }; |
710 | Q_UNREACHABLE(); |
711 | } |
712 | |
713 | template<typename T> |
714 | void endVisitT(T *t) |
715 | { |
716 | if (m_inactiveVisitorMarker && m_inactiveVisitorMarker->nodeKind == t->kind) { |
717 | m_inactiveVisitorMarker->count -= 1; |
718 | if (m_inactiveVisitorMarker->count == 0) |
719 | m_inactiveVisitorMarker.reset(); |
720 | } |
721 | if (m_inactiveVisitorMarker) { |
722 | switch (m_inactiveVisitorMarker->stillActiveVisitorKind()) { |
723 | case DomCreator: |
724 | m_domCreator.endVisit(t); |
725 | return; |
726 | case ScopeCreator: |
727 | m_scopeCreator.endVisit(t); |
728 | return; |
729 | }; |
730 | Q_UNREACHABLE(); |
731 | } |
732 | |
733 | setScopeInDomBeforeEndvisit(); |
734 | m_domCreator.endVisit(t); |
735 | setScopeInDomAfterEndvisit(); |
736 | m_scopeCreator.endVisit(t); |
737 | } |
738 | |
739 | QQmlJSScope::Ptr m_root; |
740 | QQmlJSLogger *m_logger = nullptr; |
741 | QQmlJSImporter *m_importer = nullptr; |
742 | QString m_implicitImportDirectory; |
743 | QQmlJSImportVisitor m_scopeCreator; |
744 | QQmlDomAstCreator m_domCreator; |
745 | |
746 | std::optional<InactiveVisitorMarker> m_inactiveVisitorMarker; |
747 | bool m_enableScriptExpressions = false; |
748 | bool m_loadFileLazily = false; |
749 | }; |
750 | |
751 | } // end namespace Dom |
752 | } // end namespace QQmlJS |
753 | |
754 | QT_END_NAMESPACE |
755 | #endif // QQMLDOMASTCREATOR_P_H |
756 |
Definitions
- QQmlDomAstCreator
- className
- DomValue
- DomValue
- QmlStackElement
- ScriptStackElement
- from
- takeVariant
- isList
- takeList
- setSemanticScope
- enableScriptExpressions
- enableLoadFileLazily
- currentEl
- currentScriptEl
- current
- currentIndex
- pushEl
- makeStringLiteral
- makeStringLiteral
- makeScriptElement
- makeGenericScriptElement
- UnaryExpressionKind
- makeGenericScriptElement
- makeScriptList
- pushScriptElement
- disableScriptElements
- loadAnnotations
- stackHasScriptVariant
- stackHasScriptList
- QQmlDomAstCreatorWithQQmlJSScope
- enableScriptExpressions
- enableLoadFileLazily
- scopeCreator
- VisitorKind
- InactiveVisitorMarker
- stillActiveVisitorKind
- customListIteration
- initMarkerForActiveVisitor
- performListIterationIfRequired
- visitT
Start learning QML with our Intro Training
Find out more