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 <deque>
13
14QT_BEGIN_NAMESPACE
15
16namespace QtQuickPrivate {
17
18static void cancelDeferred(QQmlData *ddata, int propertyIndex)
19{
20 auto dit = ddata->deferredData.rbegin();
21 while (dit != ddata->deferredData.rend()) {
22 (*dit)->bindings.remove(key: propertyIndex);
23 ++dit;
24 }
25}
26
27static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState)
28{
29 QObject *object = property.object();
30 QQmlData *ddata = QQmlData::get(object);
31 Q_ASSERT(!ddata->deferredData.isEmpty());
32
33 int propertyIndex = property.index();
34 int wasInProgress = enginePriv->inProgressCreations;
35
36 /* we don't want deferred properties to suddenly depend on arbitrary
37 other properties which might have trigerred the construction of
38 objects as a consequence of a read.
39 */
40 auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
41 auto cleanup = qScopeGuard(f: [&](){
42 QtPrivate::restoreBindingStatus(status: bindingStatus);
43 });
44
45 for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) {
46 QQmlData::DeferredData *deferData = *dit;
47
48 auto bindings = deferData->bindings;
49 auto range = bindings.equal_range(key: propertyIndex);
50 if (range.first == bindings.end())
51 continue;
52
53 QQmlComponentPrivate::ConstructionState state;
54 state.setCompletePending(true);
55
56 QQmlContextData *creationContext = nullptr;
57 state.initCreator(parentContext: deferData->context->parent(), compilationUnit: deferData->compilationUnit, creationContext);
58
59 enginePriv->inProgressCreations++;
60
61 std::deque<const QV4::CompiledData::Binding *> reversedBindings;
62 std::copy(first: range.first, last: range.second, result: std::front_inserter(x&: reversedBindings));
63 state.creator()->beginPopulateDeferred(context: deferData->context);
64 for (const QV4::CompiledData::Binding *binding : reversedBindings)
65 state.creator()->populateDeferredBinding(qmlProperty: property, deferredIndex: deferData->deferredIdx, binding);
66 state.creator()->finalizePopulateDeferred();
67 state.appendCreatorErrors();
68
69 deferredState->push_back(x: std::move(state));
70
71 // Cleanup any remaining deferred bindings for this property, also in inner contexts,
72 // to avoid executing them later and overriding the property that was just populated.
73 cancelDeferred(ddata, propertyIndex);
74 break;
75 }
76
77 return enginePriv->inProgressCreations > wasInProgress;
78}
79
80void beginDeferred(QObject *object, const QString &property,
81 QQuickUntypedDeferredPointer *delegate, bool isOwnState)
82{
83 QQmlData *data = QQmlData::get(object);
84 if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object) && data->context) {
85 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine());
86
87 QQmlComponentPrivate::DeferredState state;
88 if (beginDeferred(enginePriv: ep, property: QQmlProperty(object, property), deferredState: &state)) {
89 if (QQmlComponentPrivate::DeferredState *delegateState = delegate->deferredState())
90 delegateState->swap(x&: state);
91 } else if (isOwnState) {
92 delegate->clearDeferredState();
93 }
94
95 // Release deferred data for those compilation units that no longer have deferred bindings
96 data->releaseDeferredData();
97 } else if (isOwnState) {
98 delegate->clearDeferredState();
99 }
100}
101
102void cancelDeferred(QObject *object, const QString &property)
103{
104 QQmlData *data = QQmlData::get(object);
105 if (data)
106 cancelDeferred(ddata: data, propertyIndex: QQmlProperty(object, property).index());
107}
108
109void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate)
110{
111 Q_UNUSED(property);
112 QQmlComponentPrivate::DeferredState *state = delegate->deferredState();
113 if (!state)
114 return;
115
116 QQmlData *data = QQmlData::get(object);
117 if (data && !data->wasDeleted(object)) {
118 /* we don't want deferred properties to suddenly depend on arbitrary
119 other properties which might have trigerred the construction of
120 objects as a consequence of a read.
121 */
122 auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
123 auto cleanup = qScopeGuard(f: [&](){
124 QtPrivate::restoreBindingStatus(status: bindingStatus);
125 });
126
127 QQmlComponentPrivate::DeferredState localState = std::move(*state);
128 delegate->clearDeferredState();
129 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine());
130 QQmlComponentPrivate::completeDeferred(enginePriv: ep, deferredState: &localState);
131 } else {
132 delegate->clearDeferredState();
133 }
134}
135
136} // QtQuickPrivate
137
138QT_END_NAMESPACE
139

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