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