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
67QT_BEGIN_NAMESPACE
68
69QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
70{
71 QQmlBinding *b = newBinding(engine: QQmlEnginePrivate::get(c: 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(context: scriptPrivate->context);
84 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(e: 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(i: scriptPrivate->bindingId);
89 }
90
91 b->setNotifyOnValueChanged(true);
92 b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(context: 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(parent: v4->rootContext(), context: ctxtdata, scopeObject: b->scopeObject()));
99 b->setupFunction(qmlContext, f: runtimeFunction);
100 } else {
101 QString code = scriptPrivate->script;
102 b->createQmlBinding(ctxt: b->context(), scope: b->scopeObject(), code, filename: url, line: scriptPrivate->lineNumber);
103 }
104
105 return b;
106}
107
108QQmlSourceLocation QQmlBinding::sourceLocation() const
109{
110 if (m_sourceLocation)
111 return *m_sourceLocation;
112 return QQmlJavaScriptExpression::sourceLocation();
113}
114
115void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location)
116{
117 if (m_sourceLocation)
118 delete m_sourceLocation;
119 m_sourceLocation = new QQmlSourceLocation(location);
120}
121
122
123QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj,
124 QQmlContextData *ctxt, const QString &url, quint16 lineNumber)
125{
126 QQmlBinding *b = newBinding(engine: QQmlEnginePrivate::get(c: ctxt), property);
127
128 b->setNotifyOnValueChanged(true);
129 b->QQmlJavaScriptExpression::setContext(ctxt);
130 b->setScopeObject(obj);
131
132 b->createQmlBinding(ctxt, scope: obj, code: str, filename: url, line: lineNumber);
133
134 return b;
135}
136
137QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function *function,
138 QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope)
139{
140 QQmlBinding *b = newBinding(engine: QQmlEnginePrivate::get(c: ctxt), property);
141
142 b->setNotifyOnValueChanged(true);
143 b->QQmlJavaScriptExpression::setContext(ctxt);
144 b->setScopeObject(obj);
145
146 Q_ASSERT(scope);
147 b->setupFunction(qmlContext: scope, f: function);
148
149 return b;
150}
151
152QQmlBinding::~QQmlBinding()
153{
154 delete m_sourceLocation;
155}
156
157void QQmlBinding::setNotifyOnValueChanged(bool v)
158{
159 QQmlJavaScriptExpression::setNotifyOnValueChanged(v);
160}
161
162void 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(object: 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(propertyData: &d, valueTypeData: &vtd);
176 Q_ASSERT(d);
177 QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, nullptr);
178 QQmlAbstractBinding::printBindingLoopError(prop&: 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(flag: 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(e: engine)->profiler, function());
194 doUpdate(watcher, flags, scope);
195
196 if (!watcher.wasDeleted())
197 setUpdatingFlag(false);
198}
199
200QV4::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(callData: 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.
227class QQmlBindingBinding: public QQmlBinding
228{
229protected:
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(propertyData: &pd, valueTypeData: nullptr);
236 QQmlBinding *thisPtr = this;
237 pd->writeProperty(target: *m_target, value: &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.
243class QQmlNonbindingBinding: public QQmlBinding
244{
245protected:
246 void doUpdate(const DeleteWatcher &watcher,
247 QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override
248 {
249 auto ep = QQmlEnginePrivate::get(e: scope.engine);
250 ep->referenceScarceResources();
251
252 bool isUndefined = false;
253
254 QV4::ScopedValue result(scope, evaluate(isUndefined: &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
280template<int StaticPropType>
281class GenericBinding: public QQmlNonbindingBinding
282{
283protected:
284 // Returns true if successful, false if an error description was set on expression
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(propertyData: &pd, valueTypeData: &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(d: 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(target: m_target.data(), propertyIndex: pd->coreIndex());
329 }
330 }
331 break;
332 }
333 }
334
335 return slowWrite(core: *pd, valueTypeData: vpd, result, isUndefined, flags);
336 }
337
338 template <typename T>
339 Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData *pd, QQmlPropertyData::WriteFlags flags) const
340 {
341 void *o = &value;
342 return pd->writeProperty(target: targetObject(), value: o, flags);
343 }
344};
345
346class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
347public:
348 QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
349 {
350 setCompilationUnit(compilationUnit);
351 m_binding = binding;
352 }
353
354 QQmlSourceLocation sourceLocation() const override final
355 {
356 return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
357 }
358
359
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(binding: m_binding);
370
371 Q_ASSERT(targetObject());
372
373 QQmlPropertyData *pd;
374 QQmlPropertyData vpd;
375 getPropertyData(propertyData: &pd, valueTypeData: &vpd);
376 Q_ASSERT(pd);
377 if (pd->propType() == QMetaType::QString) {
378 doStore(value: result, pd, flags);
379 } else {
380 QV4::ScopedString value(scope, scope.engine->newString(s: result));
381 slowWrite(core: *pd, valueTypeData: vpd, result: value, /*isUndefined*/false, flags);
382 }
383 }
384
385 bool hasDependencies() const override final { return true; }
386
387private:
388 const QV4::CompiledData::Binding *m_binding;
389};
390
391QQmlBinding *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(binding: b, scopeObject: obj, contextData: ctxt);
402 }
403
404 return b;
405}
406
407Q_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(value: result, typeHint: qMetaTypeId<QList<QObject *> >());
425 } else if (result.isNull() && core.isQObject()) {
426 value = QVariant::fromValue(value: (QObject *)nullptr);
427 } else if (core.propType() == qMetaTypeId<QList<QUrl> >()) {
428 value = QQmlPropertyPrivate::resolvedUrlSequence(value: v4engine->toVariant(value: result, typeHint: qMetaTypeId<QList<QUrl> >()), context: context());
429 } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) {
430 value = v4engine->toVariant(value: result, typeHint: 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(obj: m_target.data());
445 Q_ASSERT(vmemo);
446 vmemo->setVMEProperty(index: core.coreIndex(), v: 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 value: 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(e: 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(type: 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
515QVariant QQmlBinding::evaluate()
516{
517 QQmlEngine *engine = context()->engine;
518 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine);
519 ep->referenceScarceResources();
520
521 bool isUndefined = false;
522
523 QV4::Scope scope(engine->handle());
524 QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(isUndefined: &isUndefined));
525
526 ep->dereferenceScarceResources();
527
528 return scope.engine->toVariant(value: result, typeHint: qMetaTypeId<QList<QObject*> >());
529}
530
531QString 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(format: ":%u:%u", lineNumber, columnNumber);
538 }
539
540 return QStringLiteral("[native code]");
541}
542
543void QQmlBinding::expressionChanged()
544{
545 update();
546}
547
548void QQmlBinding::refresh()
549{
550 update();
551}
552
553void 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(obj: targetObject())) {
561 if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(propertyIndex: m_targetIndex))
562 m_nextBinding.clearFlag2();
563 }
564
565 if (e && !wasEnabled)
566 update(flags);
567}
568
569QString QQmlBinding::expression() const
570{
571 return QStringLiteral("function() { [native code] }");
572}
573
574void QQmlBinding::setTarget(const QQmlProperty &prop)
575{
576 auto pd = QQmlPropertyPrivate::get(p: prop);
577 setTarget(prop.object(), pd->core, valueType: &pd->valueTypeData);
578}
579
580bool 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(o: object, coreIndex);
593
594 int aValueTypeIndex;
595 if (!vme->aliasTarget(index: coreIndex, target: &object, coreIndex: &coreIndex, valueTypeIndex: &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, create: false);
605 if (!data || !data->propertyCache) {
606 m_target = nullptr;
607 m_targetIndex = QQmlPropertyIndex();
608 return false;
609 }
610 QQmlPropertyData *propertyData = data->propertyCache->property(index: 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(object: *m_target, create: true);
620 if (!data->propertyCache) {
621 data->propertyCache = QQmlEnginePrivate::get(e: context()->engine)->cache(metaObject: m_target->metaObject());
622 data->propertyCache->addref();
623 }
624
625 return true;
626}
627
628void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
629{
630 Q_ASSERT(propertyData);
631
632 QQmlData *data = QQmlData::get(object: *m_target, create: false);
633 Q_ASSERT(data);
634
635 if (Q_UNLIKELY(!data->propertyCache)) {
636 data->propertyCache = QQmlEnginePrivate::get(e: context()->engine)->cache(metaObject: m_target->metaObject());
637 data->propertyCache->addref();
638 }
639
640 *propertyData = data->propertyCache->property(index: m_targetIndex.coreIndex());
641 Q_ASSERT(*propertyData);
642
643 if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) {
644 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: (*propertyData)->propType());
645 Q_ASSERT(valueTypeMetaObject);
646 QMetaProperty vtProp = valueTypeMetaObject->property(index: m_targetIndex.valueTypeIndex());
647 valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
648
649 // valueTypeData is expected to be local here. It must not be shared with other threads.
650 valueTypeData->setPropType(vtProp.userType());
651
652 valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
653 }
654}
655
656QVector<QQmlProperty> QQmlBinding::dependencies() const
657{
658 QVector<QQmlProperty> dependencies;
659 if (!m_target.data())
660 return dependencies;
661
662 for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(v: guard)) {
663 if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
664 continue;
665
666 QObject *senderObject = guard->senderAsObject();
667 if (!senderObject)
668 continue;
669
670 const QMetaObject *senderMeta = senderObject->metaObject();
671 if (!senderMeta)
672 continue;
673
674 for (int i = 0; i < senderMeta->propertyCount(); i++) {
675 QMetaProperty property = senderMeta->property(index: i);
676 if (property.notifySignalIndex() == QMetaObjectPrivate::signal(m: senderMeta, signal_index: guard->signalIndex()).methodIndex()) {
677 dependencies.push_back(t: QQmlProperty(senderObject, QString::fromUtf8(str: senderObject->metaObject()->property(index: i).name())));
678 }
679 }
680 }
681
682 return dependencies;
683}
684
685bool QQmlBinding::hasDependencies() const
686{
687 return !activeGuards.isEmpty() || translationsCaptured();
688}
689
690class QObjectPointerBinding: public QQmlNonbindingBinding
691{
692 QQmlMetaObject targetMetaObject;
693
694public:
695 QObjectPointerBinding(QQmlEnginePrivate *engine, int propertyType)
696 : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(engine, propertyType))
697 {}
698
699protected:
700 Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
701 QQmlPropertyData::WriteFlags flags) override final
702 {
703 QQmlPropertyData *pd;
704 QQmlPropertyData vtpd;
705 getPropertyData(propertyData: &pd, valueTypeData: &vtpd);
706 if (Q_UNLIKELY(isUndefined || vtpd.isValid()))
707 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
708
709 // Check if the result is a QObject:
710 QObject *resultObject = nullptr;
711 QQmlMetaObject resultMo;
712 if (result.isNull()) {
713 // Special case: we can always write a nullptr. Don't bother checking anything else.
714 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
715 } else if (auto wrapper = result.as<QV4::QObjectWrapper>()) {
716 resultObject = wrapper->object();
717 if (!resultObject)
718 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
719 if (QQmlData *ddata = QQmlData::get(object: resultObject, create: false))
720 resultMo = ddata->propertyCache;
721 if (resultMo.isNull()) {
722 resultMo = resultObject->metaObject();
723 }
724 } else if (auto variant = result.as<QV4::VariantObject>()) {
725 QVariant value = variant->d()->data();
726 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(c: context());
727 resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType());
728 if (resultMo.isNull())
729 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
730 resultObject = *static_cast<QObject *const *>(value.constData());
731 } else {
732 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
733 }
734
735 // Compare & set:
736 if (QQmlMetaObject::canConvert(from: resultMo, to: targetMetaObject)) {
737 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
738 } else if (!resultObject && QQmlMetaObject::canConvert(from: targetMetaObject, to: resultMo)) {
739 // In the case of a null QObject, we assign the null if there is
740 // any change that the null variant type could be up or down cast to
741 // the property type.
742 return pd->writeProperty(target: targetObject(), value: &resultObject, flags);
743 } else {
744 return slowWrite(core: *pd, valueTypeData: vtpd, result, isUndefined, flags);
745 }
746 }
747};
748
749QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property)
750{
751 if (property && property->isQObject())
752 return new QObjectPointerBinding(engine, property->propType());
753
754 // If the property is not resolved at this point, you get a binding of unknown type.
755 // This has been the case for a long time and we keep it like this in Qt5 to be bug-compatible.
756 const int type = (property && property->isResolved())
757 ? property->propType()
758 : QMetaType::UnknownType;
759
760 if (type == qMetaTypeId<QQmlBinding *>()) {
761 return new QQmlBindingBinding;
762 }
763
764 switch (type) {
765 case QMetaType::Bool:
766 return new GenericBinding<QMetaType::Bool>;
767 case QMetaType::Int:
768 return new GenericBinding<QMetaType::Int>;
769 case QMetaType::Double:
770 return new GenericBinding<QMetaType::Double>;
771 case QMetaType::Float:
772 return new GenericBinding<QMetaType::Float>;
773 case QMetaType::QString:
774 return new GenericBinding<QMetaType::QString>;
775 default:
776 return new GenericBinding<QMetaType::UnknownType>;
777 }
778}
779
780QT_END_NAMESPACE
781

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