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