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 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | namespace QtQuickPrivate { |
17 | |
18 | static 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 | |
27 | static 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 | |
80 | void 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 | |
102 | void 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 | |
109 | void 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 | |
138 | QT_END_NAMESPACE |
139 | |