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 "qqmlbinding_p.h"
5
6#include "qqmlcontext.h"
7#include "qqmldata_p.h"
8
9#include <private/qqmldebugserviceinterfaces_p.h>
10#include <private/qqmldebugconnector_p.h>
11
12#include <private/qqmlprofiler_p.h>
13#include <private/qqmlexpression_p.h>
14#include <private/qqmlscriptstring_p.h>
15#include <private/qqmlbuiltinfunctions_p.h>
16#include <private/qqmlvmemetaobject_p.h>
17#include <private/qqmlvaluetypewrapper_p.h>
18#include <private/qv4qmlcontext_p.h>
19#include <private/qv4qobjectwrapper_p.h>
20#include <private/qv4variantobject_p.h>
21#include <private/qv4jscall_p.h>
22#include <private/qjsvalue_p.h>
23
24#include <qtqml_tracepoints_p.h>
25
26#include <QVariant>
27#include <QtCore/qdebug.h>
28#include <QVector>
29
30QT_BEGIN_NAMESPACE
31
32Q_TRACE_POINT(qtqml, QQmlBinding_entry, const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column)
33Q_TRACE_POINT(qtqml, QQmlBinding_exit)
34
35QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
36{
37 QQmlBinding *b = newBinding(property);
38
39 if (ctxt && !ctxt->isValid())
40 return b;
41
42 const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
43 if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
44 return b;
45
46 QString url;
47 QV4::Function *runtimeFunction = nullptr;
48
49 QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(context: scriptPrivate->context);
50 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(e: scriptPrivate->context->engine());
51 if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit()) {
52 url = ctxtdata->urlString();
53 if (scriptPrivate->bindingId != QQmlBinding::Invalid)
54 runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(i: scriptPrivate->bindingId);
55 }
56
57 b->setNotifyOnValueChanged(true);
58 b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(context: ctxt ? ctxt : scriptPrivate->context));
59 b->setScopeObject(obj ? obj : scriptPrivate->scope);
60
61 QV4::ExecutionEngine *v4 = b->engine()->handle();
62 if (runtimeFunction) {
63 QV4::Scope scope(v4);
64 QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(parent: v4->rootContext(), context: ctxtdata, scopeObject: b->scopeObject()));
65 b->setupFunction(qmlContext, f: runtimeFunction);
66 } else {
67 QString code = scriptPrivate->script;
68 b->createQmlBinding(ctxt: b->context(), scope: b->scopeObject(), code, filename: url, line: scriptPrivate->lineNumber);
69 }
70
71 return b;
72}
73
74QQmlSourceLocation QQmlBinding::sourceLocation() const
75{
76 if (m_sourceLocation)
77 return *m_sourceLocation;
78 return QQmlJavaScriptExpression::sourceLocation();
79}
80
81void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location)
82{
83 if (m_sourceLocation)
84 delete m_sourceLocation;
85 m_sourceLocation = new QQmlSourceLocation(location);
86}
87
88
89QQmlBinding *QQmlBinding::create(
90 const QQmlPropertyData *property, const QString &str, QObject *obj,
91 const QQmlRefPointer<QQmlContextData> &ctxt, const QString &url, quint16 lineNumber)
92{
93 QQmlBinding *b = newBinding(property);
94
95 b->setNotifyOnValueChanged(true);
96 b->QQmlJavaScriptExpression::setContext(ctxt);
97 b->setScopeObject(obj);
98
99 b->createQmlBinding(ctxt, scope: obj, code: str, filename: url, line: lineNumber);
100
101 return b;
102}
103
104QQmlBinding *QQmlBinding::create(
105 const QQmlPropertyData *property, QV4::Function *function, QObject *obj,
106 const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope)
107{
108 return create(propertyType: property ? property->propType() : QMetaType(), function, obj, ctxt, scope);
109}
110
111QQmlBinding *QQmlBinding::create(QMetaType propertyType, QV4::Function *function, QObject *obj,
112 const QQmlRefPointer<QQmlContextData> &ctxt,
113 QV4::ExecutionContext *scope)
114{
115 QQmlBinding *b = newBinding(propertyType);
116
117 b->setNotifyOnValueChanged(true);
118 b->QQmlJavaScriptExpression::setContext(ctxt);
119 b->setScopeObject(obj);
120
121 Q_ASSERT(scope);
122 b->setupFunction(qmlContext: scope, f: function);
123
124 return b;
125}
126
127QQmlBinding::~QQmlBinding()
128{
129 delete m_sourceLocation;
130}
131
132void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
133{
134 if (!enabledFlag() || !hasValidContext())
135 return;
136
137 // Check that the target has not been deleted
138 if (QQmlData::wasDeleted(object: targetObject()))
139 return;
140
141 // Check for a binding update loop
142 if (Q_UNLIKELY(updatingFlag())) {
143 const QQmlPropertyData *d = nullptr;
144 QQmlPropertyData vtd;
145 getPropertyData(propertyData: &d, valueTypeData: &vtd);
146 Q_ASSERT(d);
147 QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, nullptr);
148 QQmlAbstractBinding::printBindingLoopError(prop: p);
149 return;
150 }
151 setUpdatingFlag(true);
152
153 DeleteWatcher watcher(this);
154
155 QQmlEngine *qmlEngine = engine();
156 QV4::Scope scope(qmlEngine->handle());
157
158 if (canUseAccessor())
159 flags.setFlag(flag: QQmlPropertyData::BypassInterceptor);
160
161 Q_TRACE_SCOPE(QQmlBinding, qmlEngine, function() ? function()->name()->toQString() : QString(),
162 sourceLocation().sourceFile, sourceLocation().line, sourceLocation().column);
163 QQmlBindingProfiler prof(QQmlEnginePrivate::get(e: qmlEngine)->profiler, function());
164 doUpdate(watcher, flags, scope);
165
166 if (!watcher.wasDeleted())
167 setUpdatingFlag(false);
168}
169
170QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
171{
172 QV4::ExecutionEngine *v4 = engine()->handle();
173 int argc = 0;
174 const QV4::Value *argv = nullptr;
175 const QV4::Value *thisObject = nullptr;
176 QV4::BoundFunction *b = nullptr;
177 if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) {
178 QV4::Heap::MemberData *args = b->boundArgs();
179 if (args) {
180 argc = args->values.size;
181 argv = args->values.data();
182 }
183 thisObject = &b->d()->boundThis;
184 }
185 QV4::Scope scope(v4);
186 QV4::JSCallData jsCall(thisObject, argv, argc);
187
188 return QQmlJavaScriptExpression::evaluate(callData: jsCall.callData(scope), isUndefined);
189}
190
191template<int StaticPropType>
192class GenericBinding: public QQmlBinding
193{
194protected:
195 // Returns true if successful, false if an error description was set on expression
196 Q_ALWAYS_INLINE bool write(void *result, QMetaType type, bool isUndefined,
197 QQmlPropertyData::WriteFlags flags) override final
198 {
199 const QQmlPropertyData *pd;
200 QQmlPropertyData vpd;
201 getPropertyData(propertyData: &pd, valueTypeData: &vpd);
202 Q_ASSERT(pd);
203
204 if (isUndefined || vpd.isValid())
205 return slowWrite(*pd, vpd, result, type, isUndefined, flags);
206
207 if ((StaticPropType == QMetaType::UnknownType && pd->propType() == type)
208 || StaticPropType == type.id()) {
209 Q_ASSERT(targetObject());
210 return pd->writeProperty(target: targetObject(), value: result, flags);
211 }
212
213 // If the type didn't match, we need to do JavaScript conversion. This should be rare.
214 return write(engine()->handle()->metaTypeToJS(type, result), isUndefined, flags);
215 }
216
217 // Returns true if successful, false if an error description was set on expression
218 Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
219 QQmlPropertyData::WriteFlags flags) override final
220 {
221 Q_ASSERT(targetObject());
222
223 const QQmlPropertyData *pd;
224 QQmlPropertyData vpd;
225 getPropertyData(propertyData: &pd, valueTypeData: &vpd);
226 Q_ASSERT(pd);
227
228 int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded.
229 if (propertyType == QMetaType::UnknownType)
230 propertyType = pd->propType().id();
231
232 if (Q_LIKELY(!isUndefined && !vpd.isValid())) {
233 switch (propertyType) {
234 case QMetaType::Bool:
235 if (result.isBoolean())
236 return doStore<bool>(result.booleanValue(), pd, flags);
237 else
238 return doStore<bool>(result.toBoolean(), pd, flags);
239 case QMetaType::Int:
240 if (result.isInteger())
241 return doStore<int>(result.integerValue(), pd, flags);
242 else if (result.isNumber()) {
243 return doStore<int>(result.toInt32(), pd, flags);
244 }
245 break;
246 case QMetaType::Double:
247 if (result.isNumber())
248 return doStore<double>(result.asDouble(), pd, flags);
249 break;
250 case QMetaType::Float:
251 if (result.isNumber())
252 return doStore<float>(result.asDouble(), pd, flags);
253 break;
254 case QMetaType::QString:
255 if (result.isString())
256 return doStore<QString>(result.toQStringNoThrow(), pd, flags);
257 break;
258 default:
259 if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
260 if (vtw->d()->metaType() == pd->propType()) {
261 return vtw->write(target: m_target.data(), propertyIndex: pd->coreIndex());
262 }
263 }
264 break;
265 }
266 }
267
268 return slowWrite(*pd, vpd, result, isUndefined, flags);
269 }
270
271 template <typename T>
272 Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData *pd, QQmlPropertyData::WriteFlags flags) const
273 {
274 void *o = &value;
275 return pd->writeProperty(target: targetObject(), value: o, flags);
276 }
277};
278
279class QQmlTranslationBinding : public GenericBinding<QMetaType::QString>, public QPropertyObserver {
280public:
281 QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
282 : QPropertyObserver(&QQmlTranslationBinding::onLanguageChange)
283 {
284 setCompilationUnit(compilationUnit);
285 setSource(QQmlEnginePrivate::get(e: compilationUnit->engine)->translationLanguage);
286 }
287
288 virtual QString bindingValue() const = 0;
289
290 static void onLanguageChange(QPropertyObserver *observer, QUntypedPropertyData *)
291 { static_cast<QQmlTranslationBinding *>(observer)->update(); }
292
293 void doUpdate(const DeleteWatcher &watcher,
294 QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
295 {
296 if (watcher.wasDeleted())
297 return;
298
299 if (!isAddedToObject() || hasError())
300 return;
301
302 const QString result = this->bindingValue();
303
304 Q_ASSERT(targetObject());
305
306 const QQmlPropertyData *pd;
307 QQmlPropertyData vpd;
308 getPropertyData(propertyData: &pd, valueTypeData: &vpd);
309 Q_ASSERT(pd);
310 if (pd->propType().id() == QMetaType::QString) {
311 doStore(value: result, pd, flags);
312 } else {
313 QV4::ScopedString value(scope, scope.engine->newString(s: result));
314 slowWrite(core: *pd, valueTypeData: vpd, result: value, /*isUndefined*/false, flags);
315 }
316 }
317
318 bool hasDependencies() const override final { return true; }
319};
320
321class QQmlTranslationBindingFromBinding : public QQmlTranslationBinding
322{
323 const QV4::CompiledData::Binding *m_binding;
324
325public:
326 QQmlTranslationBindingFromBinding(
327 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
328 const QV4::CompiledData::Binding *binding)
329 : QQmlTranslationBinding(compilationUnit), m_binding(binding)
330 {
331 }
332
333 QString bindingValue() const override
334 {
335 return this->m_compilationUnit->bindingValueAsString(binding: m_binding);
336 }
337
338 QQmlSourceLocation sourceLocation() const override final
339 {
340 return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line(),
341 m_binding->valueLocation.column());
342 }
343};
344
345class QQmlTranslationBindingFromTranslationInfo : public QQmlTranslationBinding
346{
347 QQmlTranslation m_translationData;
348
349 quint16 m_line;
350 quint16 m_column;
351
352public:
353 QQmlTranslationBindingFromTranslationInfo(
354 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
355 const QQmlTranslation &translationData, quint16 line, quint16 column)
356 : QQmlTranslationBinding(compilationUnit),
357 m_translationData(translationData),
358 m_line(line),
359 m_column(column)
360 {
361 }
362
363 virtual QString bindingValue() const override { return m_translationData.translate(); }
364
365 QQmlSourceLocation sourceLocation() const override final
366 {
367 return QQmlSourceLocation(m_compilationUnit->fileName(), m_line, m_column);
368 }
369};
370
371QQmlBinding *QQmlBinding::createTranslationBinding(
372 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
373 const QV4::CompiledData::Binding *binding, QObject *obj,
374 const QQmlRefPointer<QQmlContextData> &ctxt)
375{
376 QQmlTranslationBinding *b = new QQmlTranslationBindingFromBinding(unit, binding);
377
378 b->setNotifyOnValueChanged(true);
379 b->QQmlJavaScriptExpression::setContext(ctxt);
380 b->setScopeObject(obj);
381#if QT_CONFIG(translation) && QT_CONFIG(qml_debug)
382 if (QQmlDebugTranslationService *service
383 = QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
384 service->foundTranslationBinding(
385 translationBindingInformation: TranslationBindingInformation::create(compilationUnit: unit, binding, scopeObject: b->scopeObject(), ctxt));
386 }
387#endif
388 return b;
389}
390
391QQmlBinding *QQmlBinding::createTranslationBinding(
392 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
393 const QQmlRefPointer<QQmlContextData> &ctxt, const QString &propertyName,
394 const QQmlTranslation &translationData, const QQmlSourceLocation &location, QObject *obj)
395{
396 QQmlTranslationBinding *b = new QQmlTranslationBindingFromTranslationInfo(
397 unit, translationData, location.column, location.line);
398
399 b->setNotifyOnValueChanged(true);
400 b->QQmlJavaScriptExpression::setContext(ctxt);
401 b->setScopeObject(obj);
402
403#if QT_CONFIG(translation) && QT_CONFIG(qml_debug)
404 QString originString;
405 if (QQmlDebugTranslationService *service =
406 QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
407 service->foundTranslationBinding(translationBindingInformation: { .compilationUnit: unit, .scopeObject: b->scopeObject(), .ctxt: ctxt,
408
409 .propertyName: propertyName, .translation: translationData,
410
411 .line: location.line, .column: location.column });
412 }
413#endif
414 return b;
415}
416
417bool QQmlBinding::slowWrite(
418 const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, const void *result,
419 QMetaType resultType, bool isUndefined, QQmlPropertyData::WriteFlags flags)
420{
421 // The logic in this method is obscure. It follows what the other slowWrite does, minus the type
422 // conversions and the checking for binding functions. If you're writing a C++ type, and
423 // you're passing a binding function wrapped into QJSValue, you probably want it to be assigned.
424
425 if (hasError())
426 return false;
427
428 QQmlEngine *qmlEngine = engine();
429 const QMetaType metaType = valueTypeData.isValid() ? valueTypeData.propType() : core.propType();
430 QQmlJavaScriptExpression::DeleteWatcher watcher(this);
431
432 if (core.isVarProperty()) {
433 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: m_target.data());
434 Q_ASSERT(vmemo);
435 vmemo->setVMEProperty(index: core.coreIndex(),
436 v: qmlEngine->handle()->metaTypeToJS(type: resultType, data: result));
437 } else if (isUndefined && core.isResettable()) {
438 void *args[] = { nullptr };
439 QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args);
440 } else if (isUndefined && metaType == QMetaType::fromType<QVariant>()) {
441 QQmlPropertyPrivate::writeValueProperty(
442 m_target.data(), core, valueTypeData, QVariant(), context(), flags);
443 } else if (metaType == QMetaType::fromType<QJSValue>()) {
444 QQmlPropertyPrivate::writeValueProperty(
445 m_target.data(), core, valueTypeData,
446 QVariant(resultType, result), context(), flags);
447 } else if (isUndefined) {
448 const char *name = metaType.name();
449 const QString typeName = name
450 ? QString::fromUtf8(utf8: name)
451 : QStringLiteral("[unknown property type]");
452 delayedError()->setErrorDescription(
453 QStringLiteral("Unable to assign [undefined] to ") + typeName);
454 return false;
455 } else if (!QQmlPropertyPrivate::writeValueProperty(
456 m_target.data(), core, valueTypeData, QVariant(resultType, result),
457 context(), flags)) {
458 if (watcher.wasDeleted())
459 return true;
460 handleWriteError(result, resultType, metaType);
461 return false;
462 }
463
464 return true;
465}
466
467Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
468 const QQmlPropertyData &valueTypeData,
469 const QV4::Value &result,
470 bool isUndefined, QQmlPropertyData::WriteFlags flags)
471{
472 const QMetaType metaType = valueTypeData.isValid() ? valueTypeData.propType() : core.propType();
473 const int type = metaType.id();
474
475 QQmlJavaScriptExpression::DeleteWatcher watcher(this);
476
477 QVariant value;
478 bool isVarProperty = core.isVarProperty();
479
480 if (isUndefined) {
481 } else if (core.isQList()) {
482 if (core.propType().flags() & QMetaType::IsQmlList)
483 value = QV4::ExecutionEngine::toVariant(value: result, typeHint: QMetaType::fromType<QList<QObject*>>());
484 else
485 value = QV4::ExecutionEngine::toVariant(value: result, typeHint: core.propType());
486 } else if (result.isNull() && core.isQObject()) {
487 value = QVariant::fromValue(value: (QObject *)nullptr);
488 } else if (core.propType() == QMetaType::fromType<QList<QUrl>>()) {
489 const QVariant resultVariant
490 = QV4::ExecutionEngine::toVariant(value: result, typeHint: QMetaType::fromType<QList<QUrl>>());
491 value = QVariant::fromValue(value: QQmlPropertyPrivate::resolveUrlsOnAssignment()
492 ? QQmlPropertyPrivate::urlSequence(value: resultVariant, ctxt: context())
493 : QQmlPropertyPrivate::urlSequence(value: resultVariant));
494 } else if (!isVarProperty && metaType != QMetaType::fromType<QJSValue>()) {
495 value = QV4::ExecutionEngine::toVariant(value: result, typeHint: metaType);
496 }
497
498 if (hasError()) {
499 return false;
500 } else if (isVarProperty) {
501 const QV4::FunctionObject *f = result.as<QV4::FunctionObject>();
502 if (f && f->isBinding()) {
503 // we explicitly disallow this case to avoid confusion. Users can still store one
504 // in an array in a var property if they need to, but the common case is user error.
505 delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
506 return false;
507 }
508
509 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: m_target.data());
510 Q_ASSERT(vmemo);
511 vmemo->setVMEProperty(index: core.coreIndex(), v: result);
512 } else if (isUndefined
513 && (valueTypeData.isValid() ? valueTypeData.isResettable() : core.isResettable())) {
514 QQmlPropertyPrivate::resetValueProperty(
515 m_target.data(), core, valueTypeData, context(), flags);
516 } else if (isUndefined && type == QMetaType::QVariant) {
517 QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags);
518 } else if (metaType == QMetaType::fromType<QJSValue>()) {
519 const QV4::FunctionObject *f = result.as<QV4::FunctionObject>();
520 if (f && f->isBinding()) {
521 delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
522 return false;
523 }
524 QQmlPropertyPrivate::writeValueProperty(
525 m_target.data(), core, valueTypeData,
526 QVariant::fromValue(value: QJSValuePrivate::fromReturnedValue(d: result.asReturnedValue())),
527 context(), flags);
528 } else if (isUndefined) {
529 const char *name = QMetaType(type).name();
530 const QLatin1String typeName(name ? name : "[unknown property type]");
531 delayedError()->setErrorDescription(QLatin1String("Unable to assign [undefined] to ")
532 + typeName);
533 return false;
534 } else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>()) {
535 if (f->isBinding())
536 delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
537 else
538 delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
539 return false;
540 } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, value, context(), flags)) {
541
542 if (watcher.wasDeleted())
543 return true;
544 handleWriteError(result: value.constData(), resultType: value.metaType(), metaType);
545 return false;
546 }
547
548 return true;
549}
550
551void QQmlBinding::handleWriteError(const void *result, QMetaType resultType, QMetaType metaType)
552{
553 const char *valueType = nullptr;
554 const char *propertyType = nullptr;
555
556 if (resultType.flags() & QMetaType::PointerToQObject) {
557 if (QObject *o = *(QObject *const *)result) {
558 valueType = o->metaObject()->className();
559 QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(metaType);
560 if (!propertyMetaObject.isNull())
561 propertyType = propertyMetaObject.className();
562 }
563 } else if (resultType.isValid()) {
564 if (resultType == QMetaType::fromType<std::nullptr_t>()
565 || resultType == QMetaType::fromType<void *>()) {
566 valueType = "null";
567 } else {
568 valueType = resultType.name();
569 }
570 }
571
572 if (!valueType)
573 valueType = "undefined";
574 if (!propertyType)
575 propertyType = metaType.name();
576 if (!propertyType)
577 propertyType = "[unknown property type]";
578
579 delayedError()->setErrorDescription(QStringLiteral("Unable to assign ")
580 + QString::fromUtf8(utf8: valueType)
581 + QStringLiteral(" to ")
582 + QString::fromUtf8(utf8: propertyType));
583}
584
585QVariant QQmlBinding::evaluate()
586{
587 QQmlEngine *qmlEngine = engine();
588 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: qmlEngine);
589 ep->referenceScarceResources();
590
591 bool isUndefined = false;
592
593 QV4::Scope scope(qmlEngine->handle());
594 QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(isUndefined: &isUndefined));
595
596 ep->dereferenceScarceResources();
597
598 return QV4::ExecutionEngine::toVariant(value: result, typeHint: QMetaType::fromType<QList<QObject*> >());
599}
600
601void QQmlBinding::expressionChanged()
602{
603 update();
604}
605
606void QQmlBinding::refresh()
607{
608 update();
609}
610
611void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
612{
613 const bool wasEnabled = enabledFlag();
614 setEnabledFlag(e);
615 setNotifyOnValueChanged(e);
616 updateCanUseAccessor();
617
618 if (e && !wasEnabled)
619 update(flags);
620}
621
622QString QQmlBinding::expression() const
623{
624 return QStringLiteral("function() { [native code] }");
625}
626
627QVector<QQmlProperty> QQmlBinding::dependencies() const
628{
629 QVector<QQmlProperty> dependencies;
630 if (!m_target.data())
631 return dependencies;
632
633 for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(v: guard)) {
634 if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
635 continue;
636
637 QObject *senderObject = guard->senderAsObject();
638 if (!senderObject)
639 continue;
640
641 const QMetaObject *senderMeta = senderObject->metaObject();
642 if (!senderMeta)
643 continue;
644
645 for (int i = 0; i < senderMeta->propertyCount(); i++) {
646 QMetaProperty property = senderMeta->property(index: i);
647 if (property.notifySignalIndex() == QMetaObjectPrivate::signal(m: senderMeta, signal_index: guard->signalIndex()).methodIndex()) {
648 dependencies.push_back(t: QQmlProperty(senderObject, QString::fromUtf8(utf8: property.name())));
649 }
650 }
651 }
652
653 for (auto trigger = qpropertyChangeTriggers; trigger; trigger = trigger->next) {
654 QMetaProperty prop = trigger->property();
655 if (prop.isValid())
656 dependencies.push_back(t: QQmlProperty(trigger->target, QString::fromUtf8(utf8: prop.name())));
657 }
658
659 return dependencies;
660}
661
662bool QQmlBinding::hasDependencies() const
663{
664 return !activeGuards.isEmpty() || qpropertyChangeTriggers;
665}
666
667void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope)
668{
669 auto ep = QQmlEnginePrivate::get(e: scope.engine);
670 ep->referenceScarceResources();
671
672 bool error = false;
673 auto canWrite = [&]() { return !watcher.wasDeleted() && isAddedToObject() && !hasError(); };
674 const QV4::Function *v4Function = function();
675 if (v4Function && v4Function->kind == QV4::Function::AotCompiled && !hasBoundFunction()) {
676 const auto returnType = v4Function->aotCompiledFunction->returnType;
677 if (returnType == QMetaType::fromType<QVariant>()) {
678 // It expects uninitialized memory
679 Q_ALLOCA_VAR(QVariant, result, sizeof(QVariant));
680 const bool isUndefined = !evaluate(result, type: returnType);
681 if (canWrite())
682 error = !write(result: result->data(), type: result->metaType(), isUndefined, flags);
683 result->~QVariant();
684 } else {
685 const auto size = returnType.sizeOf();
686 if (Q_LIKELY(size > 0)) {
687 Q_ALLOCA_VAR(void, result, size);
688 const bool isUndefined = !evaluate(result, type: returnType);
689 if (canWrite())
690 error = !write(result, type: returnType, isUndefined, flags);
691 returnType.destruct(data: result);
692 } else if (canWrite()) {
693 error = !write(result: QV4::Encode::undefined(), isUndefined: true, flags);
694 }
695 }
696 } else {
697 bool isUndefined = false;
698 QV4::ScopedValue result(scope, evaluate(isUndefined: &isUndefined));
699 if (canWrite())
700 error = !write(result, isUndefined, flags);
701 }
702
703 if (!watcher.wasDeleted()) {
704
705 if (error) {
706 delayedError()->setErrorLocation(sourceLocation());
707 delayedError()->setErrorObject(m_target.data());
708 }
709
710 if (hasError()) {
711 if (!delayedError()->addError(ep)) ep->warning(this->error(engine()));
712 } else {
713 clearError();
714 }
715 }
716
717 ep->dereferenceScarceResources();
718}
719
720class QObjectPointerBinding: public QQmlBinding
721{
722 QQmlMetaObject targetMetaObject;
723
724public:
725 QObjectPointerBinding(QMetaType propertyType)
726 : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(metaType: propertyType))
727 {}
728
729protected:
730 Q_ALWAYS_INLINE bool write(void *result, QMetaType type, bool isUndefined,
731 QQmlPropertyData::WriteFlags flags) override final
732 {
733 const QQmlPropertyData *pd;
734 QQmlPropertyData vtpd;
735 getPropertyData(propertyData: &pd, valueTypeData: &vtpd);
736 if (Q_UNLIKELY(isUndefined || vtpd.isValid()))
737 return slowWrite(core: *pd, valueTypeData: vtpd, result, resultType: type, isUndefined, flags);
738
739 // Check if the result is a QObject:
740 QObject *resultObject = nullptr;
741 QQmlMetaObject resultMo;
742 const auto typeFlags = type.flags();
743 if (!result || ((typeFlags & QMetaType::IsPointer) && !*static_cast<void **>(result))) {
744 // Special case: we can always write a nullptr. Don't bother checking anything else.
745 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
746 } else if (typeFlags & QMetaType::PointerToQObject) {
747 resultObject = *static_cast<QObject **>(result);
748 if (!resultObject)
749 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
750 if (QQmlData *ddata = QQmlData::get(object: resultObject, create: false))
751 resultMo = ddata->propertyCache;
752 if (resultMo.isNull())
753 resultMo = resultObject->metaObject();
754 } else if (type == QMetaType::fromType<QVariant>()) {
755 const QVariant value = *static_cast<QVariant *>(result);
756 resultMo = QQmlPropertyPrivate::rawMetaObjectForType(metaType: value.metaType());
757 if (resultMo.isNull())
758 return slowWrite(core: *pd, valueTypeData: vtpd, result, resultType: type, isUndefined, flags);
759 resultObject = *static_cast<QObject *const *>(value.constData());
760 } else {
761 return slowWrite(core: *pd, valueTypeData: vtpd, result, resultType: type, isUndefined, flags);
762 }
763
764 return compareAndSet(resultMo, resultObject, pd, flags, slowWrite: [&]() {
765 return slowWrite(core: *pd, valueTypeData: vtpd, result, resultType: type, isUndefined, flags);
766 });
767 }
768
769 Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
770 QQmlPropertyData::WriteFlags flags) override final
771 {
772 const QQmlPropertyData *pd;
773 QQmlPropertyData vtpd;
774 getPropertyData(propertyData: &pd, valueTypeData: &vtpd);
775 if (Q_UNLIKELY(isUndefined || vtpd.isValid()))
776 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
777
778 // Check if the result is a QObject:
779 QObject *resultObject = nullptr;
780 QQmlMetaObject resultMo;
781 if (result.isNull()) {
782 // Special case: we can always write a nullptr. Don't bother checking anything else.
783 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
784 } else if (auto wrapper = result.as<QV4::QObjectWrapper>()) {
785 resultObject = wrapper->object();
786 if (!resultObject)
787 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
788 if (QQmlData *ddata = QQmlData::get(object: resultObject, create: false))
789 resultMo = ddata->propertyCache;
790 if (resultMo.isNull()) {
791 resultMo = resultObject->metaObject();
792 }
793 } else if (auto variant = result.as<QV4::VariantObject>()) {
794 const QVariant value = variant->d()->data();
795 resultMo = QQmlPropertyPrivate::rawMetaObjectForType(metaType: value.metaType());
796 if (resultMo.isNull())
797 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
798 resultObject = *static_cast<QObject *const *>(value.constData());
799 } else {
800 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
801 }
802
803 return compareAndSet(resultMo, resultObject, pd, flags, slowWrite: [&]() {
804 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
805 });
806 }
807
808private:
809 using QQmlBinding::slowWrite;
810
811 template<typename SlowWrite>
812 bool compareAndSet(const QQmlMetaObject &resultMo, QObject *resultObject, const QQmlPropertyData *pd,
813 QQmlPropertyData::WriteFlags flags, const SlowWrite &slowWrite) const
814 {
815 if (QQmlMetaObject::canConvert(from: resultMo, to: targetMetaObject)) {
816 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
817 } else if (!resultObject && QQmlMetaObject::canConvert(from: targetMetaObject, to: resultMo)) {
818 // In the case of a null QObject, we assign the null if there is
819 // any change that the null variant type could be up or down cast to
820 // the property type.
821 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
822 } else {
823 return slowWrite();
824 }
825 }
826};
827
828QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property)
829{
830 return newBinding(propertyType: property ? property->propType() : QMetaType());
831}
832
833QQmlBinding *QQmlBinding::newBinding(QMetaType propertyType)
834{
835 if (propertyType.flags() & QMetaType::PointerToQObject)
836 return new QObjectPointerBinding(propertyType);
837
838 switch (propertyType.id()) {
839 case QMetaType::Bool:
840 return new GenericBinding<QMetaType::Bool>;
841 case QMetaType::Int:
842 return new GenericBinding<QMetaType::Int>;
843 case QMetaType::Double:
844 return new GenericBinding<QMetaType::Double>;
845 case QMetaType::Float:
846 return new GenericBinding<QMetaType::Float>;
847 case QMetaType::QString:
848 return new GenericBinding<QMetaType::QString>;
849 default:
850 return new GenericBinding<QMetaType::UnknownType>;
851 }
852}
853
854QT_END_NAMESPACE
855

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