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
qqmlcontextdata_p.h
Go to the documentation of this file.
1// Copyright (C) 2020 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// Qt-Security score:significant
4
5#ifndef QQMLCONTEXTDATA_P_H
6#define QQMLCONTEXTDATA_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 <QtQml/private/qtqmlglobal_p.h>
20#include <QtQml/private/qqmlcontext_p.h>
21#include <QtQml/private/qqmlguard_p.h>
22#include <QtQml/private/qqmltypenamecache_p.h>
23#include <QtQml/private/qqmlnotifier_p.h>
24#include <QtQml/private/qv4identifierhash_p.h>
25#include <QtQml/private/qv4executablecompilationunit_p.h>
26
27QT_BEGIN_NAMESPACE
28
29class QQmlComponentAttached;
30class QQmlGuardedContextData;
31class QQmlJavaScriptExpression;
33
34class Q_QML_EXPORT QQmlContextData
35{
36public:
37 static QQmlRefPointer<QQmlContextData> createRefCounted(
38 const QQmlRefPointer<QQmlContextData> &parent)
39 {
40 return QQmlRefPointer<QQmlContextData>(new QQmlContextData(RefCounted, nullptr, parent),
41 QQmlRefPointer<QQmlContextData>::Adopt);
42 }
43
44 // Owned by the parent. When the parent is reset to nullptr, it will be deref'd.
45 static QQmlRefPointer<QQmlContextData> createChild(
46 const QQmlRefPointer<QQmlContextData> &parent)
47 {
48 Q_ASSERT(!parent.isNull());
49 return QQmlRefPointer<QQmlContextData>(new QQmlContextData(OwnedByParent, nullptr, parent));
50 }
51
52 void addref() const { ++m_refCount; }
53 void release() const { if (--m_refCount == 0) delete this; }
54 int count() const { return m_refCount; }
55 int refCount() const { return m_refCount; }
56
57 QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit() const
58 {
59 return m_typeCompilationUnit;
60 }
61 void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
62 int subComponentIndex);
63
64 static QQmlRefPointer<QQmlContextData> get(QQmlContext *context) {
65 return QQmlContextPrivate::get(context)->m_data;
66 }
67
68 void emitDestruction();
69 void clearExpressions();
70 void clearContextRecursively();
71 void clearChildrenAndSiblings();
72 void clearImportedScripts();
73 void clearOwnedObjects();
74 void clearContextGuards();
75 void clearIdValues();
76 void invalidate();
77
78 bool isValid() const
79 {
80 return m_engine && (!m_isInternal || !m_contextObject
81 || !QObjectPrivate::get(m_contextObject)->wasDeleted);
82 }
83
84 bool isInternal() const { return m_isInternal; }
85 void setInternal(bool isInternal) { m_isInternal = isInternal; }
86
87 bool isJSContext() const { return m_isJSContext; }
88 void setJSContext(bool isJSContext) { m_isJSContext = isJSContext; }
89
90 bool isPragmaLibraryContext() const { return m_isPragmaLibraryContext; }
91 void setPragmaLibraryContext(bool library) { m_isPragmaLibraryContext = library; }
92
93 QQmlRefPointer<QQmlContextData> parent() const { return m_parent; }
94 void clearParent()
95 {
96 if (!m_parent)
97 return;
98
99 m_parent = nullptr;
100 if (m_ownedByParent) {
101 m_ownedByParent = false;
102 release();
103 }
104 }
105
106 void refreshExpressions();
107
108 void addOwnedObject(QQmlData *ownedObject);
109 QQmlData *ownedObjects() const { return m_ownedObjects; }
110 void setOwnedObjects(QQmlData *ownedObjects) { m_ownedObjects = ownedObjects; }
111
112 enum QmlObjectKind {
113 OrdinaryObject,
114 DocumentRoot,
115 };
116 void installContext(QQmlData *ddata, QmlObjectKind kind);
117
118 QUrl resolvedUrl(const QUrl &) const;
119
120 // My containing QQmlContext. If isInternal is true this owns publicContext.
121 // If internal is false publicContext owns this.
122 QQmlContext *asQQmlContext()
123 {
124 if (!m_publicContext)
125 m_publicContext = new QQmlContext(*new QQmlContextPrivate(this));
126 return m_publicContext;
127 }
128
129 QQmlContextPrivate *asQQmlContextPrivate()
130 {
131 return QQmlContextPrivate::get(asQQmlContext());
132 }
133
134 QObject *contextObject() const { return m_contextObject; }
135 void setContextObject(QObject *contextObject) { m_contextObject = contextObject; }
136
137 template<typename HandleSelf, typename HandleLinked>
138 void deepClearContextObject(
139 QObject *contextObject, HandleSelf &&handleSelf, HandleLinked &&handleLinked) {
140 for (QQmlContextData *lc = m_linkedContext.data(); lc; lc = lc->m_linkedContext.data()) {
141 handleLinked(lc);
142 if (lc->m_contextObject == contextObject)
143 lc->m_contextObject = nullptr;
144 }
145
146 handleSelf(this);
147 if (m_contextObject == contextObject)
148 m_contextObject = nullptr;
149 }
150
151 void deepClearContextObject(QObject *contextObject)
152 {
153 deepClearContextObject(
154 contextObject,
155 [](QQmlContextData *self) { self->emitDestruction(); },
156 [](QQmlContextData *){});
157 }
158
159 QQmlEngine *engine() const { return m_engine; }
160 void setEngine(QQmlEngine *engine) { m_engine = engine; }
161
162 QQmlContext *publicContext() const { return m_publicContext; }
163 void clearPublicContext()
164 {
165 if (!m_publicContext)
166 return;
167
168 m_publicContext = nullptr;
169 if (m_ownedByPublicContext) {
170 m_ownedByPublicContext = false;
171 release();
172 }
173 }
174
175 int propertyIndex(const QString &name) const
176 {
177 ensurePropertyNames();
178 return m_propertyNameCache.value(name);
179 }
180
181 int propertyIndex(QV4::Heap::String *name) const
182 {
183 ensurePropertyNames();
184 return m_propertyNameCache.value(name);
185 }
186
187 QString propertyName(int index) const
188 {
189 ensurePropertyNames();
190 return m_propertyNameCache.key<QString>(index);
191 }
192
193 void addPropertyNameAndIndex(const QString &name, int index)
194 {
195 Q_ASSERT(m_propertyNameCache.isValid());
196 m_propertyNameCache.add(name, index);
197 }
198
199 void setExpressions(QQmlJavaScriptExpression *expressions) { m_expressions = expressions; }
200 QQmlJavaScriptExpression *takeExpressions()
201 {
202 QQmlJavaScriptExpression *expressions = m_expressions;
203 m_expressions = nullptr;
204 return expressions;
205 }
206
207 void setChildContexts(const QQmlRefPointer<QQmlContextData> &childContexts)
208 {
209 m_childContexts = childContexts.data();
210 }
211 QQmlRefPointer<QQmlContextData> childContexts() const { return m_childContexts; }
212 QQmlRefPointer<QQmlContextData> takeChildContexts()
213 {
214 QQmlRefPointer<QQmlContextData> childContexts = m_childContexts;
215 m_childContexts = nullptr;
216 return childContexts;
217 }
218 QQmlRefPointer<QQmlContextData> nextChild() const { return m_nextChild; }
219
220 int numIdValues() const { return m_idValueCount; }
221 void setIdValue(int index, QObject *idValue);
222 bool isIdValueSet(int index) const { return m_idValues[index].wasSet(); }
223 QQmlNotifier *idValueBindings(int index) const { return m_idValues[index].bindings(); }
224 QObject *idValue(int index) const { return m_idValues[index].data(); }
225
226 // Return the outermost id for obj, if any.
227 QString findObjectId(const QObject *obj) const;
228
229 // url() and urlString() prefer the CU's URL over explicitly set baseUrls. They
230 // don't search the context hierarchy.
231 // baseUrl() and baseUrlString() search the context hierarchy and prefer explicit
232 // base URLs over CU Urls.
233
234 QUrl url() const;
235 QString urlString() const;
236
237 void setBaseUrlString(const QString &baseUrlString) { m_baseUrlString = baseUrlString; }
238 QString baseUrlString() const
239 {
240 for (const QQmlContextData *data = this; data; data = data->m_parent) {
241 if (!data->m_baseUrlString.isEmpty())
242 return data->m_baseUrlString;
243 if (data->m_typeCompilationUnit)
244 return data->m_typeCompilationUnit->finalUrlString();
245 }
246 return QString();
247 }
248
249 void setBaseUrl(const QUrl &baseUrl) { m_baseUrl = baseUrl; }
250 QUrl baseUrl() const
251 {
252 for (const QQmlContextData *data = this; data; data = data->m_parent) {
253 if (!data->m_baseUrl.isEmpty())
254 return data->m_baseUrl;
255 if (data->m_typeCompilationUnit)
256 return data->m_typeCompilationUnit->finalUrl();
257 }
258 return QUrl();
259 }
260
261 QQmlRefPointer<QQmlTypeNameCache> imports() const { return m_imports; }
262 void setImports(const QQmlRefPointer<QQmlTypeNameCache> &imports) { m_imports = imports; }
263
264 QQmlIncubatorPrivate *incubator() const { return m_hasExtraObject ? nullptr : m_incubator; }
265 void setIncubator(QQmlIncubatorPrivate *incubator)
266 {
267 Q_ASSERT(!m_hasExtraObject || m_extraObject == nullptr);
268 m_hasExtraObject = false;
269 m_incubator = incubator;
270 }
271
272 QObject *extraObject() const { return m_hasExtraObject ? m_extraObject : nullptr; }
273 void setExtraObject(QObject *extraObject)
274 {
275 Q_ASSERT(m_hasExtraObject || m_incubator == nullptr);
276 m_hasExtraObject = true;
277 m_extraObject = extraObject;
278 }
279
280 bool isRootObjectInCreation() const { return m_isRootObjectInCreation; }
281 void setRootObjectInCreation(bool rootInCreation) { m_isRootObjectInCreation = rootInCreation; }
282
283 QV4::ReturnedValue importedScripts() const
284 {
285 if (m_hasWeakImportedScripts)
286 return m_weakImportedScripts.value();
287 else
288 return m_importedScripts.value();
289 }
290 void setImportedScripts(QV4::ExecutionEngine *engine, QV4::Value scripts) {
291 // setImportedScripts should not be called on an invalidated context
292 Q_ASSERT(!m_hasWeakImportedScripts);
293 m_importedScripts.set(engine, scripts);
294 }
295
296 /*
297 we can safely pass a ReturnedValue here, as setImportedScripts will directly store
298 scripts in a persistentValue, without any intermediate allocation that could trigger
299 a gc run
300 */
301 void setImportedScripts(QV4::ExecutionEngine *engine, QV4::ReturnedValue scripts) {
302 // setImportedScripts should not be called on an invalidated context
303 Q_ASSERT(!m_hasWeakImportedScripts);
304 m_importedScripts.set(engine, scripts);
305 }
306
307 QQmlRefPointer<QQmlContextData> linkedContext() const { return m_linkedContext; }
308 void setLinkedContext(const QQmlRefPointer<QQmlContextData> &context) { m_linkedContext = context; }
309
310 bool hasUnresolvedNames() const { return m_unresolvedNames; }
311 void setUnresolvedNames(bool hasUnresolvedNames) { m_unresolvedNames = hasUnresolvedNames; }
312
313 QQmlComponentAttached *componentAttacheds() const { return m_componentAttacheds; }
314 void addComponentAttached(QQmlComponentAttached *attached);
315
316 void addExpression(QQmlJavaScriptExpression *expression);
317
318 bool valueTypesAreAddressable() const {
319 return m_typeCompilationUnit && m_typeCompilationUnit->valueTypesAreAddressable();
320 }
321
322 bool valueTypesAreAssertable() const {
323 return m_typeCompilationUnit && m_typeCompilationUnit->valueTypesAreAssertable();
324 }
325
326private:
327 friend class QQmlGuardedContextData;
328 friend class QQmlContextPrivate;
329
330 enum Ownership {
331 RefCounted,
332 OwnedByParent,
333 OwnedByPublicContext
334 };
335
336 // id guards
337 struct ContextGuard : public QQmlGuard<QObject>
338 {
339 enum Tag {
340 NoTag,
341 ObjectWasSet
342 };
343
344 inline ContextGuard() : QQmlGuard<QObject>(&ContextGuard::objectDestroyedImpl, nullptr), m_context(nullptr) {}
345 inline ContextGuard &operator=(QObject *obj);
346
347 inline bool wasSet() const;
348
349 QQmlNotifier *bindings() { return &m_bindings; }
350 void setContext(const QQmlRefPointer<QQmlContextData> &context)
351 {
352 m_context = context.data();
353 }
354
355 private:
356 inline static void objectDestroyedImpl(QQmlGuardImpl *);
357 // Not refcounted, as it always belongs to the QQmlContextData.
358 QTaggedPointer<QQmlContextData, Tag> m_context;
359 QQmlNotifier m_bindings;
360 };
361
362 // It's OK to pass a half-created publicContext here. We will not dereference it during
363 // construction.
364 QQmlContextData(
365 Ownership ownership, QQmlContext *publicContext,
366 const QQmlRefPointer<QQmlContextData> &parent, QQmlEngine *engine = nullptr)
367 : m_parent(parent.data()),
368 m_engine(engine ? engine : (parent.isNull() ? nullptr : parent->engine())),
369 m_isInternal(false), m_isJSContext(false), m_isPragmaLibraryContext(false),
370 m_unresolvedNames(false), m_hasEmittedDestruction(false), m_isRootObjectInCreation(false),
371 m_ownedByParent(ownership == OwnedByParent),
372 m_ownedByPublicContext(ownership == OwnedByPublicContext), m_hasExtraObject(false),
373 m_hasWeakImportedScripts(false), m_dummy(0), m_publicContext(publicContext), m_incubator(nullptr)
374 {
375 Q_ASSERT(!m_ownedByParent || !m_ownedByPublicContext);
376 if (!m_parent)
377 return;
378
379 m_nextChild = m_parent->m_childContexts;
380 if (m_nextChild)
381 m_nextChild->m_prevChild = &m_nextChild;
382 m_prevChild = &m_parent->m_childContexts;
383 m_parent->m_childContexts = this;
384 }
385
386 ~QQmlContextData();
387
388 bool hasExpressionsToRun(bool isGlobalRefresh) const
389 {
390 return m_expressions && (!isGlobalRefresh || m_unresolvedNames);
391 }
392
393 void refreshExpressionsRecursive(bool isGlobal);
394 void refreshExpressionsRecursive(QQmlJavaScriptExpression *);
395 void initPropertyNames() const;
396
397 void ensurePropertyNames() const
398 {
399 if (!m_propertyNameCache.isValid())
400 initPropertyNames();
401 Q_ASSERT(m_propertyNameCache.isValid());
402 }
403
404 // My parent context and engine
405 QQmlContextData *m_parent = nullptr;
406 QQmlEngine *m_engine = nullptr;
407
408 mutable quint32 m_refCount = 1;
409 quint32 m_isInternal:1;
410 quint32 m_isJSContext:1;
411 quint32 m_isPragmaLibraryContext:1;
412 quint32 m_unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name
413 quint32 m_hasEmittedDestruction:1;
414 quint32 m_isRootObjectInCreation:1;
415 quint32 m_ownedByParent:1;
416 quint32 m_ownedByPublicContext:1;
417 quint32 m_hasExtraObject:1; // used in QQmlDelegateModelItem::dataForObject to find the corresponding QQmlDelegateModelItem of an object
418 quint32 m_hasWeakImportedScripts:1;
419 Q_DECL_UNUSED_MEMBER quint32 m_dummy:22;
420 QQmlContext *m_publicContext = nullptr;
421
422 union {
423 // The incubator that is constructing this context if any
424 QQmlIncubatorPrivate *m_incubator;
425 // a pointer to extra data, currently only used in QQmlDelegateModel
426 QObject *m_extraObject;
427 };
428
429 // Compilation unit for contexts that belong to a compiled type.
430 QQmlRefPointer<QV4::ExecutableCompilationUnit> m_typeCompilationUnit;
431
432 // object index in CompiledData::Unit to component that created this context
433 int m_componentObjectIndex = -1;
434
435 // flag indicates whether the context owns the cache (after mutation) or not.
436 mutable QV4::IdentifierHash m_propertyNameCache;
437
438 // Context object
439 QObject *m_contextObject = nullptr;
440
441 // Any script blocks that exist on this context
442 union {
443 /* an invalidated context transitions from a strong reference to the scripts
444 to a weak one, so that the context doesn't needlessly holds on to the scripts,
445 but closures can still access them if needed
446 */
447 QV4::PersistentValue m_importedScripts = {}; // This is a JS Array
448 QV4::WeakValue m_weakImportedScripts;
449 };
450
451 QUrl m_baseUrl;
452 QString m_baseUrlString;
453
454 // List of imports that apply to this context
455 QQmlRefPointer<QQmlTypeNameCache> m_imports;
456
457 // My children, not refcounted as that would create cyclic references
458 QQmlContextData *m_childContexts = nullptr;
459
460 // My peers in parent's childContexts list; not refcounted
461 QQmlContextData *m_nextChild = nullptr;
462 QQmlContextData **m_prevChild = nullptr;
463
464 // Expressions that use this context
465 QQmlJavaScriptExpression *m_expressions = nullptr;
466
467 // Doubly-linked list of objects that are owned by this context
468 QQmlData *m_ownedObjects = nullptr;
469
470 // Doubly-linked list of context guards (XXX merge with contextObjects)
471 QQmlGuardedContextData *m_contextGuards = nullptr;
472
473 ContextGuard *m_idValues = nullptr;
474 int m_idValueCount = 0;
475
476 // Linked contexts. this owns linkedContext.
477 QQmlRefPointer<QQmlContextData> m_linkedContext;
478
479 // Linked list of uses of the Component attached property in this context
480 QQmlComponentAttached *m_componentAttacheds = nullptr;
481};
482
483QQmlContextData::ContextGuard &QQmlContextData::ContextGuard::operator=(QObject *obj)
484{
485 QQmlGuard<QObject>::operator=(obj);
486 m_context.setTag(ObjectWasSet);
487 m_bindings.notify(); // For alias connections
488 return *this;
489}
490
491 void QQmlContextData::ContextGuard::objectDestroyedImpl(QQmlGuardImpl *impl)
492{
493 auto This = static_cast<QQmlContextData::ContextGuard *>(impl);
494 if (QObject *contextObject = This->m_context->contextObject()) {
495 if (!QObjectPrivate::get(contextObject)->wasDeleted)
496 This->m_bindings.notify();
497 }
498}
499
500bool QQmlContextData::ContextGuard::wasSet() const
501{
502 return m_context.tag() == ObjectWasSet;
503}
504
505QT_END_NAMESPACE
506
507#endif // QQMLCONTEXTDATA_P_H
friend class QQmlIncubatorPrivate
Combined button and popup list for selecting options.