1 | // Copyright (C) 2019 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #ifndef QQMLJSIMPORTEDMEMBERSVISITOR_P_H |
5 | #define QQMLJSIMPORTEDMEMBERSVISITOR_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 | #include <private/qqmljscontextualtypes_p.h> |
18 | #include <qtqmlcompilerexports.h> |
19 | |
20 | #include "qqmljsannotation_p.h" |
21 | #include "qqmljsimporter_p.h" |
22 | #include "qqmljslogger_p.h" |
23 | #include "qqmljsscope_p.h" |
24 | #include "qqmljsscopesbyid_p.h" |
25 | |
26 | #include <QtCore/qvariant.h> |
27 | #include <QtCore/qstack.h> |
28 | |
29 | #include <private/qqmljsast_p.h> |
30 | #include <private/qqmljsdiagnosticmessage_p.h> |
31 | #include <private/qv4compileddata_p.h> |
32 | |
33 | #include <functional> |
34 | |
35 | QT_BEGIN_NAMESPACE |
36 | |
37 | namespace QQmlJS::Dom { |
38 | class QQmlDomAstCreatorWithQQmlJSScope; |
39 | } |
40 | |
41 | struct QQmlJSResourceFileMapper; |
42 | class Q_QMLCOMPILER_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor |
43 | { |
44 | public: |
45 | QQmlJSImportVisitor(); |
46 | QQmlJSImportVisitor(const QQmlJSScope::Ptr &target, |
47 | QQmlJSImporter *importer, QQmlJSLogger *logger, |
48 | const QString &implicitImportDirectory, |
49 | const QStringList &qmldirFiles = QStringList()); |
50 | ~QQmlJSImportVisitor(); |
51 | |
52 | using QQmlJS::AST::Visitor::endVisit; |
53 | using QQmlJS::AST::Visitor::postVisit; |
54 | using QQmlJS::AST::Visitor::preVisit; |
55 | using QQmlJS::AST::Visitor::visit; |
56 | |
57 | QQmlJSScope::Ptr result() const { return m_exportedRootScope; } |
58 | |
59 | const QQmlJSLogger *logger() const { return m_logger; } |
60 | QQmlJSLogger *logger() { return m_logger; } |
61 | |
62 | QQmlJSImporter::ImportedTypes imports() const { return m_rootScopeImports; } |
63 | QQmlJSScopesById addressableScopes() const { return m_scopesById; } |
64 | QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> signalHandlers() const |
65 | { |
66 | return m_signalHandlers; |
67 | } |
68 | QSet<QQmlJSScope::ConstPtr> literalScopesToCheck() const { return m_literalScopesToCheck; } |
69 | QList<QQmlJSScope::ConstPtr> qmlTypes() const { return m_qmlTypes; } |
70 | QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> scopesBylocation() const |
71 | { |
72 | return m_scopesByIrLocation; |
73 | } |
74 | |
75 | static QString implicitImportDirectory( |
76 | const QString &localFile, QQmlJSResourceFileMapper *mapper); |
77 | |
78 | // ### should this be restricted? |
79 | QQmlJSImporter *importer() { return m_importer; } |
80 | const QQmlJSImporter *importer() const { return m_importer; } |
81 | |
82 | struct UnfinishedBinding |
83 | { |
84 | QQmlJSScope::Ptr owner; |
85 | std::function<QQmlJSMetaPropertyBinding()> create; |
86 | QQmlJSScope::BindingTargetSpecifier specifier = QQmlJSScope::SimplePropertyTarget; |
87 | }; |
88 | |
89 | QStringList seenModuleQualifiers() const { return m_seenModuleQualifiers; } |
90 | |
91 | protected: |
92 | // Linter warnings, we might want to move this at some point |
93 | bool visit(QQmlJS::AST::StringLiteral *) override; |
94 | |
95 | bool visit(QQmlJS::AST::ExpressionStatement *ast) override; |
96 | void endVisit(QQmlJS::AST::ExpressionStatement *ast) override; |
97 | |
98 | bool visit(QQmlJS::AST::UiProgram *) override; |
99 | void endVisit(QQmlJS::AST::UiProgram *) override; |
100 | bool visit(QQmlJS::AST::UiObjectDefinition *) override; |
101 | void endVisit(QQmlJS::AST::UiObjectDefinition *) override; |
102 | bool visit(QQmlJS::AST::UiInlineComponent *) override; |
103 | void endVisit(QQmlJS::AST::UiInlineComponent *) override; |
104 | bool visit(QQmlJS::AST::UiPublicMember *) override; |
105 | void endVisit(QQmlJS::AST::UiPublicMember *) override; |
106 | bool visit(QQmlJS::AST::UiRequired *required) override; |
107 | bool visit(QQmlJS::AST::UiScriptBinding *) override; |
108 | void endVisit(QQmlJS::AST::UiScriptBinding *) override; |
109 | bool visit(QQmlJS::AST::UiArrayBinding *) override; |
110 | void endVisit(QQmlJS::AST::UiArrayBinding *) override; |
111 | bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override; |
112 | bool visit(QQmlJS::AST::FunctionExpression *fexpr) override; |
113 | void endVisit(QQmlJS::AST::FunctionExpression *) override; |
114 | bool visit(QQmlJS::AST::UiSourceElement *) override; |
115 | bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override; |
116 | void endVisit(QQmlJS::AST::FunctionDeclaration *) override; |
117 | bool visit(QQmlJS::AST::ClassExpression *ast) override; |
118 | void endVisit(QQmlJS::AST::ClassExpression *) override; |
119 | bool visit(QQmlJS::AST::UiImport *import) override; |
120 | bool visit(QQmlJS::AST::UiPragma *pragma) override; |
121 | bool visit(QQmlJS::AST::ClassDeclaration *ast) override; |
122 | void endVisit(QQmlJS::AST::ClassDeclaration *ast) override; |
123 | bool visit(QQmlJS::AST::ForStatement *ast) override; |
124 | void endVisit(QQmlJS::AST::ForStatement *ast) override; |
125 | bool visit(QQmlJS::AST::ForEachStatement *ast) override; |
126 | void endVisit(QQmlJS::AST::ForEachStatement *ast) override; |
127 | bool visit(QQmlJS::AST::Block *ast) override; |
128 | void endVisit(QQmlJS::AST::Block *ast) override; |
129 | bool visit(QQmlJS::AST::CaseBlock *ast) override; |
130 | void endVisit(QQmlJS::AST::CaseBlock *ast) override; |
131 | bool visit(QQmlJS::AST::Catch *ast) override; |
132 | void endVisit(QQmlJS::AST::Catch *ast) override; |
133 | bool visit(QQmlJS::AST::WithStatement *withStatement) override; |
134 | void endVisit(QQmlJS::AST::WithStatement *ast) override; |
135 | |
136 | bool visit(QQmlJS::AST::VariableDeclarationList *vdl) override; |
137 | bool visit(QQmlJS::AST::FormalParameterList *fpl) override; |
138 | |
139 | bool visit(QQmlJS::AST::UiObjectBinding *uiob) override; |
140 | void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override; |
141 | |
142 | bool visit(QQmlJS::AST::ExportDeclaration *exp) override; |
143 | void endVisit(QQmlJS::AST::ExportDeclaration *exp) override; |
144 | |
145 | bool visit(QQmlJS::AST::ESModule *module) override; |
146 | void endVisit(QQmlJS::AST::ESModule *module) override; |
147 | |
148 | bool visit(QQmlJS::AST::Program *program) override; |
149 | void endVisit(QQmlJS::AST::Program *program) override; |
150 | |
151 | void endVisit(QQmlJS::AST::FieldMemberExpression *) override; |
152 | bool visit(QQmlJS::AST::IdentifierExpression *idexp) override; |
153 | |
154 | bool visit(QQmlJS::AST::PatternElement *) override; |
155 | |
156 | void throwRecursionDepthError() override; |
157 | |
158 | QString m_implicitImportDirectory; |
159 | QStringList m_qmldirFiles; |
160 | QQmlJSScope::Ptr m_currentScope; |
161 | const QQmlJSScope::Ptr m_exportedRootScope; |
162 | QQmlJSImporter *m_importer = nullptr; |
163 | QQmlJSLogger *m_logger = nullptr; |
164 | |
165 | using RootDocumentNameType = QQmlJSScope::RootDocumentNameType; |
166 | using InlineComponentNameType = QQmlJSScope::InlineComponentNameType; |
167 | using InlineComponentOrDocumentRootName = QQmlJSScope::RootDocumentNameType; |
168 | QQmlJSScope::InlineComponentOrDocumentRootName m_currentRootName = |
169 | QQmlJSScope::RootDocumentNameType(); |
170 | bool m_nextIsInlineComponent = false; |
171 | bool m_rootIsSingleton = false; |
172 | QQmlJSScope::Ptr m_savedBindingOuterScope; |
173 | QQmlJSScope::ConstPtr m_globalScope; |
174 | QQmlJSScopesById m_scopesById; |
175 | QQmlJSImporter::ImportedTypes m_rootScopeImports; |
176 | QList<QQmlJSScope::ConstPtr> m_qmlTypes; |
177 | |
178 | // We need to record the locations as IR locations because those contain less data. |
179 | // This way we can look up objects by IR location later. |
180 | QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> m_scopesByIrLocation; |
181 | |
182 | // Maps all qmlNames to the source location of their import |
183 | QMultiHash<QString, QQmlJS::SourceLocation> m_importTypeLocationMap; |
184 | // Maps all static modules to the source location of their import |
185 | QMultiHash<QString, QQmlJS::SourceLocation> m_importStaticModuleLocationMap; |
186 | // Contains all import source locations (could be extracted from above but that is expensive) |
187 | QSet<QQmlJS::SourceLocation> m_importLocations; |
188 | // A set of all types that have been used during type resolution |
189 | QSet<QString> m_usedTypes; |
190 | |
191 | QList<UnfinishedBinding> m_bindings; |
192 | |
193 | // stores JS functions and Script bindings per scope (only the name). mimics |
194 | // the content of QmlIR::Object::functionsAndExpressions |
195 | QHash<QQmlJSScope::ConstPtr, QList<QString>> m_functionsAndExpressions; |
196 | |
197 | struct FunctionOrExpressionIdentifier |
198 | { |
199 | QQmlJSScope::ConstPtr scope; |
200 | QString name; |
201 | friend bool operator==(const FunctionOrExpressionIdentifier &x, |
202 | const FunctionOrExpressionIdentifier &y) |
203 | { |
204 | return x.scope == y.scope && x.name == y.name; |
205 | } |
206 | friend bool operator!=(const FunctionOrExpressionIdentifier &x, |
207 | const FunctionOrExpressionIdentifier &y) |
208 | { |
209 | return !(x == y); |
210 | } |
211 | friend size_t qHash(const FunctionOrExpressionIdentifier &x, size_t seed = 0) |
212 | { |
213 | return qHashMulti(seed, args: x.scope, args: x.name); |
214 | } |
215 | }; |
216 | |
217 | // tells whether last-processed UiScriptBinding is truly a script binding |
218 | bool m_thisScriptBindingIsJavaScript = false; |
219 | QStack<FunctionOrExpressionIdentifier> m_functionStack; |
220 | // stores the number of functions inside each function |
221 | QHash<FunctionOrExpressionIdentifier, int> m_innerFunctions; |
222 | QQmlJSMetaMethod::RelativeFunctionIndex |
223 | addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope, const QString &name); |
224 | void forgetFunctionExpression(const QString &name); |
225 | int synthesizeCompilationUnitRuntimeFunctionIndices(const QQmlJSScope::Ptr &scope, |
226 | int count) const; |
227 | void populateRuntimeFunctionIndicesForDocument() const; |
228 | |
229 | void enterEnvironment(QQmlJSScope::ScopeType type, const QString &name, |
230 | const QQmlJS::SourceLocation &location); |
231 | // Finds an existing scope before attempting to create a new one. Returns \c |
232 | // true if the scope already exists and \c false if the new scope is created |
233 | bool enterEnvironmentNonUnique(QQmlJSScope::ScopeType type, const QString &name, |
234 | const QQmlJS::SourceLocation &location); |
235 | void leaveEnvironment(); |
236 | |
237 | // A set of types that have not been resolved but have been used during the |
238 | // AST traversal |
239 | QSet<QQmlJSScope::ConstPtr> m_unresolvedTypes; |
240 | template<typename ErrorHandler> |
241 | bool isTypeResolved(const QQmlJSScope::ConstPtr &type, ErrorHandler handle) |
242 | { |
243 | if (type->isFullyResolved()) |
244 | return true; |
245 | |
246 | // Note: ignore duplicates, but only after we are certain that the type |
247 | // is still unresolved |
248 | if (m_unresolvedTypes.contains(value: type)) |
249 | return false; |
250 | |
251 | m_unresolvedTypes.insert(value: type); |
252 | |
253 | handle(type); |
254 | return false; |
255 | } |
256 | bool isTypeResolved(const QQmlJSScope::ConstPtr &type); |
257 | |
258 | QVector<QQmlJSAnnotation> parseAnnotations(QQmlJS::AST::UiAnnotationList *list); |
259 | void setAllBindings(); |
260 | void addDefaultProperties(); |
261 | void processDefaultProperties(); |
262 | void processPropertyBindings(); |
263 | void checkRequiredProperties(); |
264 | void processPropertyTypes(); |
265 | void processMethodTypes(); |
266 | void processPropertyBindingObjects(); |
267 | void flushPendingSignalParameters(); |
268 | |
269 | QQmlJSScope::ConstPtr scopeById(const QString &id, const QQmlJSScope::ConstPtr ¤t); |
270 | |
271 | void breakInheritanceCycles(const QQmlJSScope::Ptr &scope); |
272 | void checkDeprecation(const QQmlJSScope::ConstPtr &scope); |
273 | void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope); |
274 | bool rootScopeIsValid() const { return m_exportedRootScope->sourceLocation().isValid(); } |
275 | |
276 | enum class BindingExpressionParseResult { Invalid, Script, Literal, Translation }; |
277 | BindingExpressionParseResult parseBindingExpression(const QString &name, |
278 | const QQmlJS::AST::Statement *statement); |
279 | bool isImportPrefix(QString prefix) const; |
280 | |
281 | // Used to temporarily store annotations for functions and generators wrapped in UiSourceElements |
282 | QVector<QQmlJSAnnotation> m_pendingMethodAnnotations; |
283 | |
284 | struct PendingPropertyType |
285 | { |
286 | QQmlJSScope::Ptr scope; |
287 | QString name; |
288 | QQmlJS::SourceLocation location; |
289 | }; |
290 | |
291 | struct PendingMethodTypeAnnotations |
292 | { |
293 | QQmlJSScope::Ptr scope; |
294 | QString methodName; |
295 | // This keeps type annotations' locations in order (parameters then return type). |
296 | // If an annotation is not present, it is represented by an invalid source location. |
297 | QVarLengthArray<QQmlJS::SourceLocation, 3> locations; |
298 | }; |
299 | |
300 | struct PendingPropertyObjectBinding |
301 | { |
302 | QQmlJSScope::Ptr scope; |
303 | QQmlJSScope::Ptr childScope; |
304 | QString name; |
305 | QQmlJS::SourceLocation location; |
306 | bool onToken; |
307 | }; |
308 | |
309 | struct RequiredProperty |
310 | { |
311 | QQmlJSScope::Ptr scope; |
312 | QString name; |
313 | QQmlJS::SourceLocation location; |
314 | }; |
315 | |
316 | /*! |
317 | Utility wrapper that adds visibility scope to the data. |
318 | |
319 | This wrapper becomes useful for binding processing where we need to know |
320 | both the property (or signal handler) owner and the scope in which the |
321 | binding is executed (the "visibility" scope). |
322 | |
323 | As visibility scope (and data) does not typically have sufficient |
324 | information about a proper source location of that data, the location |
325 | also has to be provided to simplify the error reporting. |
326 | */ |
327 | template<typename T> |
328 | struct WithVisibilityScope |
329 | { |
330 | QQmlJSScope::Ptr visibilityScope; |
331 | QQmlJS::SourceLocation dataLocation; |
332 | T data; |
333 | }; |
334 | |
335 | QHash<QQmlJSScope::Ptr, QVector<QQmlJSScope::Ptr>> m_pendingDefaultProperties; |
336 | QVector<PendingPropertyType> m_pendingPropertyTypes; |
337 | QVector<PendingMethodTypeAnnotations> m_pendingMethodTypeAnnotations; |
338 | QVector<PendingPropertyObjectBinding> m_pendingPropertyObjectBindings; |
339 | QVector<RequiredProperty> m_requiredProperties; |
340 | QVector<QQmlJSScope::Ptr> m_objectBindingScopes; |
341 | QVector<QQmlJSScope::Ptr> m_objectDefinitionScopes; |
342 | |
343 | QHash<QQmlJSScope::Ptr, QVector<WithVisibilityScope<QString>>> m_propertyBindings; |
344 | |
345 | QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> m_signalHandlers; |
346 | QSet<QQmlJSScope::ConstPtr> m_literalScopesToCheck; |
347 | QQmlJS::SourceLocation m_pendingSignalHandler; |
348 | QStringList m_seenModuleQualifiers; |
349 | |
350 | private: |
351 | void checkSignal( |
352 | const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location, |
353 | const QString &handlerName, const QStringList &handlerParameters); |
354 | void importBaseModules(); |
355 | void resolveAliases(); |
356 | void resolveGroupProperties(); |
357 | void handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding); |
358 | |
359 | void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr); |
360 | void processImportWarnings( |
361 | const QString &what, const QList<QQmlJS::DiagnosticMessage> &warnings, |
362 | const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation()); |
363 | void addImportWithLocation(const QString &name, const QQmlJS::SourceLocation &loc); |
364 | void populateCurrentScope(QQmlJSScope::ScopeType type, const QString &name, |
365 | const QQmlJS::SourceLocation &location); |
366 | void enterRootScope(QQmlJSScope::ScopeType type, const QString &name, |
367 | const QQmlJS::SourceLocation &location); |
368 | |
369 | QList<QQmlJS::DiagnosticMessage> importFromHost( |
370 | const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location); |
371 | QList<QQmlJS::DiagnosticMessage> importFromQrc( |
372 | const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location); |
373 | |
374 | public: |
375 | friend class QQmlJS::Dom::QQmlDomAstCreatorWithQQmlJSScope; |
376 | }; |
377 | |
378 | QT_END_NAMESPACE |
379 | |
380 | #endif // QQMLJSIMPORTEDMEMBERSVISITOR_P_H |
381 |
Definitions
- QQmlJSImportVisitor
- result
- logger
- logger
- imports
- addressableScopes
- signalHandlers
- literalScopesToCheck
- qmlTypes
- scopesBylocation
- importer
- importer
- UnfinishedBinding
- seenModuleQualifiers
- FunctionOrExpressionIdentifier
- operator==
- operator!=
- qHash
- isTypeResolved
- rootScopeIsValid
- BindingExpressionParseResult
- PendingPropertyType
- PendingMethodTypeAnnotations
- PendingPropertyObjectBinding
- RequiredProperty
Learn Advanced QML with KDAB
Find out more