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