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
qquickdeferredexecute.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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 reason:default
4
6
7#include <QtCore/qhash.h>
8#include <QtQml/qqmlengine.h>
9#include <QtQml/private/qqmldata_p.h>
10#include <QtQml/private/qqmlcomponent_p.h>
11#include <QtQml/private/qqmlobjectcreator_p.h>
12
13#include <private/qv4resolvedtypereference_p.h>
14
15#include <deque>
16
17QT_BEGIN_NAMESPACE
18
19/*!
20 \internal
21
22 For the history behind why these functions were introduced, see
23 the comments of QTBUG-50992, specifically
24 \l {https://bugreports.qt.io/browse/QTBUG-50992?focusedId=325677&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-325677}{this}.
25
26 The first commit to the QML engine to support deferred properties seems to
27 be 59b8d719d6122d86a4cc650911788cc4d778ce29.
28
29 The first commit to add support for it in Controls was
30 458eb65f730178bc93ba7b18f0e4dd2a13efad2e.
31
32 In short, deferred execution solved two issues:
33
34 \list 1
35 \li Incubation issues when using asynchronous loaders, AKA QTBUG-50992.
36 \li Performance issues from creating two items unnecessarily when a
37 styled control was customized, which is explained in more detail
38 in the commit message of 458eb65f730178bc93ba7b18f0e4dd2a13efad2e.
39 \endlist
40
41 \sa qmlExecuteDeferred
42*/
43
44namespace QtQuickPrivate {
45
46static void cancelDeferred(QQmlData *ddata, int propertyIndex)
47{
48 auto dit = ddata->deferredData.rbegin();
49 while (dit != ddata->deferredData.rend()) {
50 (*dit)->bindings.remove(propertyIndex);
51 ++dit;
52 }
53}
54
55static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState)
56{
57 QObject *object = property.object();
58 QQmlData *ddata = QQmlData::get(object);
59 Q_ASSERT(!ddata->deferredData.isEmpty());
60
61 if (!ddata->propertyCache)
62 ddata->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
63
64 int propertyIndex = property.index();
65 int wasInProgress = enginePriv->inProgressCreations;
66
67 /* we don't want deferred properties to suddenly depend on arbitrary
68 other properties which might have trigerred the construction of
69 objects as a consequence of a read.
70 */
71 auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
72 auto cleanup = qScopeGuard([&](){
73 QtPrivate::restoreBindingStatus(bindingStatus);
74 });
75
76 for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) {
77 QQmlData::DeferredData *deferData = *dit;
78
79 auto bindings = deferData->bindings;
80 auto range = bindings.equal_range(propertyIndex);
81 if (range.first == bindings.end())
82 continue;
83
84 QQmlComponentPrivate::ConstructionState state;
85 state.setCompletePending(true);
86
87 QQmlContextData *creationContext = nullptr;
88
89 state.initCreator(deferData->context->parent(), deferData->compilationUnit, creationContext, deferData->inlineComponentName);
90
91 enginePriv->inProgressCreations++;
92
93 std::deque<const QV4::CompiledData::Binding *> reversedBindings;
94 std::copy(range.first, range.second, std::front_inserter(reversedBindings));
95 state.creator()->beginPopulateDeferred(deferData->context);
96 for (const QV4::CompiledData::Binding *binding : reversedBindings)
97 state.creator()->populateDeferredBinding(property, deferData->deferredIdx, binding);
98 state.creator()->finalizePopulateDeferred();
99 state.appendCreatorErrors();
100
101 deferredState->push_back(std::move(state));
102
103 // Cleanup any remaining deferred bindings for this property, also in inner contexts,
104 // to avoid executing them later and overriding the property that was just populated.
105 cancelDeferred(ddata, propertyIndex);
106 break;
107 }
108
109 return enginePriv->inProgressCreations > wasInProgress;
110}
111
112void beginDeferred(QObject *object, const QString &property,
113 QQuickUntypedDeferredPointer *delegate, bool isOwnState, QQmlEngine *engine)
114{
115 QQmlData *data = QQmlData::get(object);
116 // If object is an attached object, its QQmlData won't have a context, hence why we provide
117 // the option to pass an engine explicitly.
118 if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object) && (data->context || engine)) {
119 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(
120 data->context && data->context->engine() ? data->context->engine() : engine);
121
122 QQmlComponentPrivate::DeferredState state;
123 if (beginDeferred(ep, QQmlProperty(object, property), &state)) {
124 if (QQmlComponentPrivate::DeferredState *delegateState = delegate->deferredState())
125 delegateState->swap(state);
126 } else if (isOwnState) {
127 delegate->clearDeferredState();
128 }
129
130 // Release deferred data for those compilation units that no longer have deferred bindings
131 data->releaseDeferredData();
132 } else if (isOwnState) {
133 delegate->clearDeferredState();
134 }
135}
136
137void cancelDeferred(QObject *object, const QString &property)
138{
139 QQmlData *data = QQmlData::get(object);
140 if (data)
141 cancelDeferred(data, QQmlProperty(object, property).index());
142}
143
144void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate,
145 QQmlEngine *engine)
146{
147 Q_UNUSED(property);
148 QQmlComponentPrivate::DeferredState *state = delegate->deferredState();
149 if (!state)
150 return;
151
152 QQmlData *data = QQmlData::get(object);
153 if (data && !data->wasDeleted(object)) {
154 /* we don't want deferred properties to suddenly depend on arbitrary
155 other properties which might have trigerred the construction of
156 objects as a consequence of a read.
157 */
158 auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
159 auto cleanup = qScopeGuard([&](){
160 QtPrivate::restoreBindingStatus(bindingStatus);
161 });
162
163 QQmlComponentPrivate::DeferredState localState = std::move(*state);
164 delegate->clearDeferredState();
165 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(
166 data->context && data->context->engine() ? data->context->engine() : engine);
167 QQmlComponentPrivate::completeDeferred(ep, &localState);
168 } else {
169 delegate->clearDeferredState();
170 }
171}
172
173} // QtQuickPrivate
174
175QT_END_NAMESPACE
Q_QUICKTEMPLATES2_EXPORT void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate, QQmlEngine *engine=nullptr)
static void cancelDeferred(QQmlData *ddata, int propertyIndex)
Q_QUICKTEMPLATES2_EXPORT void beginDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate, bool isOwnState, QQmlEngine *engine=nullptr)
Q_QUICKTEMPLATES2_EXPORT void cancelDeferred(QObject *object, const QString &property)
static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState)