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

source code of qtdeclarative/src/qml/qml/qqmlcontextdata_p.h