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