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.cpp
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
5
6#include <QtQml/qqmlengine.h>
7#include <QtQml/private/qqmlcomponentattached_p.h>
8#include <QtQml/private/qqmljavascriptexpression_p.h>
9#include <QtQml/private/qqmlguardedcontextdata_p.h>
10
12
13void QQmlContextData::installContext(QQmlData *ddata, QQmlContextData::QmlObjectKind kind)
14{
15 Q_ASSERT(ddata);
16 if (kind == QQmlContextData::DocumentRoot) {
17 if (ddata->context) {
18 Q_ASSERT(ddata->context != this);
19 Q_ASSERT(ddata->outerContext);
20 Q_ASSERT(ddata->outerContext != this);
21 QQmlRefPointer<QQmlContextData> c = ddata->context;
22 while (QQmlRefPointer<QQmlContextData> linked = c->linkedContext())
23 c = linked;
24 c->setLinkedContext(this);
25 } else {
26 ddata->context = this;
27 }
28 ddata->ownContext.reset(ddata->context);
29 } else if (!ddata->context) {
30 ddata->context = this;
31 }
32
33 addOwnedObject(ddata);
34}
35
36QUrl QQmlContextData::resolvedUrl(const QUrl &src) const
37{
38 QUrl resolved;
39 if (src.isRelative() && !src.isEmpty()) {
40 const QUrl ownUrl = url();
41 if (ownUrl.isValid()) {
42 resolved = ownUrl.resolved(src);
43 } else {
44 for (QQmlRefPointer<QQmlContextData> ctxt = parent(); ctxt; ctxt = ctxt->parent()) {
45 const QUrl ctxtUrl = ctxt->url();
46 if (ctxtUrl.isValid()) {
47 resolved = ctxtUrl.resolved(src);
48 break;
49 }
50 }
51
52 if (m_engine && resolved.isEmpty())
53 resolved = m_engine->baseUrl().resolved(src);
54 }
55 } else {
56 resolved = src;
57 }
58
59 if (resolved.isEmpty()) //relative but no ctxt
60 return resolved;
61
62 return m_engine ? m_engine->interceptUrl(resolved, QQmlAbstractUrlInterceptor::UrlString)
63 : resolved;
64}
65
66void QQmlContextData::emitDestruction()
67{
68 if (!m_hasEmittedDestruction) {
69 m_hasEmittedDestruction = true;
70
71 // Emit the destruction signal - must be emitted before invalidate so that the
72 // context is still valid if bindings or resultant expression evaluation requires it
73 if (m_engine) {
74 while (m_componentAttacheds) {
75 QQmlComponentAttached *attached = m_componentAttacheds;
76 attached->removeFromList();
77 emit attached->destruction();
78 }
79
80 for (QQmlRefPointer<QQmlContextData> child = m_childContexts; !child.isNull(); child = child->m_nextChild)
81 child->emitDestruction();
82 }
83 }
84}
85
86void QQmlContextData::invalidate()
87{
88 emitDestruction();
89
90 while (m_childContexts) {
91 Q_ASSERT(m_childContexts != this);
92 m_childContexts->invalidate();
93 }
94
95 if (m_prevChild) {
96 *m_prevChild = m_nextChild;
97 if (m_nextChild) m_nextChild->m_prevChild = m_prevChild;
98 m_nextChild = nullptr;
99 m_prevChild = nullptr;
100 }
101
102 if (!m_hasWeakImportedScripts) { // invalidate might be called multiple times
103 if (m_engine && !m_importedScripts.isNullOrUndefined()) {
104 QV4::Scope scope(m_engine->handle());
105 QV4::ScopedValue val(scope, m_importedScripts.value());
106 m_importedScripts.~PersistentValue();
107 new (&m_weakImportedScripts) QV4::WeakValue();
108 m_weakImportedScripts.set(m_engine->handle(), val);
109 m_hasWeakImportedScripts = true;
110 } else {
111 // clear even if the value is null/undefined, in case it was set to explicit null/undefined
112 m_importedScripts.clear();
113 }
114 }
115
116 m_engine = nullptr;
117 clearParent();
118}
119
120void QQmlContextData::clearContextRecursively()
121{
122 clearContext();
123
124 for (auto ctxIt = m_childContexts; ctxIt; ctxIt = ctxIt->m_nextChild)
125 ctxIt->clearContextRecursively();
126
127 m_engine = nullptr;
128}
129
130void QQmlContextData::clearContext()
131{
132 emitDestruction();
133
134 QQmlJavaScriptExpression *expression = m_expressions;
135 while (expression) {
136 QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression;
137
138 expression->m_prevExpression = nullptr;
139 expression->m_nextExpression = nullptr;
140
141 expression->setContext(nullptr);
142
143 expression = nextExpression;
144 }
145 m_expressions = nullptr;
146}
147
148QQmlContextData::~QQmlContextData()
149{
150 Q_ASSERT(refCount() == 0);
151
152 // avoid recursion
153 addref();
154 if (!m_hasWeakImportedScripts) {
155 // avoid busy work in invalidate – we don't want to construct a weak value
156 // just to throw it away afterwards
157 m_importedScripts.clear();
158 }
159 invalidate();
160 if (m_hasWeakImportedScripts)
161 m_weakImportedScripts.~WeakValue();
162 else
163 m_importedScripts.~PersistentValue();
164 m_linkedContext.reset();
165
166 Q_ASSERT(refCount() == 1);
167 clearContext();
168 Q_ASSERT(refCount() == 1);
169
170 while (m_ownedObjects) {
171 QQmlData *co = m_ownedObjects;
172 m_ownedObjects = m_ownedObjects->nextContextObject;
173
174 if (co->context == this)
175 co->context = nullptr;
176 co->outerContext = nullptr;
177 co->nextContextObject = nullptr;
178 co->prevContextObject = nullptr;
179 }
180 Q_ASSERT(refCount() == 1);
181
182 QQmlGuardedContextData *contextGuard = m_contextGuards;
183 while (contextGuard) {
184 // TODO: Is this dead code? Why?
185 QQmlGuardedContextData *next = contextGuard->next();
186 contextGuard->setContextData({});
187 contextGuard = next;
188 }
189 m_contextGuards = nullptr;
190 Q_ASSERT(refCount() == 1);
191
192 delete [] m_idValues;
193 m_idValues = nullptr;
194
195 Q_ASSERT(refCount() == 1);
196 if (m_publicContext)
197 delete m_publicContext;
198
199 Q_ASSERT(refCount() == 1);
200}
201
202void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression)
203{
204 QQmlJavaScriptExpression::DeleteWatcher w(expression);
205
206 if (expression->m_nextExpression)
207 refreshExpressionsRecursive(expression->m_nextExpression);
208
209 if (!w.wasDeleted())
210 expression->refresh();
211}
212
213void QQmlContextData::refreshExpressionsRecursive(bool isGlobal)
214{
215 // For efficiency, we try and minimize the number of guards we have to create
216 if (hasExpressionsToRun(isGlobal) && (m_nextChild || m_childContexts)) {
217 QQmlGuardedContextData guard(this);
218
219 if (m_childContexts)
220 m_childContexts->refreshExpressionsRecursive(isGlobal);
221
222 if (guard.isNull()) return;
223
224 if (m_nextChild)
225 m_nextChild->refreshExpressionsRecursive(isGlobal);
226
227 if (guard.isNull()) return;
228
229 if (hasExpressionsToRun(isGlobal))
230 refreshExpressionsRecursive(m_expressions);
231
232 } else if (hasExpressionsToRun(isGlobal)) {
233 refreshExpressionsRecursive(m_expressions);
234 } else if (m_nextChild && m_childContexts) {
235 QQmlGuardedContextData guard(this);
236 m_childContexts->refreshExpressionsRecursive(isGlobal);
237 if (!guard.isNull() && m_nextChild)
238 m_nextChild->refreshExpressionsRecursive(isGlobal);
239 } else if (m_nextChild) {
240 m_nextChild->refreshExpressionsRecursive(isGlobal);
241 } else if (m_childContexts) {
242 m_childContexts->refreshExpressionsRecursive(isGlobal);
243 }
244}
245
246// Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
247// context-tree dependent caches in the expressions, and should occur every time the context tree
248// *structure* (not values) changes.
249void QQmlContextData::refreshExpressions()
250{
251 bool isGlobal = (m_parent == nullptr);
252
253 // For efficiency, we try and minimize the number of guards we have to create
254 if (hasExpressionsToRun(isGlobal) && m_childContexts) {
255 QQmlGuardedContextData guard(this);
256 m_childContexts->refreshExpressionsRecursive(isGlobal);
257 if (!guard.isNull() && hasExpressionsToRun(isGlobal))
258 refreshExpressionsRecursive(m_expressions);
259 } else if (hasExpressionsToRun(isGlobal)) {
260 refreshExpressionsRecursive(m_expressions);
261 } else if (m_childContexts) {
262 m_childContexts->refreshExpressionsRecursive(isGlobal);
263 }
264}
265
266void QQmlContextData::addOwnedObject(QQmlData *data)
267{
268 if (data->outerContext) {
269 if (data->nextContextObject)
270 data->nextContextObject->prevContextObject = data->prevContextObject;
271 if (data->prevContextObject)
272 *data->prevContextObject = data->nextContextObject;
273 else if (data->outerContext->m_ownedObjects == data)
274 data->outerContext->m_ownedObjects = data->nextContextObject;
275 }
276
277 data->outerContext = this;
278
279 data->nextContextObject = m_ownedObjects;
280 if (data->nextContextObject)
281 data->nextContextObject->prevContextObject = &data->nextContextObject;
282 data->prevContextObject = &m_ownedObjects;
283 m_ownedObjects = data;
284}
285
286void QQmlContextData::setIdValue(int idx, QObject *obj)
287{
288 m_idValues[idx] = obj;
289 m_idValues[idx].setContext(this);
290}
291
292QString QQmlContextData::findObjectId(const QObject *obj) const
293{
294 for (int ii = 0; ii < m_idValueCount; ii++) {
295 if (m_idValues[ii] == obj)
296 return propertyName(ii);
297 }
298
299 const QVariant objVariant = QVariant::fromValue(obj);
300 if (m_publicContext) {
301 QQmlContextPrivate *p = QQmlContextPrivate::get(m_publicContext);
302 for (int ii = 0; ii < p->numPropertyValues(); ++ii)
303 if (p->propertyValue(ii) == objVariant)
304 return propertyName(ii);
305 }
306
307 if (m_contextObject) {
308 // This is expensive, but nameForObject should really mirror contextProperty()
309 for (const QMetaObject *metaObject = m_contextObject->metaObject();
310 metaObject; metaObject = metaObject->superClass()) {
311 for (int i = metaObject->propertyOffset(), end = metaObject->propertyCount();
312 i != end; ++i) {
313 const QMetaProperty prop = metaObject->property(i);
314 if (prop.metaType().flags() & QMetaType::PointerToQObject
315 && prop.read(m_contextObject) == objVariant) {
316 return QString::fromUtf8(prop.name());
317 }
318 }
319 }
320 }
321
322 return QString();
323}
324
325void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex)
326{
327 m_typeCompilationUnit = unit;
328 m_componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex;
329 Q_ASSERT(!m_idValues);
330 m_idValueCount = m_typeCompilationUnit->objectAt(m_componentObjectIndex)
331 ->nNamedObjectsInComponent;
332 if (m_idValueCount > 0)
333 m_idValues = new ContextGuard[m_idValueCount];
334}
335
336void QQmlContextData::addComponentAttached(QQmlComponentAttached *attached)
337{
338 attached->insertIntoList(&m_componentAttacheds);
339}
340
341void QQmlContextData::addExpression(QQmlJavaScriptExpression *expression)
342{
343 expression->insertIntoList(&m_expressions);
344}
345
346void QQmlContextData::initPropertyNames() const
347{
348 if (m_typeCompilationUnit)
349 m_propertyNameCache = m_typeCompilationUnit->namedObjectsPerComponent(m_componentObjectIndex);
350 else
351 m_propertyNameCache = QV4::IdentifierHash(m_engine->handle());
352 Q_ASSERT(!m_propertyNameCache.isEmpty());
353}
354
355QUrl QQmlContextData::url() const
356{
357 if (m_typeCompilationUnit)
358 return m_typeCompilationUnit->finalUrl();
359 return m_baseUrl;
360}
361
362QString QQmlContextData::urlString() const
363{
364 if (m_typeCompilationUnit)
365 return m_typeCompilationUnit->finalUrlString();
366 return m_baseUrlString;
367}
368
369QT_END_NAMESPACE