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
qqmlpropertytopropertybinding.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
7
8#include <private/qqmlanybinding_p.h>
9#include <private/qqmlengine_p.h>
10#include <private/qqmlproperty_p.h>
11#include <private/qqmlvmemetaobject_p.h>
12#include <private/qv4alloca_p.h>
13
14#include <QtQml/qqmlinfo.h>
15
16#include <QtCore/qscopeguard.h>
17
19
20/*!
21 * \internal
22 * \class QQmlPropertyToPropertyBinding
23 *
24 * This class can be used to create a direct binding from a source property to
25 * a target property, without going through QQmlJavaScriptExpression and
26 * QV4::Function. In particular you don't need a compilation unit or byte code
27 * to set this up.
28 *
29 * \note The target cannot be a grouped property, but the source can.
30 */
31
32QQmlAnyBinding QQmlPropertyToPropertyBinding::create(
33 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
34{
35 QQmlAnyBinding result;
36 if (target.isBindable() && !QQmlPropertyPrivate::get(target)->isValueType()) {
37 QPropertyBindingPrivate *binding = nullptr;
38 if (source.isBindable())
39 binding = new QQmlBindableToBindablePropertyBinding(engine, source, target);
40 else
41 binding = new QQmlUnbindableToBindablePropertyBinding(engine, source, target);
42 result = QPropertyBindingPrivate::makeUntyped(binding);
43 return result;
44 }
45
46 if (source.isBindable()) {
47 result = new QQmlBindableToUnbindablePropertyBinding(engine, source, target);
48 return result;
49 }
50
51 result = new QQmlUnbindableToUnbindablePropertyBinding(engine, source, target);
52 return result;
53}
54
55QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding(
56 QQmlEngine *engine, const QQmlProperty &source)
57 : engine(engine)
58 , sourceObject(source.object())
59 , sourcePropertyIndex(QQmlPropertyPrivate::get(source)->encodedIndex())
60{
61}
62
64 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
66 , QQmlPropertyToUnbindablePropertyBinding(engine, source, target)
67{
68}
69
71{
72 return PropertyToPropertyBinding;
73}
74
76 bool e, QQmlPropertyData::WriteFlags flags)
77{
78 const bool wasEnabled = enabledFlag();
79 setEnabledFlag(e);
80 updateCanUseAccessor();
81 if (e && !wasEnabled)
82 update(flags);
83}
84
85void QQmlPropertyToUnbindablePropertyBinding::update(QQmlPropertyData::WriteFlags flags)
86{
87 if (!enabledFlag())
88 return;
89
90 // Check that the target has not been deleted
91 QObject *target = targetObject();
92 if (QQmlData::wasDeleted(target))
93 return;
94
95 const QQmlPropertyData *d = nullptr;
96 QQmlPropertyData vtd;
97 getPropertyData(&d, &vtd);
98 Q_ASSERT(d);
99
100 // Check for a binding update loop
101 if (Q_UNLIKELY(updatingFlag()))
102 return;
103
104 setUpdatingFlag(true);
105 const auto resetUpdating = qScopeGuard([this]() { setUpdatingFlag(false); });
106
107 if (canUseAccessor())
108 flags.setFlag(QQmlPropertyData::BypassInterceptor);
109
110 QVariant value = m_binding.readSourceValue(
111 [&](const QMetaObject *sourceMetaObject, const QMetaProperty &property) {
112 captureProperty(sourceMetaObject, property);
113 });
114
115 QV4::ExecutionEngine *v4 = m_binding.engine->handle();
116 Q_ASSERT(v4);
117
118 const QMetaType targetMetaType = vtd.isValid() ? vtd.propType() : d->propType();
119 const QMetaType valueMetaType = value.metaType();
120
121 if (QV4::ExecutionEngine::isUndefinedVariant(value)) {
122 /* we want to be consistent with the logic in QQmlBinding::slowWrite:
123 undefined should reset if possible, be stored as is if possible, and
124 create a warning if not; we especially don't want to coerce to string
125 even though that is possible
126 */
127 if (vtd.isValid() ? vtd.isResettable() : d->isResettable()) {
128 QQmlPropertyPrivate::resetValueProperty(target, *d, vtd, {}, flags);
129 return;
130 }
131
132 if (!QQmlMetaType::canLosslesslyHoldUndefined(targetMetaType)) {
133 const char *typeName = targetMetaType.name();
134 qmlWarning(target)
135 << QStringLiteral("Unable to assign [undefined] to \"%1\" of type %2")
136 .arg(QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr).name(),
137 typeName ? QString::fromUtf8(typeName)
138 : QStringLiteral("[unknown property type]"));
139 return;
140 }
141 // other cases are handled below
142 }
143
144 if (valueMetaType != targetMetaType && targetMetaType != QMetaType::fromType<QVariant>()) {
145 // Coerce the value to the target type using JavaScript semantics.
146 // Don't use QQmlProperty's internal coercion. It's not the same.
147 QVariant coerced(targetMetaType);
148 QV4::Scope scope(v4);
149 QV4::ScopedValue jsValue(
150 scope, value.isValid()
151 ? v4->metaTypeToJS(valueMetaType, value.constData())
152 : QV4::Encode::undefined());
153
154 // If the coercion fails, let writeValueProperty() do its thing after all.
155 if (QV4::ExecutionEngine::metaTypeFromJS(jsValue, targetMetaType, coerced.data()))
156 value = std::move(coerced);
157 }
158
159 QQmlPropertyPrivate::writeValueProperty(target, *d, vtd, value, {}, flags);
160}
161
163 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
165{
166 setTarget(target);
167}
168
170 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
172 , QQmlPropertyToUnbindablePropertyBinding(engine, source, target)
173{
174}
175
177 QPropertyObserver *observer, QUntypedPropertyData *)
178{
179 static_cast<QQmlBindableToUnbindablePropertyBinding *>(observer)
180 ->QQmlPropertyToUnbindablePropertyBinding::update();
181}
182
184 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
185{
186 Q_UNUSED(sourceMetaObject);
187 m_binding.doConnectNotify(this, sourceProperty);
188}
189
191 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
192{
193 Q_UNUSED(sourceProperty);
194
195 // We have already captured.
196 if (m_isObserving)
197 return;
198
199 QUntypedBindable bindable;
200 void *argv[] = { &bindable };
201 sourceMetaObject->metacall(
202 m_binding.sourceObject, QMetaObject::BindableProperty,
203 m_binding.sourcePropertyIndex.coreIndex(), argv);
204 bindable.observe(this);
205 m_isObserving = true;
206}
207
208namespace QtPrivate {
209template<typename Binding>
210inline constexpr BindingFunctionVTable
213 [](void *qpropertyBinding) { delete reinterpret_cast<Binding *>(qpropertyBinding); },
214 [](void *, void *){},
215 0
216 };
217}
218
220 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target,
221 const QtPrivate::BindingFunctionVTable *vtable)
225{
226 errorCallBack = [](QPropertyBindingPrivate *that) {
227 auto self = static_cast<QQmlPropertyToBindablePropertyBinding *>(that);
228 if (self->m_binding.engine) {
229 const auto error = self->bindingError();
230 if (error.type() == QPropertyBindingError::BindingLoop) {
231 qmlWarning(self->m_binding.sourceObject)
232 << QStringLiteral("Binding loop detected for property bound to ")
233 << self->propertyDataPtr;
234 }
235 }
236 };
237}
238
248
250{
251 PendingBindingObserverList bindingObservers;
252 evaluateRecursive(bindingObservers);
253
254 if (const QPropertyBindingError error = bindingError();
255 Q_UNLIKELY(error.type() == QPropertyBindingError::BindingLoop)) {
256 return;
257 }
258
259 notifyNonRecursive(bindingObservers);
260}
261
270
271void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *e, void **)
272{
273 static_cast<QQmlUnbindableToUnbindablePropertyBinding *>(e)->update();
274}
275
276void QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *e, void **)
277{
279}
280
281QT_END_NAMESPACE
QQmlBindableToBindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
void captureProperty(const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty) final
QQmlBindableToUnbindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
QQmlPropertyToBindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target, const QtPrivate::BindingFunctionVTable *vtable)
void setEnabled(bool e, QQmlPropertyData::WriteFlags flags) final
QQmlPropertyToUnbindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
void update(QQmlPropertyData::WriteFlags flags=QQmlPropertyData::DontRemoveBinding)
QQmlUnbindableToBindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
QQmlUnbindableToUnbindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
void captureProperty(const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty) final
Combined button and popup list for selecting options.
constexpr BindingFunctionVTable bindingFunctionVTableForQQmlPropertyToBindablePropertyBinding
void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *, void **)
void QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *, void **)