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

source code of qtdeclarative/src/quicktemplates/qquickdeferredexecute.cpp