Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmljsscopesbyid_p.h
Go to the documentation of this file.
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// Qt-Security score:significant
4
5#ifndef QQMLJSSCOPESBYID_P_H
6#define QQMLJSSCOPESBYID_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17
18
19#include "qqmljsscope_p.h"
20
21#include <QtCore/qhash.h>
22#include <QtCore/qstring.h>
23
25
31
33{
34public:
36 enum class CallbackResult: bool { StopSearch = false, ContinueSearch = true };
37 enum class Success: bool { No = false, Yes = true };
38
39 template<typename T>
41 {
42 CallbackResult operator() (const T &candidate, Confidence confidence) {
43 // Here, we only accept certain results, and abort the search otherwise.
44 switch (confidence) {
46 result = candidate;
49 break;
50 }
52 }
53
54 T result = {};
55 };
56
57 template<typename T>
59 {
60 CallbackResult operator() (const T &candidate, Confidence confidence) {
61 // The last one in a chain of candidates is the one that's definitely a component,
62 // by virtue of either being the file root component or a recognized inline component,
63 // or QQmlComponent property.
64 Q_UNUSED(confidence);
65 result = candidate;
67 }
68
69 T result = {};
70 };
71
72 bool componentsAreBound() const { return m_componentsAreBound; }
73 void setComponentsAreBound(bool bound) { m_componentsAreBound = bound; }
74
75 void setSignaturesAreEnforced(bool enforced) { m_signaturesAreEnforced = enforced; }
76 bool signaturesAreEnforced() const { return m_signaturesAreEnforced; }
77
78 void setValueTypesAreAddressable(bool addressable) { m_valueTypesAreAddressable = addressable; }
79 bool valueTypesAreAddressable() const { return m_valueTypesAreAddressable; }
80
81 /*!
82 \internal
83 Find the possible IDs for \a scope as seen by \a referrer. There can be at most one
84 ID for a scope. Depending on whether we can determine the component boundaries of the
85 \a scope and the \a referrer we may or may not be able to tell whether it's visible.
86
87 We can generally determine the relevant component boundaries for each scope. However,
88 if the scope or any of its parents is assigned to a property of which we cannot see the
89 type, we don't know whether the type of that property happens to be Component. In that
90 case, we can't say.
91
92 Returns \c Success::Yes if either no ID was found or the \a callback returned
93 \c CallbackResult::ContinueSearch for the ID found. Returns \c Success::No if the
94 \a callback returned \c CallbackResult::StopSearch.
95 */
96 template<typename F>
98 const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer,
99 QQmlJSScopesByIdOptions options, F &&callback) const
100 {
101 Q_ASSERT(!scope.isNull());
102
103 // A scope can only have one ID.
104 const QString key = m_scopesById.key(scope);
105 if (key.isEmpty())
106 return Success::Yes;
107
108 Success result = Success::Yes;
109 possibleComponentRoots(
110 referrer, [&](const QQmlJSScope::ConstPtr &referrerRoot,
111 QQmlJSScope::IsComponentRoot referrerConfidence) {
112 return possibleComponentRoots(
113 scope, [&](const QQmlJSScope::ConstPtr &referredRoot,
114 QQmlJSScope::IsComponentRoot referredConfidence) {
115 if (isComponentVisible(referredRoot, referrerRoot, options)) {
116 // The key won't change and our confidence won't change either. No need to
117 // call this again for each combination of scopes.
118 if (callback(key, confidence(referrerConfidence, referredConfidence))
120 result = Success::No;
121 }
122
124 }
126 });
127 });
128
129 return result;
130 }
131
132 /*!
133 \internal
134 Returns the id of \a scope in the component to which \a referrer belongs to.
135 If \a scope is not visible from \a referrer or has no ID, an empty string is returned.
136 An empty string is also returned if we can't determine the component boundaries for either
137 \a scope or \a referrer.
138 */
139 QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer,
140 QQmlJSScopesByIdOptions options = Default) const
141 {
142 CertainCallback<QString> result;
143 const Success isCertain = possibleIds(scope, referrer, options, result);
144
145 // The default callback only assigns the result if it's certain.
146 // We can't have "possible" results after a certain one.
147 Q_ASSERT(isCertain == Success::Yes || result.result.isEmpty());
148
149 return result.result;
150 }
151
152 /*!
153 \internal
154 Find all possible scopes for \a id as seen by \a referrer. There can be multiple
155 possibilities if we cannot determine component boundaries for any candidate or the
156 referrer.
157
158 We can generally determine the relevant component boundaries for each scope. However,
159 if the scope or any of its parents is assigned to a property of which we cannot see the
160 type, we don't know whether the type of that property happens to be Component. In that
161 case, we can't say.
162
163 Returns \c Success::Yes if either no suitable scope was found or the \a callback returned
164 \c CallbackResult::ContinueSearch for all scopes found. Returns \c Success::No if the
165 \a callback returns \c CallbackResult::StopSearch for any scope found. It also stops the
166 search at that point.
167 */
168 template<typename F>
170 const QString &id, const QQmlJSScope::ConstPtr &referrer,
171 QQmlJSScopesByIdOptions options, F &&callback) const
172 {
173 Q_ASSERT(!id.isEmpty());
174 Success result = Success::Yes;
175
176 const auto range = m_scopesById.equal_range(id);
177 for (auto it = range.first; it != range.second; ++it) {
178 possibleComponentRoots(
179 *it, [&](const QQmlJSScope::ConstPtr &referredRoot,
180 QQmlJSScope::IsComponentRoot referredConfidence) {
181
182 possibleComponentRoots(
183 referrer, [&](const QQmlJSScope::ConstPtr &referrerRoot,
184 QQmlJSScope::IsComponentRoot referrerConfidence) {
185
186 if (!isComponentVisible(referredRoot, referrerRoot, options))
188
189 if (callback(*it, confidence(referrerConfidence, referredConfidence))
191 // Propagate the negative result from the callback.
192 result = Success::No;
193 }
194
195 // Once we've reported *it, we don't care about the other possible referrerRoots
196 // anymore. They are not reported after all. The confidence can't change
197 // anymore, either.
199 });
200
201 // If nothing matched or the callback was successful, consider the next candidate.
202 // If the callback failed, stop here.
203 return result == Success::Yes
206 });
207
208 // If the callback failed, return right away.
209 if (result == Success::No)
210 return result;
211 }
212
213 Q_ASSERT(result == Success::Yes);
214 return Success::Yes;
215 }
216
217 /*!
218 \internal
219 Returns the scope that has id \a id in the component to which \a referrer belongs to.
220 If no such scope exists, a null scope is returned.
221 A null scope is also returned if we cannot determine the component boundaries for any
222 candidate or the \a referrer.
223 */
224 QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer,
225 QQmlJSScopesByIdOptions options = Default) const
226 {
227 CertainCallback<QQmlJSScope::ConstPtr> result;
228 const Success isCertain = possibleScopes(id, referrer, options, result);
229
230 // The default callback only assigns the result if it's certain.
231 // We can't have "possible" results after a certain one.
232 Q_ASSERT(isCertain == Success::Yes || result.result.isNull());
233
234 return result.result;
235 }
236
237 void insert(const QString &id, const QQmlJSScope::ConstPtr &scope)
238 {
239 Q_ASSERT(!id.isEmpty());
240 m_scopesById.insert(id, scope);
241 }
242
243 void clear() { m_scopesById.clear(); }
244
245 /*!
246 \internal
247 Returns \c true if \a id exists anywhere in the current document.
248 This is still allowed if the other occurrence is in a different (inline) component.
249 Check the return value of scope to know whether the id has already been assigned
250 in a givne scope.
251 */
252 bool existsAnywhereInDocument(const QString &id) const { return m_scopesById.contains(id); }
253
254private:
255 template<typename F>
256 static CallbackResult possibleComponentRoots(const QQmlJSScope::ConstPtr &inner, F &&callback)
257 {
258 QQmlJSScope::ConstPtr scope = inner;
259 QQmlJSScope::IsComponentRoot maxConfidence = QQmlJSScope::IsComponentRoot::Yes;
260 while (scope) {
261 switch (scope->componentRootStatus()) {
262 case QQmlJSScope::IsComponentRoot::Maybe:
263 if (callback(scope, QQmlJSScope::IsComponentRoot::Maybe)
264 == CallbackResult::StopSearch) {
266 }
267 // If we've seen one "maybe", then there is no certainty anymore.
268 // The "maybe" ones are always processed first since the properties of unknown
269 // type are inside the elements they belong to.
270 maxConfidence = QQmlJSScope::IsComponentRoot::Maybe;
271 Q_FALLTHROUGH();
272 case QQmlJSScope::IsComponentRoot::No:
273 scope = scope->parentScope();
274 continue;
275 case QQmlJSScope::IsComponentRoot::Yes:
276 return callback(scope, maxConfidence);
277 }
278 }
279
281 }
282
283 static Confidence confidence(
284 QQmlJSScope::IsComponentRoot a, QQmlJSScope::IsComponentRoot b) {
285 switch (a) {
286 case QQmlJSScope::IsComponentRoot::Yes:
287 return b == QQmlJSScope::IsComponentRoot::Yes
288 ? Confidence::Certain
289 : Confidence::Possible;
290 case QQmlJSScope::IsComponentRoot::Maybe:
292 default:
293 break;
294 }
295
296 Q_UNREACHABLE_RETURN(Confidence::Certain);
297 }
298
299 bool isComponentVisible(const QQmlJSScope::ConstPtr &observed,
300 const QQmlJSScope::ConstPtr &observer,
301 QQmlJSScopesByIdOptions options) const
302 {
303 if (!m_componentsAreBound && !options.testAnyFlag(AssumeComponentsAreBound))
304 return observed == observer;
305
306 for (QQmlJSScope::ConstPtr scope = observer; scope; scope = scope->parentScope()) {
307 if (scope == observed)
308 return true;
309 }
310
311 return false;
312 }
313
314 QMultiHash<QString, QQmlJSScope::ConstPtr> m_scopesById;
315 bool m_componentsAreBound = false;
316 bool m_signaturesAreEnforced = true;
317 bool m_valueTypesAreAddressable = false;
318};
319
320QT_END_NAMESPACE
321
322#endif // QQMLJSSCOPESBYID_P_H
Success possibleIds(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer, QQmlJSScopesByIdOptions options, F &&callback) const
bool signaturesAreEnforced() const
bool componentsAreBound() const
Success possibleScopes(const QString &id, const QQmlJSScope::ConstPtr &referrer, QQmlJSScopesByIdOptions options, F &&callback) const
bool existsAnywhereInDocument(const QString &id) const
void setValueTypesAreAddressable(bool addressable)
void setComponentsAreBound(bool bound)
QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer, QQmlJSScopesByIdOptions options=Default) const
void setSignaturesAreEnforced(bool enforced)
bool valueTypesAreAddressable() const
QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer, QQmlJSScopesByIdOptions options=Default) const
void insert(const QString &id, const QQmlJSScope::ConstPtr &scope)
QQmlJSScopesByIdOption
@ Default
@ AssumeComponentsAreBound
Q_DECLARE_FLAGS(QQmlJSScopesByIdOptions, QQmlJSScopesByIdOption)
CallbackResult operator()(const T &candidate, Confidence confidence)
CallbackResult operator()(const T &candidate, Confidence confidence)
std::shared_ptr< QQmlJSResourceFileMapper > mapper
std::shared_ptr< QQmlJSLogger > logger
std::shared_ptr< QQmlJSImporter > importer