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,
int signalIndex,
49 QObject *receiver,
int slotIndex,
bool enabled)
50 : QtPrivate::QSlotObjectBase(&impl)
53 , slotIndex(slotIndex)
56 QMetaMethod signal = sender->metaObject()->method(signalIndex);
57 QQmlMetaObject::methodReturnAndParameterTypes(signal, &signalMetaTypes,
nullptr);
59 QMetaMethod slot = receiver->metaObject()->method(slotIndex);
60 QQmlMetaObject::methodReturnAndParameterTypes(slot, &slotMetaTypes,
nullptr);
63 template<
typename ArgTypeStorage>
66 Q_DISABLE_COPY_MOVE(TypedFunction)
68 TypedFunction(
const ArgTypeStorage *storage) : storage(storage) {}
70 QMetaType returnMetaType()
const {
return storage->at(0); }
71 qsizetype parameterCount()
const {
return storage->size() - 1; }
72 QMetaType parameterMetaType(qsizetype i)
const {
return storage->at(i + 1); }
75 const ArgTypeStorage *storage;
78 static void impl(
int which, QSlotObjectBase *base, QObject *,
void **metaArgs,
bool *ret)
82 delete static_cast<QQmlConnectionSlotDispatcher *>(base);
86 QQmlConnectionSlotDispatcher *self =
static_cast<QQmlConnectionSlotDispatcher *>(base);
87 QV4::ExecutionEngine *v4 = self->v4;
94 TypedFunction typedFunction(&self->slotMetaTypes);
96 v4, &typedFunction, metaArgs,
97 self->signalMetaTypes.data(), self->signalMetaTypes.size() - 1,
98 [&](
void **argv,
int) {
99 self->receiver->metaObject()->metacall(
100 self->receiver, QMetaObject::InvokeMetaMethod,
101 self->slotIndex, argv);
104 if (v4->hasException) {
105 QQmlError error = v4->catchExceptionAsQmlError();
106 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
107 QQmlEnginePrivate::get(qmlEngine)->warning(error);
110 qPrintable(error.url().toString()), error.line(),
nullptr)
145
146
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
207QQmlConnections::QQmlConnections(QObject *parent) :
208 QObject(*(
new QQmlConnectionsPrivate), parent)
212QQmlConnections::~QQmlConnections()
214 Q_D(QQmlConnections);
217 for (
const auto &bound : std::as_const(d->boundsignals)) {
218 if (QQmlConnectionSlotDispatcher *dispatcher = bound.isT2() ? bound.asT2() :
nullptr) {
222 dispatcher->connection = {};
223 dispatcher->destroyIfLastRef();
229
230
231
232
233
234
235
236
237QObject *QQmlConnections::target()
const
239 Q_D(
const QQmlConnections);
240 return d->targetSet ? d->target.data() : parent();
250 QQmlBoundSignal *m_signal;
253void QQmlConnections::setTarget(QObject *obj)
255 Q_D(QQmlConnections);
256 if (d->targetSet && d->target == obj)
259 for (
const auto &bound : std::as_const(d->boundsignals)) {
262 if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() :
nullptr) {
263 if (signal->isNotifying())
264 (
new QQmlBoundSignalDeleter(signal))->deleteLater();
268 QQmlConnectionSlotDispatcher *dispatcher = bound.asT2();
269 QObject::disconnect(std::exchange(dispatcher->connection, {}));
270 dispatcher->destroyIfLastRef();
273 d->boundsignals.clear();
276 emit targetChanged();
280
281
282
283
284
285
286
287bool QQmlConnections::isEnabled()
const
289 Q_D(
const QQmlConnections);
293void QQmlConnections::setEnabled(
bool enabled)
295 Q_D(QQmlConnections);
296 if (d->enabled == enabled)
299 d->enabled = enabled;
301 for (
const auto &bound : std::as_const(d->boundsignals)) {
302 if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() :
nullptr)
303 signal->setEnabled(d->enabled);
305 bound.asT2()->enabled = enabled;
308 emit enabledChanged();
312
313
314
315
316
317
318
319
320bool QQmlConnections::ignoreUnknownSignals()
const
322 Q_D(
const QQmlConnections);
323 return d->ignoreUnknownSignals;
326void QQmlConnections::setIgnoreUnknownSignals(
bool ignore)
328 Q_D(QQmlConnections);
329 d->ignoreUnknownSignals = ignore;
333 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
334 const QList<
const QV4::CompiledData::Binding *> &props)
336 for (
int ii = 0; ii < props.size(); ++ii) {
337 const QV4::CompiledData::Binding *binding = props.at(ii);
338 const QString &propName = compilationUnit->stringAt(binding->propertyNameIndex);
340 if (!QQmlSignalNames::isHandlerName(propName)) {
341 error(props.at(ii), QQmlConnections::tr(
"Cannot assign to non-existent property \"%1\"").arg(propName));
345 if (binding->type() == QV4::CompiledData::Binding::Type_Script)
348 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
349 const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex);
350 if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty())
351 error(binding, QQmlConnections::tr(
"Connections: nested objects not allowed"));
353 error(binding, QQmlConnections::tr(
"Connections: syntax error"));
357 error(binding, QQmlConnections::tr(
"Connections: script expected"));
362void QQmlConnectionsParser::
applyBindings(QObject *object,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QList<
const QV4::CompiledData::Binding *> &bindings)
365 static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object));
366 p->compilationUnit = compilationUnit;
367 p->bindings = bindings;
370void QQmlConnections::connectSignals()
372 Q_D(QQmlConnections);
373 if (!d->componentcomplete || (d->targetSet && !target()))
376 if (d->bindings.isEmpty()) {
377 connectSignalsToMethods();
379 if (lcQmlConnections().isWarningEnabled()) {
380 qmlWarning(
this) << tr(
"Implicitly defined onFoo properties in Connections are deprecated. "
381 "Use this syntax instead: function onFoo(<arguments>) { ... }");
383 connectSignalsToBindings();
387void QQmlConnections::connectSignalsToMethods()
389 Q_D(QQmlConnections);
391 QObject *target =
this->target();
392 QQmlData *ddata = QQmlData::get(
this);
396 QV4::ExecutionEngine *engine = ddata->context->engine()->handle();
398 QQmlRefPointer<QQmlContextData> ctxtdata = ddata->outerContext;
399 for (
int i = ddata->propertyCache->methodOffset(),
400 end = ddata->propertyCache->methodOffset() + ddata->propertyCache->methodCount();
404 const QQmlPropertyData *handler = ddata->propertyCache->method(i);
408 const QString propName = handler->name(
this);
410 QQmlProperty prop(target, propName);
411 if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
412 QV4::Scope scope(engine);
413 QV4::ScopedContext global(scope, engine->rootContext());
415 if (QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(
this)) {
416 int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
417 auto *signal =
new QQmlBoundSignal(target, signalIndex,
this, qmlEngine(
this));
418 signal->setEnabled(d->enabled);
420 QV4::Scoped<QV4::JavaScriptFunctionObject> method(
421 scope, vmeMetaObject->vmeMethod(handler->coreIndex()));
423 QQmlBoundSignalExpression *expression = ctxtdata
424 ?
new QQmlBoundSignalExpression(
425 target, signalIndex, ctxtdata,
this, method->function())
428 signal->takeExpression(expression);
429 d->boundsignals += signal;
431 QQmlConnectionSlotDispatcher *slot =
new QQmlConnectionSlotDispatcher(
432 scope.engine, target, prop.index(),
433 this, handler->coreIndex(), d->enabled);
434 slot->connection = QObjectPrivate::connect(
435 target, prop.index(), slot, Qt::AutoConnection);
437 d->boundsignals += slot;
439 }
else if (!d->ignoreUnknownSignals
440 && propName.startsWith(QLatin1String(
"on")) && propName.size() > 2
441 && propName.at(2).isUpper()) {
442 qmlWarning(
this) << tr(
"Detected function \"%1\" in Connections element. "
443 "This is probably intended to be a signal handler but no "
444 "signal of the target matches the name.").arg(propName);
450void QQmlConnections::connectSignalsToBindings()
452 Q_D(QQmlConnections);
453 QObject *target =
this->target();
454 QQmlData *ddata = QQmlData::get(
this);
455 QQmlRefPointer<QQmlContextData> ctxtdata = ddata ? ddata->outerContext :
nullptr;
457 for (
const QV4::CompiledData::Binding *binding : std::as_const(d->bindings)) {
458 Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
459 QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex);
461 QQmlProperty prop(target, propName);
462 if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
463 int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
464 QQmlBoundSignal *signal =
465 new QQmlBoundSignal(target, signalIndex,
this, qmlEngine(
this));
466 signal->setEnabled(d->enabled);
468 auto f = d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
469 QQmlBoundSignalExpression *expression =
470 ctxtdata ?
new QQmlBoundSignalExpression(target, signalIndex, ctxtdata,
this, f)
472 signal->takeExpression(expression);
473 d->boundsignals += signal;
475 if (!d->ignoreUnknownSignals)
476 qmlWarning(
this) << tr(
"Cannot assign to non-existent property \"%1\"").arg(propName);
481void QQmlConnections::classBegin()
483 Q_D(QQmlConnections);
484 d->componentcomplete=
false;
487void QQmlConnections::componentComplete()
489 Q_D(QQmlConnections);
490 d->componentcomplete=
true;
496#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