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