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