1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlbinding_p.h"
41
42#include "qqml.h"
43#include "qqmlcontext.h"
44#include "qqmlinfo.h"
45#include "qqmldata_p.h"
46#include <private/qqmlprofiler_p.h>
47#include <private/qqmlexpression_p.h>
48#include <private/qqmlscriptstring_p.h>
49#include <private/qqmlbuiltinfunctions_p.h>
50#include <private/qqmlvmemetaobject_p.h>
51#include <private/qqmlvaluetypewrapper_p.h>
52#include <private/qv4qmlcontext_p.h>
53#include <private/qv4qobjectwrapper_p.h>
54#include <private/qv4variantobject_p.h>
55#include <private/qv4jscall_p.h>
56
57#include <QVariant>
58#include <QtCore/qdebug.h>
59#include <QVector>
60
61QT_BEGIN_NAMESPACE
62
63QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
64{
65 QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
66
67 if (ctxt && !ctxt->isValid())
68 return b;
69
70 const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
71 if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
72 return b;
73
74 QString url;
75 QV4::Function *runtimeFunction = nullptr;
76
77 QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context);
78 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
79 if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit) {
80 url = ctxtdata->urlString();
81 if (scriptPrivate->bindingId != QQmlBinding::Invalid)
82 runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(scriptPrivate->bindingId);
83 }
84
85 b->setNotifyOnValueChanged(true);
86 b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
87 b->setScopeObject(obj ? obj : scriptPrivate->scope);
88
89 QV4::ExecutionEngine *v4 = b->context()->engine->handle();
90 if (runtimeFunction) {
91 QV4::Scope scope(v4);
92 QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, b->scopeObject()));
93 b->setupFunction(qmlContext, runtimeFunction);
94 } else {
95 QString code = scriptPrivate->script;
96 b->createQmlBinding(b->context(), b->scopeObject(), code, url, scriptPrivate->lineNumber);
97 }
98
99 return b;
100}
101
102QQmlSourceLocation QQmlBinding::sourceLocation() const
103{
104 if (m_sourceLocation)
105 return *m_sourceLocation;
106 return QQmlJavaScriptExpression::sourceLocation();
107}
108
109void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location)
110{
111 if (m_sourceLocation)
112 delete m_sourceLocation;
113 m_sourceLocation = new QQmlSourceLocation(location);
114}
115
116
117QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj,
118 QQmlContextData *ctxt, const QString &url, quint16 lineNumber)
119{
120 QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
121
122 b->setNotifyOnValueChanged(true);
123 b->QQmlJavaScriptExpression::setContext(ctxt);
124 b->setScopeObject(obj);
125
126 b->createQmlBinding(ctxt, obj, str, url, lineNumber);
127
128 return b;
129}
130
131QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function *function,
132 QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope)
133{
134 QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
135
136 b->setNotifyOnValueChanged(true);
137 b->QQmlJavaScriptExpression::setContext(ctxt);
138 b->setScopeObject(obj);
139
140 Q_ASSERT(scope);
141 b->setupFunction(scope, function);
142
143 return b;
144}
145
146QQmlBinding::~QQmlBinding()
147{
148 delete m_sourceLocation;
149}
150
151void QQmlBinding::setNotifyOnValueChanged(bool v)
152{
153 QQmlJavaScriptExpression::setNotifyOnValueChanged(v);
154}
155
156void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
157{
158 if (!enabledFlag() || !context() || !context()->isValid())
159 return;
160
161 // Check that the target has not been deleted
162 if (QQmlData::wasDeleted(targetObject()))
163 return;
164
165 // Check for a binding update loop
166 if (Q_UNLIKELY(updatingFlag())) {
167 QQmlPropertyData *d = nullptr;
168 QQmlPropertyData vtd;
169 getPropertyData(&d, &vtd);
170 Q_ASSERT(d);
171 QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, nullptr);
172 QQmlAbstractBinding::printBindingLoopError(p);
173 return;
174 }
175 setUpdatingFlag(true);
176
177 DeleteWatcher watcher(this);
178
179 QQmlEngine *engine = context()->engine;
180 QV4::Scope scope(engine->handle());
181
182 if (canUseAccessor())
183 flags.setFlag(QQmlPropertyData::BypassInterceptor);
184
185 QQmlBindingProfiler prof(QQmlEnginePrivate::get(engine)->profiler, function());
186 doUpdate(watcher, flags, scope);
187
188 if (!watcher.wasDeleted())
189 setUpdatingFlag(false);
190}
191
192QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
193{
194 QV4::ExecutionEngine *v4 = context()->engine->handle();
195 int argc = 0;
196 const QV4::Value *argv = nullptr;
197 const QV4::Value *thisObject = nullptr;
198 QV4::BoundFunction *b = nullptr;
199 if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) {
200 QV4::Heap::MemberData *args = b->boundArgs();
201 if (args) {
202 argc = args->values.size;
203 argv = args->values.data();
204 }
205 thisObject = &b->d()->boundThis;
206 }
207 QV4::Scope scope(v4);
208 QV4::JSCallData jsCall(scope, argc, argv, thisObject);
209
210 return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined);
211}
212
213
214// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or
215// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant
216// expression for the switch for the compiler to generate the optimal code, but
217// qMetaTypeId<QQmlBinding *>() needs to be used for the ID. So QQmlBinding::newBinding uses that
218// to instantiate this class.
219class QQmlBindingBinding: public QQmlBinding
220{
221protected:
222 void doUpdate(const DeleteWatcher &,
223 QQmlPropertyData::WriteFlags flags, QV4::Scope &) override final
224 {
225 Q_ASSERT(!m_targetIndex.hasValueTypeIndex());
226 QQmlPropertyData *pd = nullptr;
227 getPropertyData(&pd, nullptr);
228 QQmlBinding *thisPtr = this;
229 pd->writeProperty(*m_target, &thisPtr, flags);
230 }
231};
232
233// For any target that's not a binding, we have a common doUpdate. However, depending on the type
234// of the target property, there is a specialized write method.
235class QQmlNonbindingBinding: public QQmlBinding
236{
237protected:
238 void doUpdate(const DeleteWatcher &watcher,
239 QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override
240 {
241 auto ep = QQmlEnginePrivate::get(scope.engine);
242 ep->referenceScarceResources();
243
244 bool isUndefined = false;
245
246 QV4::ScopedValue result(scope, evaluate(&isUndefined));
247
248 bool error = false;
249 if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
250 error = !write(result, isUndefined, flags);
251
252 if (!watcher.wasDeleted()) {
253
254 if (error) {
255 delayedError()->setErrorLocation(sourceLocation());
256 delayedError()->setErrorObject(m_target.data());
257 }
258
259 if (hasError()) {
260 if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine));
261 } else {
262 clearError();
263 }
264 }
265
266 ep->dereferenceScarceResources();
267 }
268
269 virtual bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0;
270};
271
272template<int StaticPropType>
273class GenericBinding: public QQmlNonbindingBinding
274{
275protected:
276 // Returns true if successful, false if an error description was set on expression
277 Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
278 QQmlPropertyData::WriteFlags flags) override final
279 {
280 Q_ASSERT(targetObject());
281
282 QQmlPropertyData *pd;
283 QQmlPropertyData vpd;
284 getPropertyData(&pd, &vpd);
285 Q_ASSERT(pd);
286
287 int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded.
288 if (propertyType == QMetaType::UnknownType)
289 propertyType = pd->propType();
290
291 if (Q_LIKELY(!isUndefined && !vpd.isValid())) {
292 switch (propertyType) {
293 case QMetaType::Bool:
294 if (result.isBoolean())
295 return doStore<bool>(result.booleanValue(), pd, flags);
296 else
297 return doStore<bool>(result.toBoolean(), pd, flags);
298 case QMetaType::Int:
299 if (result.isInteger())
300 return doStore<int>(result.integerValue(), pd, flags);
301 else if (result.isNumber())
302 return doStore<int>(result.doubleValue(), pd, flags);
303 break;
304 case QMetaType::Double:
305 if (result.isNumber())
306 return doStore<double>(result.asDouble(), pd, flags);
307 break;
308 case QMetaType::Float:
309 if (result.isNumber())
310 return doStore<float>(result.asDouble(), pd, flags);
311 break;
312 case QMetaType::QString:
313 if (result.isString())
314 return doStore<QString>(result.toQStringNoThrow(), pd, flags);
315 break;
316 default:
317 if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
318 if (vtw->d()->valueType->metaType.id() == pd->propType()) {
319 return vtw->write(m_target.data(), pd->coreIndex());
320 }
321 }
322 break;
323 }
324 }
325
326 return slowWrite(*pd, vpd, result, isUndefined, flags);
327 }
328
329 template <typename T>
330 Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData *pd, QQmlPropertyData::WriteFlags flags) const
331 {
332 void *o = &value;
333 return pd->writeProperty(targetObject(), o, flags);
334 }
335};
336
337class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
338public:
339 QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
340 {
341 setCompilationUnit(compilationUnit);
342 m_binding = binding;
343 }
344
345 QQmlSourceLocation sourceLocation() const override final
346 {
347 return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
348 }
349
350
351 void doUpdate(const DeleteWatcher &watcher,
352 QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
353 {
354 if (watcher.wasDeleted())
355 return;
356
357 if (!isAddedToObject() || hasError())
358 return;
359
360 const QString result = m_compilationUnit->bindingValueAsString(m_binding);
361
362 Q_ASSERT(targetObject());
363
364 QQmlPropertyData *pd;
365 QQmlPropertyData vpd;
366 getPropertyData(&pd, &vpd);
367 Q_ASSERT(pd);
368 if (pd->propType() == QMetaType::QString) {
369 doStore(result, pd, flags);
370 } else {
371 QV4::ScopedString value(scope, scope.engine->newString(result));
372 slowWrite(*pd, vpd, value, /*isUndefined*/false, flags);
373 }
374 }
375
376 bool hasDependencies() const override final { return true; }
377
378private:
379 const QV4::CompiledData::Binding *m_binding;
380};
381
382QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
383{
384 QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding);
385
386 b->setNotifyOnValueChanged(true);
387 b->QQmlJavaScriptExpression::setContext(ctxt);
388 b->setScopeObject(obj);
389
390 return b;
391}
392
393Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
394 const QQmlPropertyData &valueTypeData,
395 const QV4::Value &result,
396 bool isUndefined, QQmlPropertyData::WriteFlags flags)
397{
398 QQmlEngine *engine = context()->engine;
399 QV4::ExecutionEngine *v4engine = engine->handle();
400
401 int type = valueTypeData.isValid() ? valueTypeData.propType() : core.propType();
402
403 QQmlJavaScriptExpression::DeleteWatcher watcher(this);
404
405 QVariant value;
406 bool isVarProperty = core.isVarProperty();
407
408 if (isUndefined) {
409 } else if (core.isQList()) {
410 value = v4engine->toVariant(result, qMetaTypeId<QList<QObject *> >());
411 } else if (result.isNull() && core.isQObject()) {
412 value = QVariant::fromValue((QObject *)nullptr);
413 } else if (core.propType() == qMetaTypeId<QList<QUrl> >()) {
414 value = QQmlPropertyPrivate::resolvedUrlSequence(v4engine->toVariant(result, qMetaTypeId<QList<QUrl> >()), context());
415 } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) {
416 value = v4engine->toVariant(result, type);
417 }
418
419 if (hasError()) {
420 return false;
421 } else if (isVarProperty) {
422 const QV4::FunctionObject *f = result.as<QV4::FunctionObject>();
423 if (f && f->isBinding()) {
424 // we explicitly disallow this case to avoid confusion. Users can still store one
425 // in an array in a var property if they need to, but the common case is user error.
426 delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
427 return false;
428 }
429
430 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data());
431 Q_ASSERT(vmemo);
432 vmemo->setVMEProperty(core.coreIndex(), result);
433 } else if (isUndefined && core.isResettable()) {
434 void *args[] = { nullptr };
435 QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args);
436 } else if (isUndefined && type == qMetaTypeId<QVariant>()) {
437 QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags);
438 } else if (type == qMetaTypeId<QJSValue>()) {
439 const QV4::FunctionObject *f = result.as<QV4::FunctionObject>();
440 if (f && f->isBinding()) {
441 delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
442 return false;
443 }
444 QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant::fromValue(
445 QJSValue(v4engine, result.asReturnedValue())),
446 context(), flags);
447 } else if (isUndefined) {
448 const QLatin1String typeName(QMetaType::typeName(type)
449 ? QMetaType::typeName(type)
450 : "[unknown property type]");
451 delayedError()->setErrorDescription(QLatin1String("Unable to assign [undefined] to ")
452 + typeName);
453 return false;
454 } else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>()) {
455 if (f->isBinding())
456 delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
457 else
458 delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
459 return false;
460 } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, value, context(), flags)) {
461
462 if (watcher.wasDeleted())
463 return true;
464
465 const char *valueType = nullptr;
466 const char *propertyType = nullptr;
467
468 const int userType = value.userType();
469 if (userType == QMetaType::QObjectStar) {
470 if (QObject *o = *(QObject *const *)value.constData()) {
471 valueType = o->metaObject()->className();
472
473 QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate::get(engine), type);
474 if (!propertyMetaObject.isNull())
475 propertyType = propertyMetaObject.className();
476 }
477 } else if (userType != QVariant::Invalid) {
478 if (userType == QMetaType::Nullptr || userType == QMetaType::VoidStar)
479 valueType = "null";
480 else
481 valueType = QMetaType::typeName(userType);
482 }
483
484 if (!valueType)
485 valueType = "undefined";
486 if (!propertyType)
487 propertyType = QMetaType::typeName(type);
488 if (!propertyType)
489 propertyType = "[unknown property type]";
490
491 delayedError()->setErrorDescription(QLatin1String("Unable to assign ") +
492 QLatin1String(valueType) +
493 QLatin1String(" to ") +
494 QLatin1String(propertyType));
495 return false;
496 }
497
498 return true;
499}
500
501QVariant QQmlBinding::evaluate()
502{
503 QQmlEngine *engine = context()->engine;
504 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
505 ep->referenceScarceResources();
506
507 bool isUndefined = false;
508
509 QV4::Scope scope(engine->handle());
510 QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
511
512 ep->dereferenceScarceResources();
513
514 return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >());
515}
516
517QString QQmlBinding::expressionIdentifier() const
518{
519 if (auto f = function()) {
520 QString url = f->sourceFile();
521 uint lineNumber = f->compiledFunction->location.line;
522 uint columnNumber = f->compiledFunction->location.column;
523 return url + QString::asprintf(":%u:%u", lineNumber, columnNumber);
524 }
525
526 return QStringLiteral("[native code]");
527}
528
529void QQmlBinding::expressionChanged()
530{
531 update();
532}
533
534void QQmlBinding::refresh()
535{
536 update();
537}
538
539void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
540{
541 const bool wasEnabled = enabledFlag();
542 setEnabledFlag(e);
543 setNotifyOnValueChanged(e);
544
545 m_nextBinding.setFlag2(); // Always use accessors, only not when:
546 if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) {
547 if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(m_targetIndex))
548 m_nextBinding.clearFlag2();
549 }
550
551 if (e && !wasEnabled)
552 update(flags);
553}
554
555QString QQmlBinding::expression() const
556{
557 return QStringLiteral("function() { [native code] }");
558}
559
560void QQmlBinding::setTarget(const QQmlProperty &prop)
561{
562 auto pd = QQmlPropertyPrivate::get(prop);
563 setTarget(prop.object(), pd->core, &pd->valueTypeData);
564}
565
566bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
567{
568 m_target = object;
569
570 if (!object) {
571 m_targetIndex = QQmlPropertyIndex();
572 return false;
573 }
574
575 int coreIndex = core.coreIndex();
576 int valueTypeIndex = valueType ? valueType->coreIndex() : -1;
577 for (bool isAlias = core.isAlias(); isAlias; ) {
578 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
579
580 int aValueTypeIndex;
581 if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
582 // can't resolve id (yet)
583 m_target = nullptr;
584 m_targetIndex = QQmlPropertyIndex();
585 return false;
586 }
587 if (valueTypeIndex == -1)
588 valueTypeIndex = aValueTypeIndex;
589
590 QQmlData *data = QQmlData::get(object, false);
591 if (!data || !data->propertyCache) {
592 m_target = nullptr;
593 m_targetIndex = QQmlPropertyIndex();
594 return false;
595 }
596 QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
597 Q_ASSERT(propertyData);
598
599 m_target = object;
600 isAlias = propertyData->isAlias();
601 coreIndex = propertyData->coreIndex();
602 }
603 m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex);
604
605 QQmlData *data = QQmlData::get(*m_target, true);
606 if (!data->propertyCache) {
607 data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject());
608 data->propertyCache->addref();
609 }
610
611 return true;
612}
613
614void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
615{
616 Q_ASSERT(propertyData);
617
618 QQmlData *data = QQmlData::get(*m_target, false);
619 Q_ASSERT(data);
620
621 if (Q_UNLIKELY(!data->propertyCache)) {
622 data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject());
623 data->propertyCache->addref();
624 }
625
626 *propertyData = data->propertyCache->property(m_targetIndex.coreIndex());
627 Q_ASSERT(*propertyData);
628
629 if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) {
630 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType((*propertyData)->propType());
631 Q_ASSERT(valueTypeMetaObject);
632 QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex());
633 valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
634 valueTypeData->setPropType(vtProp.userType());
635 valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
636 }
637}
638
639QVector<QQmlProperty> QQmlBinding::dependencies() const
640{
641 QVector<QQmlProperty> dependencies;
642 if (!m_target.data())
643 return dependencies;
644
645 for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(guard)) {
646 if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
647 continue;
648
649 QObject *senderObject = guard->senderAsObject();
650 if (!senderObject)
651 continue;
652
653 const QMetaObject *senderMeta = senderObject->metaObject();
654 if (!senderMeta)
655 continue;
656
657 for (int i = 0; i < senderMeta->propertyCount(); i++) {
658 QMetaProperty property = senderMeta->property(i);
659 if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
660 dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
661 }
662 }
663 }
664
665 return dependencies;
666}
667
668bool QQmlBinding::hasDependencies() const
669{
670 return !activeGuards.isEmpty() || translationsCaptured();
671}
672
673class QObjectPointerBinding: public QQmlNonbindingBinding
674{
675 QQmlMetaObject targetMetaObject;
676
677public:
678 QObjectPointerBinding(QQmlEnginePrivate *engine, int propertyType)
679 : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(engine, propertyType))
680 {}
681
682protected:
683 Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
684 QQmlPropertyData::WriteFlags flags) override final
685 {
686 QQmlPropertyData *pd;
687 QQmlPropertyData vtpd;
688 getPropertyData(&pd, &vtpd);
689 if (Q_UNLIKELY(isUndefined || vtpd.isValid()))
690 return slowWrite(*pd, vtpd, result, isUndefined, flags);
691
692 // Check if the result is a QObject:
693 QObject *resultObject = nullptr;
694 QQmlMetaObject resultMo;
695 if (result.isNull()) {
696 // Special case: we can always write a nullptr. Don't bother checking anything else.
697 return pd->writeProperty(targetObject(), &resultObject, flags);
698 } else if (auto wrapper = result.as<QV4::QObjectWrapper>()) {
699 resultObject = wrapper->object();
700 if (!resultObject)
701 return pd->writeProperty(targetObject(), &resultObject, flags);
702 if (QQmlData *ddata = QQmlData::get(resultObject, false))
703 resultMo = ddata->propertyCache;
704 if (resultMo.isNull()) {
705 resultMo = resultObject->metaObject();
706 }
707 } else if (auto variant = result.as<QV4::VariantObject>()) {
708 QVariant value = variant->d()->data();
709 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context());
710 resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType());
711 if (resultMo.isNull())
712 return slowWrite(*pd, vtpd, result, isUndefined, flags);
713 resultObject = *static_cast<QObject *const *>(value.constData());
714 } else {
715 return slowWrite(*pd, vtpd, result, isUndefined, flags);
716 }
717
718 // Compare & set:
719 if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) {
720 return pd->writeProperty(targetObject(), &resultObject, flags);
721 } else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) {
722 // In the case of a null QObject, we assign the null if there is
723 // any change that the null variant type could be up or down cast to
724 // the property type.
725 return pd->writeProperty(targetObject(), &resultObject, flags);
726 } else {
727 return slowWrite(*pd, vtpd, result, isUndefined, flags);
728 }
729 }
730};
731
732QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property)
733{
734 if (property && property->isQObject())
735 return new QObjectPointerBinding(engine, property->propType());
736
737 const int type = (property && property->isFullyResolved()) ? property->propType() : QMetaType::UnknownType;
738
739 if (type == qMetaTypeId<QQmlBinding *>()) {
740 return new QQmlBindingBinding;
741 }
742
743 switch (type) {
744 case QMetaType::Bool:
745 return new GenericBinding<QMetaType::Bool>;
746 case QMetaType::Int:
747 return new GenericBinding<QMetaType::Int>;
748 case QMetaType::Double:
749 return new GenericBinding<QMetaType::Double>;
750 case QMetaType::Float:
751 return new GenericBinding<QMetaType::Float>;
752 case QMetaType::QString:
753 return new GenericBinding<QMetaType::QString>;
754 default:
755 return new GenericBinding<QMetaType::UnknownType>;
756 }
757}
758
759QT_END_NAMESPACE
760