1// Copyright (C) 2016 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 "qqmldelayedcallqueue_p.h"
5#include <private/qqmlengine_p.h>
6#include <private/qqmljavascriptexpression_p.h>
7#include <private/qv4value_p.h>
8#include <private/qv4jscall_p.h>
9#include <private/qv4qobjectwrapper_p.h>
10#include <private/qv4qmlcontext_p.h>
11
12#include <QQmlError>
13
14QT_BEGIN_NAMESPACE
15
16//
17// struct QQmlDelayedCallQueue::DelayedFunctionCall
18//
19
20void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *engine) const
21{
22 if (!m_guarded ||
23 (!m_objectGuard.isNull() &&
24 !QQmlData::wasDeleted(object: m_objectGuard) &&
25 QQmlData::get(object: m_objectGuard) &&
26 !QQmlData::get(object: m_objectGuard)->isQueuedForDeletion)) {
27
28 QV4::Scope scope(engine);
29
30 QV4::ArrayObject *array = m_args.as<QV4::ArrayObject>();
31 const QV4::FunctionObject *callback = m_function.as<QV4::FunctionObject>();
32 Q_ASSERT(callback);
33 const int argCount = array ? array->getLength() : 0;
34 QV4::JSCallArguments jsCallData(scope, argCount);
35 *jsCallData.thisObject = QV4::Encode::undefined();
36
37 for (int i = 0; i < argCount; i++) {
38 jsCallData.args[i] = array->get(idx: i);
39 }
40
41 callback->call(data: jsCallData);
42
43 if (scope.hasException()) {
44 QQmlError error = scope.engine->catchExceptionAsQmlError();
45 error.setDescription(error.description() + QLatin1String(" (exception occurred during delayed function evaluation)"));
46 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(e: scope.engine->qmlEngine()), error);
47 }
48 }
49}
50
51//
52// class QQmlDelayedCallQueue
53//
54
55QQmlDelayedCallQueue::QQmlDelayedCallQueue()
56 : QObject(nullptr), m_engine(nullptr), m_callbackOutstanding(false)
57{
58}
59
60QQmlDelayedCallQueue::~QQmlDelayedCallQueue()
61{
62}
63
64void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine)
65{
66 m_engine = engine;
67
68 const QMetaObject &metaObject = QQmlDelayedCallQueue::staticMetaObject;
69 int methodIndex = metaObject.indexOfSlot(slot: "ticked()");
70 m_tickedMethod = metaObject.method(index: methodIndex);
71}
72
73QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::ExecutionEngine *engine, QQmlV4Function *args)
74{
75 QQmlDelayedCallQueue *self = engine->delayedCallQueue();
76
77 QV4::Scope scope(engine);
78 if (args->length() == 0)
79 THROW_GENERIC_ERROR("Qt.callLater: no arguments given");
80
81 QV4::ScopedValue firstArgument(scope, (*args)[0]);
82
83 const QV4::FunctionObject *func = firstArgument->as<QV4::FunctionObject>();
84
85 if (!func)
86 THROW_GENERIC_ERROR("Qt.callLater: first argument not a function or signal");
87
88 QPair<QObject *, int> functionData = QV4::QObjectMethod::extractQtMethod(function: func);
89
90 QVector<DelayedFunctionCall>::Iterator iter;
91 if (functionData.second != -1) {
92 // This is a QObject function wrapper
93 iter = self->m_delayedFunctionCalls.begin();
94 while (iter != self->m_delayedFunctionCalls.end()) {
95 DelayedFunctionCall& dfc = *iter;
96 QPair<QObject *, int> storedFunctionData = QV4::QObjectMethod::extractQtMethod(function: dfc.m_function.as<QV4::FunctionObject>());
97 if (storedFunctionData == functionData) {
98 break; // Already stored!
99 }
100 ++iter;
101 }
102 } else {
103 // This is a JavaScript function (dynamic slot on VMEMO)
104 iter = self->m_delayedFunctionCalls.begin();
105 while (iter != self->m_delayedFunctionCalls.end()) {
106 DelayedFunctionCall& dfc = *iter;
107 if (firstArgument->asReturnedValue() == dfc.m_function.value()) {
108 break; // Already stored!
109 }
110 ++iter;
111 }
112 }
113
114 const bool functionAlreadyStored = (iter != self->m_delayedFunctionCalls.end());
115 if (functionAlreadyStored) {
116 DelayedFunctionCall dfc = *iter;
117 self->m_delayedFunctionCalls.erase(pos: iter);
118 self->m_delayedFunctionCalls.append(t: dfc);
119 } else {
120 self->m_delayedFunctionCalls.append(t: QV4::PersistentValue(engine, firstArgument));
121 }
122
123 DelayedFunctionCall& dfc = self->m_delayedFunctionCalls.last();
124 if (dfc.m_objectGuard.isNull()) {
125 if (functionData.second != -1) {
126 // if it's a qobject function wrapper, guard against qobject deletion
127 dfc.m_objectGuard = QQmlGuard<QObject>(functionData.first);
128 dfc.m_guarded = true;
129 } else if (func->scope()->type == QV4::Heap::ExecutionContext::Type_QmlContext) {
130 QV4::QmlContext::Data *g = static_cast<QV4::QmlContext::Data *>(func->scope());
131 Q_ASSERT(g->qml()->scopeObject);
132 dfc.m_objectGuard = QQmlGuard<QObject>(g->qml()->scopeObject);
133 dfc.m_guarded = true;
134 }
135 }
136 self->storeAnyArguments(dfc, args, offset: 1, engine);
137
138 if (!self->m_callbackOutstanding) {
139 self->m_tickedMethod.invoke(obj: self, c: Qt::QueuedConnection);
140 self->m_callbackOutstanding = true;
141 }
142 return QV4::Encode::undefined();
143}
144
145void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, QQmlV4Function *args, int offset, QV4::ExecutionEngine *engine)
146{
147 const int length = args->length() - offset;
148 if (length == 0) {
149 dfc.m_args.clear();
150 return;
151 }
152 QV4::Scope scope(engine);
153 QV4::ScopedArrayObject array(scope, engine->newArrayObject(count: length));
154 uint i = 0;
155 for (int j = offset, ej = args->length(); j < ej; ++i, ++j)
156 array->put(idx: i, v: (*args)[j]);
157 dfc.m_args.set(engine, value: array);
158}
159
160void QQmlDelayedCallQueue::executeAllExpired_Later()
161{
162 // Make a local copy of the list and clear m_delayedFunctionCalls
163 // This ensures correct behavior in the case of recursive calls to Qt.callLater()
164 QVector<DelayedFunctionCall> delayedCalls = m_delayedFunctionCalls;
165 m_delayedFunctionCalls.clear();
166
167 QVector<DelayedFunctionCall>::Iterator iter = delayedCalls.begin();
168 while (iter != delayedCalls.end()) {
169 DelayedFunctionCall& dfc = *iter;
170 dfc.execute(engine: m_engine);
171 ++iter;
172 }
173}
174
175void QQmlDelayedCallQueue::ticked()
176{
177 m_callbackOutstanding = false;
178 executeAllExpired_Later();
179}
180
181QT_END_NAMESPACE
182
183#include "moc_qqmldelayedcallqueue_p.cpp"
184

source code of qtdeclarative/src/qml/qml/qqmldelayedcallqueue.cpp