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