Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmlboundsignal.cpp
Go to the documentation of this file.
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 "qqmlboundsignal_p.h"
5
6#include <private/qmetaobject_p.h>
7#include <private/qmetaobjectbuilder_p.h>
8#include "qqmlengine_p.h"
9#include "qqmlglobal_p.h"
10#include <private/qqmlprofiler_p.h>
11#include <private/qqmldebugconnector_p.h>
12#include <private/qqmldebugserviceinterfaces_p.h>
13#include "qqmlinfo.h"
14
15#include <private/qjsvalue_p.h>
16#include <private/qv4value_p.h>
17#include <private/qv4jscall_p.h>
18#include <private/qv4qobjectwrapper_p.h>
19#include <private/qv4qmlcontext_p.h>
20
21#include <QtCore/qdebug.h>
22
23#include <qtqml_tracepoints_p.h>
24
26
27Q_TRACE_POINT(qtqml, QQmlHandlingSignal_entry, const QQmlEngine *engine, const QString &function,
28 const QString &fileName, int line, int column)
29Q_TRACE_POINT(qtqml, QQmlHandlingSignal_exit)
30
32 const QString &expression, const QString &fileName, quint16 line, quint16 column,
33 const QString &handlerName, const QString &parameterString)
35 m_index(index),
36 m_target(target)
37{
38 init(ctxt, scope);
39
41
43
44 // Add some leading whitespace to account for the binding's column offset.
45 // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted.
47 + QLatin1String("(function ") + handlerName + QLatin1Char('(');
48
49 if (parameterString.isEmpty()) {
51 //TODO: look at using the property cache here (as in the compiler)
52 // for further optimization
53 QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
55
56 if (!error.isEmpty()) {
57 qmlWarning(scopeObject()) << error;
58 return;
59 }
60 } else
61 function += parameterString;
62
63 function += QLatin1String(") { ") + expression + QLatin1String(" })");
64 QV4::Scope valueScope(v4);
65 QV4::ScopedFunctionObject f(valueScope, evalFunction(context(), scopeObject(), function, fileName, line));
66 QV4::ScopedContext context(valueScope, f->scope());
67 setupFunction(context, f->function());
68}
69
71 QObject *scopeObject, QV4::Function *function, QV4::ExecutionContext *scope)
73 m_index(index),
74 m_target(target)
75{
76 // It's important to call init first, because m_index gets remapped in case of cloned signals.
77 init(ctxt, scopeObject);
78
79 QV4::ExecutionEngine *engine = ctxt->engine()->handle();
80
81 if (!function->isClosureWrapper()) {
82 QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames();
83 if (!signalParameters.isEmpty()) {
86 if (!error.isEmpty()) {
88 return;
89 }
90 function->updateInternalClass(engine, signalParameters);
91 }
92 }
93
94 QV4::Scope valueScope(engine);
96 if (!qmlContext)
98 if (auto closure = function->nestedFunction()) {
99 // If the function is marked as having a nested function, then the user wrote:
100 // onSomeSignal: function() { /*....*/ }
101 // So take that nested function:
102 setupFunction(qmlContext, closure);
103 } else {
105
106 // If it's a closure wrapper but we cannot directly access the nested function
107 // we need to run the outer function to get the nested one.
108 if (function->isClosureWrapper()) {
109 bool isUndefined = false;
111 valueScope, QQmlJavaScriptExpression::evaluate(&isUndefined));
112
113 Q_ASSERT(!isUndefined);
114 Q_ASSERT(result->function());
115 Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
116
117 QV4::Scoped<QV4::ExecutionContext> callContext(valueScope, result->scope());
118 setupFunction(callContext, result->function());
119 }
120 }
121}
122
123void QQmlBoundSignalExpression::init(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope)
124{
126 setContext(ctxt);
127 setScopeObject(scope);
128
129 Q_ASSERT(m_target && m_index > -1);
130 m_index = QQmlPropertyCache::originalClone(m_target, m_index);
131}
132
134{
135}
136
138{
140 return loc.sourceFile + QLatin1Char(':') + QString::number(loc.line);
141}
142
144{
145 // bound signals do not notify on change.
146}
147
149{
150 if (expressionFunctionValid())
151 return QStringLiteral("function() { [native code] }");
152 return QString();
153}
154
155// Parts of this function mirror code in QQmlExpressionPrivate::value() and v4Value().
156// Changes made here may need to be made there and vice versa.
158{
159 if (!expressionFunctionValid())
160 return;
161
162 QQmlEngine *qmlengine = engine();
163
164 // If there is no engine, we have no way to evaluate anything.
165 // This can happen on destruction.
166 if (!qmlengine)
167 return;
168
170 QV4::ExecutionEngine *v4 = qmlengine->handle();
171 QV4::Scope scope(v4);
172
173 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
174
175 if (a) {
176 //TODO: lookup via signal index rather than method index as an optimization
177 const QMetaObject *targetMeta = m_target->metaObject();
178 const QMetaMethod metaMethod = targetMeta->method(
179 QMetaObjectPrivate::signal(targetMeta, m_index).methodIndex());
180
181 int argCount = metaMethod.parameterCount();
183 storage.reserve(argCount + 1);
184 storage.append(QMetaType()); // We're not interested in the return value
185 for (int i = 0; i < argCount; ++i) {
186 const QMetaType type = metaMethod.parameterMetaType(i);
187 if (!type.isValid())
188 argCount = 0;
189 else if (type.flags().testFlag(QMetaType::IsEnumeration))
190 storage.append(type.underlyingType());
191 else
192 storage.append(type);
193 }
194
195 QQmlJavaScriptExpression::evaluate(a, storage.constData(), argCount);
196 } else {
197 void *ignoredResult = nullptr;
198 QMetaType invalidType;
199 QQmlJavaScriptExpression::evaluate(&ignoredResult, &invalidType, 0);
200 }
201
202 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
203}
204
206
207
215 m_prevSignal(nullptr), m_nextSignal(nullptr),
216 m_enabled(true)
217{
218 addToObject(owner);
219
220 /*
221 If this is a cloned method, connect to the 'original'. For example,
222 for the signal 'void aSignal(int parameter = 0)', if the method
223 index refers to 'aSignal()', get the index of 'aSignal(int)'.
224 This ensures that 'parameter' will be available from QML.
225 */
228}
229
231{
233}
234
235void QQmlBoundSignal::addToObject(QObject *obj)
236{
237 Q_ASSERT(!m_prevSignal);
238 Q_ASSERT(obj);
239
240 QQmlData *data = QQmlData::get(obj, true);
241
242 m_nextSignal = data->signalHandlers;
243 if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
244 m_prevSignal = &data->signalHandlers;
245 data->signalHandlers = this;
246}
247
249{
250 if (m_prevSignal) {
251 *m_prevSignal = m_nextSignal;
252 if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
253 m_prevSignal = nullptr;
254 m_nextSignal = nullptr;
255 }
256}
257
262{
263 return m_expression.data();
264}
265
272{
273 m_expression.adopt(e);
274 if (m_expression)
275 m_expression->setNotifyOnValueChanged(false);
276}
277
286{
287 if (m_enabled == enabled)
288 return;
289
290 m_enabled = enabled;
291}
292
294{
295 QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e);
296
297 if (!s->m_expression || !s->m_enabled)
298 return;
299
300 QV4DebugService *service = QQmlDebugConnector::service<QV4DebugService>();
301 if (service)
302 service->signalEmitted(QString::fromUtf8(QMetaObjectPrivate::signal(
303 s->m_expression->target()->metaObject(),
304 s->signalIndex()).methodSignature()));
305
307 if (s->m_expression && (engine = s->m_expression->engine())) {
308 Q_TRACE_SCOPE(QQmlHandlingSignal, engine,
309 s->m_expression->function() ? s->m_expression->function()->name()->toQString() : QString(),
310 s->m_expression->sourceLocation().sourceFile, s->m_expression->sourceLocation().line,
311 s->m_expression->sourceLocation().column);
313 s->m_expression.data());
314 s->m_expression->evaluate(a);
315 if (s->m_expression && s->m_expression->hasError()) {
316 QQmlEnginePrivate::warning(engine, s->m_expression->error(engine));
317 }
318 }
319}
320
322
325 auto This = static_cast<QQmlPropertyObserver*>(self);
326 This->expression->evaluate(nullptr);
327 })
328{
329 expression.adopt(expr);
330}
331
\inmodule QtCore
Definition qchar.h:48
@ Space
Definition qchar.h:56
QV4::ExecutionEngine * handle() const
Definition qjsengine.h:292
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
\inmodule QtCore
Definition qmetaobject.h:18
int parameterCount() const
QByteArray methodSignature() const
QMetaType parameterMetaType(int index) const
QList< QByteArray > parameterNames() const
Returns a list of parameter names.
\inmodule QtCore
Definition qmetatype.h:320
@ IsEnumeration
Definition qmetatype.h:386
\inmodule QtCore
Definition qobject.h:90
QString expressionIdentifier() const override
QQmlBoundSignalExpression(const QObject *target, int index, const QQmlRefPointer< QQmlContextData > &ctxt, QObject *scope, const QString &expression, const QString &fileName, quint16 line, quint16 column, const QString &handlerName=QString(), const QString &parameterString=QString())
void setEnabled(bool enabled)
This property holds whether the item will emit signals.
void takeExpression(QQmlBoundSignalExpression *)
Sets the signal expression to e.
QQmlBoundSignalExpression * expression() const
Returns the signal expression.
static QQmlData * get(QObjectPrivate *priv, bool create)
Definition qqmldata_p.h:199
void warning(const QQmlError &)
static QQmlEnginePrivate * get(QQmlEngine *e)
void referenceScarceResources()
void dereferenceScarceResources()
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
QQmlContext * rootContext() const
Returns the engine's root context.
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f)
QV4::ReturnedValue evaluate(bool *isUndefined)
void setContext(const QQmlRefPointer< QQmlContextData > &context)
virtual QQmlSourceLocation sourceLocation() const
void connect(QObject *source, int sourceSignal, QQmlEngine *engine, bool doNotify=true)
static QString signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList< QByteArray > &parameterNameList, QString *errorString=nullptr)
int originalClone(int index) const
QQmlPropertyObserver(QQmlBoundSignalExpression *expr)
T * data() const
QQmlRefPointer< T > & adopt(T *)
Takes ownership of other.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
double e
auto signal
Combined button and popup list for selecting options.
static void * context
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLfloat GLfloat f
GLenum type
GLenum target
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLsizei void GLsizei void * column
GLhandleARB obj
[2]
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_TRACE_SCOPE(x,...)
Definition qtrace_p.h:146
#define Q_TRACE_POINT(provider, tracepoint,...)
Definition qtrace_p.h:232
unsigned short quint16
Definition qtypes.h:43
#define enabled
QStorageInfo storage
[1]
QObject::connect nullptr
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:17
static Q_CORE_EXPORT QMetaMethod signal(const QMetaObject *m, int signal_index)
\inmodule QtCore
QMetaMethod method(int index) const
Returns the meta-data for the method with the given index.
CompiledData::CompilationUnitBase * compilationUnit
Function * nestedFunction() const
void updateInternalClass(ExecutionEngine *engine, const QList< QByteArray > &parameters)
bool isClosureWrapper() const
static Heap::QmlContext * create(QV4::ExecutionContext *parent, QQmlRefPointer< QQmlContextData > context, QObject *scopeObject)