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 | |
26 | QT_BEGIN_NAMESPACE |
27 | |
28 | class QQmlComponentAttached; |
29 | class QQmlGuardedContextData; |
30 | class QQmlJavaScriptExpression; |
31 | class QQmlIncubatorPrivate; |
32 | |
33 | class Q_QML_PRIVATE_EXPORT QQmlContextData |
34 | { |
35 | public: |
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 *() const { return m_hasExtraObject ? m_extraObject : nullptr; } |
245 | void (QObject *) |
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 | |
273 | private: |
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 :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 *; |
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 | |
422 | QQmlContextData::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 | |
439 | bool QQmlContextData::ContextGuard::wasSet() const |
440 | { |
441 | return m_context.tag() == ObjectWasSet; |
442 | } |
443 | |
444 | QT_END_NAMESPACE |
445 | |
446 | #endif // QQMLCONTEXTDATA_P_H |
447 | |