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 void setValueTypesAreAssertable(bool assertable) { m_valueTypesAreAssertable = assertable; }
82 bool valueTypesAreAssertable() const { return m_valueTypesAreAssertable; }
83
84 /*!
85 \internal
86 Find the possible IDs for \a scope as seen by \a referrer. There can be at most one
87 ID for a scope. Depending on whether we can determine the component boundaries of the
88 \a scope and the \a referrer we may or may not be able to tell whether it's visible.
89
90 We can generally determine the relevant component boundaries for each scope. However,
91 if the scope or any of its parents is assigned to a property of which we cannot see the
92 type, we don't know whether the type of that property happens to be Component. In that
93 case, we can't say.
94
95 Returns \c Success::Yes if either no ID was found or the \a callback returned
96 \c CallbackResult::ContinueSearch for the ID found. Returns \c Success::No if the
97 \a callback returned \c CallbackResult::StopSearch.
98 */
99 template<typename F>
101 const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer,
102 QQmlJSScopesByIdOptions options, F &&callback) const
103 {
104 Q_ASSERT(!scope.isNull());
105
106 // A scope can only have one ID.
107 const QString key = m_scopesById.key(scope);
108 if (key.isEmpty())
109 return Success::Yes;
110
111 Success result = Success::Yes;
112 possibleComponentRoots(
113 referrer, [&](const QQmlJSScope::ConstPtr &referrerRoot,
114 QQmlJSScope::IsComponentRoot referrerConfidence) {
115 return possibleComponentRoots(
116 scope, [&](const QQmlJSScope::ConstPtr &referredRoot,
117 QQmlJSScope::IsComponentRoot referredConfidence) {
118 if (isComponentVisible(referredRoot, referrerRoot, options)) {
119 // The key won't change and our confidence won't change either. No need to
120 // call this again for each combination of scopes.
121 if (callback(key, confidence(referrerConfidence, referredConfidence))
123 result = Success::No;
124 }
125
127 }
129 });
130 });
131
132 return result;
133 }
134
135 /*!
136 \internal
137 Returns the id of \a scope in the component to which \a referrer belongs to.
138 If \a scope is not visible from \a referrer or has no ID, an empty string is returned.
139 An empty string is also returned if we can't determine the component boundaries for either
140 \a scope or \a referrer.
141 */
142 QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer,
143 QQmlJSScopesByIdOptions options = Default) const
144 {
145 CertainCallback<QString> result;
146 const Success isCertain = possibleIds(scope, referrer, options, result);
147
148 // The default callback only assigns the result if it's certain.
149 // We can't have "possible" results after a certain one.
150 Q_ASSERT(isCertain == Success::Yes || result.result.isEmpty());
151
152 return result.result;
153 }
154
155 /*!
156 \internal
157 Find all possible scopes for \a id as seen by \a referrer. There can be multiple
158 possibilities if we cannot determine component boundaries for any candidate or the
159 referrer.
160
161 We can generally determine the relevant component boundaries for each scope. However,
162 if the scope or any of its parents is assigned to a property of which we cannot see the
163 type, we don't know whether the type of that property happens to be Component. In that
164 case, we can't say.
165
166 Returns \c Success::Yes if either no suitable scope was found or the \a callback returned
167 \c CallbackResult::ContinueSearch for all scopes found. Returns \c Success::No if the
168 \a callback returns \c CallbackResult::StopSearch for any scope found. It also stops the
169 search at that point.
170 */
171 template<typename F>
173 const QString &id, const QQmlJSScope::ConstPtr &referrer,
174 QQmlJSScopesByIdOptions options, F &&callback) const
175 {
176 Q_ASSERT(!id.isEmpty());
177 Success result = Success::Yes;
178
179 const auto range = m_scopesById.equal_range(id);
180 for (auto it = range.first; it != range.second; ++it) {
181 possibleComponentRoots(
182 *it, [&](const QQmlJSScope::ConstPtr &referredRoot,
183 QQmlJSScope::IsComponentRoot referredConfidence) {
184
185 possibleComponentRoots(
186 referrer, [&](const QQmlJSScope::ConstPtr &referrerRoot,
187 QQmlJSScope::IsComponentRoot referrerConfidence) {
188
189 if (!isComponentVisible(referredRoot, referrerRoot, options))
191
192 if (callback(*it, confidence(referrerConfidence, referredConfidence))
194 // Propagate the negative result from the callback.
195 result = Success::No;
196 }
197
198 // Once we've reported *it, we don't care about the other possible referrerRoots
199 // anymore. They are not reported after all. The confidence can't change
200 // anymore, either.
202 });
203
204 // If nothing matched or the callback was successful, consider the next candidate.
205 // If the callback failed, stop here.
206 return result == Success::Yes
209 });
210
211 // If the callback failed, return right away.
212 if (result == Success::No)
213 return result;
214 }
215
216 Q_ASSERT(result == Success::Yes);
217 return Success::Yes;
218 }
219
220 /*!
221 \internal
222 Returns the scope that has id \a id in the component to which \a referrer belongs to.
223 If no such scope exists, a null scope is returned.
224 A null scope is also returned if we cannot determine the component boundaries for any
225 candidate or the \a referrer.
226 */
227 QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer,
228 QQmlJSScopesByIdOptions options = Default) const
229 {
230 CertainCallback<QQmlJSScope::ConstPtr> result;
231 const Success isCertain = possibleScopes(id, referrer, options, result);
232
233 // The default callback only assigns the result if it's certain.
234 // We can't have "possible" results after a certain one.
235 Q_ASSERT(isCertain == Success::Yes || result.result.isNull());
236
237 return result.result;
238 }
239
240 void insert(const QString &id, const QQmlJSScope::ConstPtr &scope)
241 {
242 Q_ASSERT(!id.isEmpty());
243 m_scopesById.insert(id, scope);
244 }
245
246 void clear() { m_scopesById.clear(); }
247
248 /*!
249 \internal
250 Returns \c true if \a id exists anywhere in the current document.
251 This is still allowed if the other occurrence is in a different (inline) component.
252 Check the return value of scope to know whether the id has already been assigned
253 in a givne scope.
254 */
255 bool existsAnywhereInDocument(const QString &id) const { return m_scopesById.contains(id); }
256
262
264 {
265 if (m_scopesById.size() == 0)
266 return {};
267
268 std::multimap<QQmlJSScope::ConstPtr, IdWithScope> componentRootsToIds;
269 for (auto it = m_scopesById.cbegin(), end = m_scopesById.cend(); it != end; ++it) {
270 possibleComponentRoots(
271 *it,
272 [&it, &componentRootsToIds](const QQmlJSScope::ConstPtr &componentRoot,
273 QQmlJSScope::IsComponentRoot) {
274 componentRootsToIds.insert({ componentRoot, { it.key(), it.value() } });
276 });
277 }
278 return componentRootsToIds;
279 }
280
281private:
282 template<typename F>
283 static CallbackResult possibleComponentRoots(const QQmlJSScope::ConstPtr &inner, F &&callback)
284 {
285 QQmlJSScope::ConstPtr scope = inner;
286 QQmlJSScope::IsComponentRoot maxConfidence = QQmlJSScope::IsComponentRoot::Yes;
287 while (scope) {
288 switch (scope->componentRootStatus()) {
289 case QQmlJSScope::IsComponentRoot::Maybe:
290 if (callback(scope, QQmlJSScope::IsComponentRoot::Maybe)
293 }
294 // If we've seen one "maybe", then there is no certainty anymore.
295 // The "maybe" ones are always processed first since the properties of unknown
296 // type are inside the elements they belong to.
297 maxConfidence = QQmlJSScope::IsComponentRoot::Maybe;
298 Q_FALLTHROUGH();
299 case QQmlJSScope::IsComponentRoot::No:
300 scope = scope->parentScope();
301 continue;
302 case QQmlJSScope::IsComponentRoot::Yes:
303 return callback(scope, maxConfidence);
304 }
305 }
306
308 }
309
310 static Confidence confidence(
311 QQmlJSScope::IsComponentRoot a, QQmlJSScope::IsComponentRoot b) {
312 switch (a) {
313 case QQmlJSScope::IsComponentRoot::Yes:
314 return b == QQmlJSScope::IsComponentRoot::Yes
315 ? Confidence::Certain
316 : Confidence::Possible;
317 case QQmlJSScope::IsComponentRoot::Maybe:
319 default:
320 break;
321 }
322
323 Q_UNREACHABLE_RETURN(Confidence::Certain);
324 }
325
326 bool isComponentVisible(const QQmlJSScope::ConstPtr &observed,
327 const QQmlJSScope::ConstPtr &observer,
328 QQmlJSScopesByIdOptions options) const
329 {
330 if (!m_componentsAreBound && !options.testAnyFlag(AssumeComponentsAreBound))
331 return observed == observer;
332
333 for (QQmlJSScope::ConstPtr scope = observer; scope; scope = scope->parentScope()) {
334 if (scope == observed)
335 return true;
336 }
337
338 return false;
339 }
340
341 QMultiHash<QString, QQmlJSScope::ConstPtr> m_scopesById;
342 bool m_componentsAreBound = false;
343 bool m_signaturesAreEnforced = true;
344 bool m_valueTypesAreAddressable = false;
345 bool m_valueTypesAreAssertable = false;
346};
347
348QT_END_NAMESPACE
349
350#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 valueTypesAreAssertable() const
bool existsAnywhereInDocument(const QString &id) const
void setValueTypesAreAssertable(bool assertable)
void setValueTypesAreAddressable(bool addressable)
std::multimap< QQmlJSScope::ConstPtr, IdWithScope > computeComponentRootsToIds()
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)
Combined button and popup list for selecting options.
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