aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates/qquickdeferredexecute.cpp
blob: 19870a5636e144ff80dbe286cdd98f73b892edbc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qquickdeferredexecute_p_p.h"

#include <QtCore/qhash.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/private/qqmldata_p.h>
#include <QtQml/private/qqmlcomponent_p.h>
#include <QtQml/private/qqmlobjectcreator_p.h>

#include <deque>

QT_BEGIN_NAMESPACE

namespace QtQuickPrivate {

static void cancelDeferred(QQmlData *ddata, int propertyIndex)
{
    auto dit = ddata->deferredData.rbegin();
    while (dit != ddata->deferredData.rend()) {
        (*dit)->bindings.remove(propertyIndex);
        ++dit;
    }
}

static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState)
{
    QObject *object = property.object();
    QQmlData *ddata = QQmlData::get(object);
    Q_ASSERT(!ddata->deferredData.isEmpty());

    if (!ddata->propertyCache)
        ddata->propertyCache = QQmlMetaType::propertyCache(object->metaObject());

    int propertyIndex = property.index();
    int wasInProgress = enginePriv->inProgressCreations;

    /* we don't want deferred properties to suddenly depend on arbitrary
       other properties which might have trigerred the construction of
       objects as a consequence of a read.
     */
    auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
    auto cleanup = qScopeGuard([&](){
        QtPrivate::restoreBindingStatus(bindingStatus);
    });

    for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) {
        QQmlData::DeferredData *deferData = *dit;

        auto bindings = deferData->bindings;
        auto range = bindings.equal_range(propertyIndex);
        if (range.first == bindings.end())
            continue;

        QQmlComponentPrivate::ConstructionState state;
        state.setCompletePending(true);

        QQmlContextData *creationContext = nullptr;
        state.initCreator(deferData->context->parent(), deferData->compilationUnit, creationContext);

        enginePriv->inProgressCreations++;

        std::deque<const QV4::CompiledData::Binding *> reversedBindings;
        std::copy(range.first, range.second, std::front_inserter(reversedBindings));
        state.creator()->beginPopulateDeferred(deferData->context);
        for (const QV4::CompiledData::Binding *binding : reversedBindings)
            state.creator()->populateDeferredBinding(property, deferData->deferredIdx, binding);
        state.creator()->finalizePopulateDeferred();
        state.appendCreatorErrors();

        deferredState->push_back(std::move(state));

        // Cleanup any remaining deferred bindings for this property, also in inner contexts,
        // to avoid executing them later and overriding the property that was just populated.
        cancelDeferred(ddata, propertyIndex);
        break;
    }

    return enginePriv->inProgressCreations > wasInProgress;
}

void beginDeferred(QObject *object, const QString &property,
                   QQuickUntypedDeferredPointer *delegate, bool isOwnState)
{
    QQmlData *data = QQmlData::get(object);
    if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object) && data->context) {
        QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());

        QQmlComponentPrivate::DeferredState state;
        if (beginDeferred(ep, QQmlProperty(object, property), &state)) {
            if (QQmlComponentPrivate::DeferredState *delegateState = delegate->deferredState())
                delegateState->swap(state);
        } else if (isOwnState) {
            delegate->clearDeferredState();
        }

        // Release deferred data for those compilation units that no longer have deferred bindings
        data->releaseDeferredData();
    } else if (isOwnState) {
        delegate->clearDeferredState();
    }
}

void cancelDeferred(QObject *object, const QString &property)
{
    QQmlData *data = QQmlData::get(object);
    if (data)
        cancelDeferred(data, QQmlProperty(object, property).index());
}

void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate)
{
    Q_UNUSED(property);
    QQmlComponentPrivate::DeferredState *state = delegate->deferredState();
    if (!state)
        return;

    QQmlData *data = QQmlData::get(object);
    if (data && !data->wasDeleted(object)) {
        /* we don't want deferred properties to suddenly depend on arbitrary
           other properties which might have trigerred the construction of
           objects as a consequence of a read.
         */
        auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
        auto cleanup = qScopeGuard([&](){
            QtPrivate::restoreBindingStatus(bindingStatus);
        });

        QQmlComponentPrivate::DeferredState localState = std::move(*state);
        delegate->clearDeferredState();
        QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
        QQmlComponentPrivate::completeDeferred(ep, &localState);
    } else {
        delegate->clearDeferredState();
    }
}

} // QtQuickPrivate

QT_END_NAMESPACE