7#include <private/qqmlboundsignal_p.h>
8#include <private/qqmlcontext_p.h>
9#include <private/qqmlexpression_p.h>
10#include <private/qqmlproperty_p.h>
11#include <private/qqmlsignalnames_p.h>
12#include <private/qqmlvmemetaobject_p.h>
13#include <private/qv4jscall_p.h>
14#include <private/qv4qobjectwrapper_p.h>
16#include <QtQml/qqmlcontext.h>
17#include <QtQml/qqmlinfo.h>
19#include <QtCore/qdebug.h>
20#include <QtCore/qloggingcategory.h>
21#include <QtCore/qstringlist.h>
23#include <private/qobject_p.h>
27Q_STATIC_LOGGING_CATEGORY(lcQmlConnections,
"qt.qml.connections")
33struct QQmlConnectionSlotDispatcher :
public QtPrivate::QSlotObjectBase
35 QV4::ExecutionEngine *v4 =
nullptr;
36 QObject *receiver =
nullptr;
39 QQmlMetaObject::ArgTypeStorage<2> signalMetaTypes;
40 QQmlMetaObject::ArgTypeStorage<2> slotMetaTypes;
42 QMetaObject::Connection connection;
47 QQmlConnectionSlotDispatcher(
48 QV4::ExecutionEngine *v4, QObject *sender,
const QQmlPropertyData &signal,
49 QObject *receiver,
const QQmlPropertyData &slot,
bool enabled)
50 : QtPrivate::QSlotObjectBase(&impl)
53 , slotIndex(slot.coreIndex())
56 const QQmlMetaObject senderMeta(sender->metaObject());
57 senderMeta.methodReturnAndParameterTypes(
58 signal, senderMeta.metaObject()->method(signal.coreIndex()), &signalMetaTypes);
60 const QQmlMetaObject receiverMeta(receiver->metaObject());
61 receiverMeta.methodReturnAndParameterTypes(
62 slot, receiverMeta.metaObject()->method(slotIndex), &slotMetaTypes);
65 template<
typename ArgTypeStorage>
68 Q_DISABLE_COPY_MOVE(TypedFunction)
70 TypedFunction(
const ArgTypeStorage *storage) : storage(storage) {}
72 QMetaType returnMetaType()
const {
return storage->at(0); }
73 qsizetype parameterCount()
const {
return storage->size() - 1; }
74 QMetaType parameterMetaType(qsizetype i)
const {
return storage->at(i + 1); }
77 const ArgTypeStorage *storage;
80 static void impl(
int which, QSlotObjectBase *base, QObject *,
void **metaArgs,
bool *ret)
84 delete static_cast<QQmlConnectionSlotDispatcher *>(base);
88 QQmlConnectionSlotDispatcher *self =
static_cast<QQmlConnectionSlotDispatcher *>(base);
89 QV4::ExecutionEngine *v4 = self->v4;
96 TypedFunction typedFunction(&self->slotMetaTypes);
98 v4, &typedFunction, metaArgs,
99 self->signalMetaTypes.data(), self->signalMetaTypes.size() - 1,
100 [&](
void **argv,
int) {
101 self->receiver->metaObject()->metacall(
102 self->receiver, QMetaObject::InvokeMetaMethod,
103 self->slotIndex, argv);
106 if (v4->hasException) {
107 QQmlError error = v4->catchExceptionAsQmlError();
108 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
109 QQmlEnginePrivate::get(qmlEngine)->warning(error);
112 qPrintable(error.url().toString()), error.line(),
nullptr)
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209QQmlConnections::QQmlConnections(QObject *parent) :
210 QObject(*(
new QQmlConnectionsPrivate), parent)
214QQmlConnections::~QQmlConnections()
216 Q_D(QQmlConnections);
219 for (
const auto &bound : std::as_const(d->boundsignals)) {
220 if (QQmlConnectionSlotDispatcher *dispatcher = bound.isT2() ? bound.asT2() :
nullptr) {
224 dispatcher->connection = {};
225 dispatcher->destroyIfLastRef();
231
232
233
234
235
236
237
238
239QObject *QQmlConnections::target()
const
241 Q_D(
const QQmlConnections);
242 return d->targetSet ? d->target.data() : parent();
252 QQmlBoundSignal *m_signal;
255void QQmlConnections::setTarget(QObject *obj)
257 Q_D(QQmlConnections);
258 if (d->targetSet && d->target == obj)
261 for (
const auto &bound : std::as_const(d->boundsignals)) {
264 if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() :
nullptr) {
265 if (signal->isNotifying())
266 (
new QQmlBoundSignalDeleter(signal))->deleteLater();
270 QQmlConnectionSlotDispatcher *dispatcher = bound.asT2();
271 QObject::disconnect(std::exchange(dispatcher->connection, {}));
272 dispatcher->destroyIfLastRef();
275 d->boundsignals.clear();
278 emit targetChanged();
282
283
284
285
286
287
288
289bool QQmlConnections::isEnabled()
const
291 Q_D(
const QQmlConnections);
295void QQmlConnections::setEnabled(
bool enabled)
297 Q_D(QQmlConnections);
298 if (d->enabled == enabled)
301 d->enabled = enabled;
303 for (
const auto &bound : std::as_const(d->boundsignals)) {
304 if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() :
nullptr)
305 signal->setEnabled(d->enabled);
307 bound.asT2()->enabled = enabled;
310 emit enabledChanged();
314
315
316
317
318
319
320
321
322bool QQmlConnections::ignoreUnknownSignals()
const
324 Q_D(
const QQmlConnections);
325 return d->ignoreUnknownSignals;
328void QQmlConnections::setIgnoreUnknownSignals(
bool ignore)
330 Q_D(QQmlConnections);
331 d->ignoreUnknownSignals = ignore;
335 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
336 const QList<
const QV4::CompiledData::Binding *> &props)
338 for (
int ii = 0; ii < props.size(); ++ii) {
339 const QV4::CompiledData::Binding *binding = props.at(ii);
340 const QString &propName = compilationUnit->stringAt(binding->propertyNameIndex);
342 if (!QQmlSignalNames::isHandlerName(propName)) {
343 error(props.at(ii), QQmlConnections::tr(
"Cannot assign to non-existent property \"%1\"").arg(propName));
347 if (binding->type() == QV4::CompiledData::Binding::Type_Script)
350 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
351 const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex);
352 if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty())
353 error(binding, QQmlConnections::tr(
"Connections: nested objects not allowed"));
355 error(binding, QQmlConnections::tr(
"Connections: syntax error"));
359 error(binding, QQmlConnections::tr(
"Connections: script expected"));
364void QQmlConnectionsParser::
applyBindings(QObject *object,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QList<
const QV4::CompiledData::Binding *> &bindings)
367 static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object));
368 p->compilationUnit = compilationUnit;
369 p->bindings = bindings;
372void QQmlConnections::connectSignals()
374 Q_D(QQmlConnections);
375 if (!d->componentcomplete || (d->targetSet && !target()))
378 if (d->bindings.isEmpty()) {
379 connectSignalsToMethods();
381 if (lcQmlConnections().isWarningEnabled()) {
382 qmlWarning(
this) << tr(
"Implicitly defined onFoo properties in Connections are deprecated. "
383 "Use this syntax instead: function onFoo(<arguments>) { ... }");
385 connectSignalsToBindings();
389void QQmlConnections::connectSignalsToMethods()
391 Q_D(QQmlConnections);
393 QObject *target =
this->target();
394 QQmlData *ddata = QQmlData::get(
this);
395 if (!ddata || !ddata->propertyCache || !ddata->context || !ddata->context->isValid())
398 QV4::ExecutionEngine *engine = ddata->context->engine()->handle();
400 QQmlRefPointer<QQmlContextData> ctxtdata = ddata->outerContext;
401 for (
int i = ddata->propertyCache->methodOffset(),
402 end = ddata->propertyCache->methodOffset() + ddata->propertyCache->methodCount();
406 const QQmlPropertyData *handler = ddata->propertyCache->method(i);
410 const QString propName = handler->name(
this);
412 QQmlProperty prop(target, propName);
413 if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
414 QQmlPropertyPrivate *propPrivate = QQmlPropertyPrivate::get(prop);
415 QV4::Scope scope(engine);
416 QV4::ScopedContext global(scope, engine->rootContext());
418 if (QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(
this)) {
419 const int signalIndex = propPrivate->signalIndex();
420 auto *signal =
new QQmlBoundSignal(target, signalIndex,
this, qmlEngine(
this));
421 signal->setEnabled(d->enabled);
423 QV4::Scoped<QV4::JavaScriptFunctionObject> method(
424 scope, vmeMetaObject->vmeMethod(handler->coreIndex()));
426 QQmlBoundSignalExpression *expression = ctxtdata
427 ?
new QQmlBoundSignalExpression(
428 target, signalIndex, ctxtdata,
this, method->function())
431 signal->takeExpression(expression);
432 d->boundsignals += signal;
434 QQmlConnectionSlotDispatcher *slot =
new QQmlConnectionSlotDispatcher(
435 scope.engine, target, propPrivate->core,
this, *handler, d->enabled);
436 slot->connection = QObjectPrivate::connect(
437 target, prop.index(), slot, Qt::AutoConnection);
439 d->boundsignals += slot;
441 }
else if (!d->ignoreUnknownSignals
442 && propName.startsWith(QLatin1String(
"on")) && propName.size() > 2
443 && propName.at(2).isUpper()) {
444 qmlWarning(
this) << tr(
"Detected function \"%1\" in Connections element. "
445 "This is probably intended to be a signal handler but no "
446 "signal of the target matches the name.").arg(propName);
452void QQmlConnections::connectSignalsToBindings()
454 Q_D(QQmlConnections);
455 QObject *target =
this->target();
456 QQmlData *ddata = QQmlData::get(
this);
457 QQmlRefPointer<QQmlContextData> ctxtdata = ddata ? ddata->outerContext :
nullptr;
459 for (
const QV4::CompiledData::Binding *binding : std::as_const(d->bindings)) {
460 Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
461 QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex);
463 QQmlProperty prop(target, propName);
464 if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
465 int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
466 QQmlBoundSignal *signal =
467 new QQmlBoundSignal(target, signalIndex,
this, qmlEngine(
this));
468 signal->setEnabled(d->enabled);
470 auto f = d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
471 QQmlBoundSignalExpression *expression =
472 ctxtdata ?
new QQmlBoundSignalExpression(target, signalIndex, ctxtdata,
this, f)
474 signal->takeExpression(expression);
475 d->boundsignals += signal;
477 if (!d->ignoreUnknownSignals)
478 qmlWarning(
this) << tr(
"Cannot assign to non-existent property \"%1\"").arg(propName);
483void QQmlConnections::classBegin()
485 Q_D(QQmlConnections);
486 d->componentcomplete=
false;
489void QQmlConnections::componentComplete()
491 Q_D(QQmlConnections);
492 d->componentcomplete=
true;
498#include "moc_qqmlconnections_p.cpp"
QQmlBoundSignalDeleter(QQmlBoundSignal *signal)
~QQmlBoundSignalDeleter()
void applyBindings(QObject *object, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QList< const QV4::CompiledData::Binding * > &bindings) override
void verifyBindings(const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &compilationUnit, const QList< const QV4::CompiledData::Binding * > &props) override
QList< const QV4::CompiledData::Binding * > bindings
QQmlGuard< QObject > target
QList< QBiPointer< QQmlBoundSignal, QQmlConnectionSlotDispatcher > > boundsignals
QQmlRefPointer< QV4::ExecutableCompilationUnit > compilationUnit
bool ignoreUnknownSignals
Combined button and popup list for selecting options.