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