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
4#include "qqmlabstractbinding_p.h"
5
6#include <QtQml/qqmlinfo.h>
7#include <private/qqmlbinding_p.h>
8#include <private/qqmlvaluetypeproxybinding_p.h>
9#include <private/qqmlvmemetaobject_p.h>
10
11QT_BEGIN_NAMESPACE
12
13QQmlAbstractBinding::QQmlAbstractBinding()
14 : m_targetIndex(-1)
15{
16 Q_ASSERT(!isAddedToObject());
17}
18
19QQmlAbstractBinding::~QQmlAbstractBinding()
20{
21 Q_ASSERT(!ref);
22 Q_ASSERT(!isAddedToObject());
23
24 if (m_nextBinding.data() && !m_nextBinding->ref.deref())
25 delete m_nextBinding.data();
26}
27
28/*!
29Add this binding to \a object.
30
31This transfers ownership of the binding to the object, marks the object's property as
32being bound.
33
34However, it does not enable the binding itself or call update() on it.
35*/
36void QQmlAbstractBinding::addToObject()
37{
38 Q_ASSERT(!nextBinding());
39 Q_ASSERT(isAddedToObject() == false);
40
41 QObject *obj = targetObject();
42 Q_ASSERT(obj);
43
44 QQmlData *data = QQmlData::get(object: obj, create: true);
45
46 int coreIndex = targetPropertyIndex().coreIndex();
47 if (targetPropertyIndex().hasValueTypeIndex()) {
48 // Value type
49
50 // Find the value type proxy (if there is one)
51 QQmlValueTypeProxyBinding *proxy = nullptr;
52 if (data->hasBindingBit(coreIndex)) {
53 QQmlAbstractBinding *b = data->bindings;
54 while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
55 b->targetPropertyIndex().hasValueTypeIndex()))
56 b = b->nextBinding();
57 Q_ASSERT(b && b->kind() == QQmlAbstractBinding::ValueTypeProxy);
58 proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
59 }
60
61 if (!proxy) {
62 proxy = new QQmlValueTypeProxyBinding(obj, QQmlPropertyIndex(coreIndex));
63
64 Q_ASSERT(proxy->targetPropertyIndex().coreIndex() == coreIndex);
65 Q_ASSERT(!proxy->targetPropertyIndex().hasValueTypeIndex());
66 Q_ASSERT(proxy->targetObject() == obj);
67
68 proxy->addToObject();
69 }
70
71 setNextBinding(proxy->m_bindings.data());
72 proxy->m_bindings = this;
73
74 } else {
75 setNextBinding(data->bindings);
76 if (data->bindings) {
77 data->bindings->ref.deref();
78 Q_ASSERT(data->bindings->ref.refCount > 0);
79 }
80 data->bindings = this;
81 ref.ref();
82
83 data->setBindingBit(obj, coreIndex);
84 }
85
86 setAddedToObject(true);
87}
88
89/*!
90Remove the binding from the object.
91*/
92void QQmlAbstractBinding::removeFromObject()
93{
94 if (!isAddedToObject())
95 return;
96
97 setAddedToObject(false);
98
99 QObject *obj = targetObject();
100 QQmlData *data = QQmlData::get(object: obj, create: false);
101 Q_ASSERT(data);
102
103 QQmlAbstractBinding::Ptr next;
104 next = nextBinding();
105 setNextBinding(nullptr);
106
107 int coreIndex = targetPropertyIndex().coreIndex();
108 if (targetPropertyIndex().hasValueTypeIndex()) {
109
110 // Find the value type binding
111 QQmlAbstractBinding *vtbinding = data->bindings;
112 Q_ASSERT(vtbinding);
113 while (vtbinding->targetPropertyIndex().coreIndex() != coreIndex
114 || vtbinding->targetPropertyIndex().hasValueTypeIndex()) {
115 vtbinding = vtbinding->nextBinding();
116 Q_ASSERT(vtbinding);
117 }
118 Q_ASSERT(vtbinding->kind() == QQmlAbstractBinding::ValueTypeProxy);
119
120 QQmlValueTypeProxyBinding *vtproxybinding =
121 static_cast<QQmlValueTypeProxyBinding *>(vtbinding);
122
123 QQmlAbstractBinding *binding = vtproxybinding->m_bindings.data();
124 if (binding == this) {
125 vtproxybinding->m_bindings = next;
126 } else {
127 while (binding->nextBinding() != this) {
128 binding = binding->nextBinding();
129 Q_ASSERT(binding);
130 }
131 binding->setNextBinding(next.data());
132 }
133
134 // Value type - we don't remove the proxy from the object. It will sit their happily
135 // doing nothing until it is removed by a write, a binding change or it is reused
136 // to hold more sub-bindings.
137 return;
138 }
139
140 if (data->bindings == this) {
141 if (next.data())
142 next->ref.ref();
143 data->bindings = next.data();
144 if (!ref.deref())
145 delete this;
146 } else {
147 QQmlAbstractBinding *binding = data->bindings;
148 while (binding->nextBinding() != this) {
149 binding = binding->nextBinding();
150 Q_ASSERT(binding);
151 }
152 binding->setNextBinding(next.data());
153 }
154
155 data->clearBindingBit(coreIndex);
156}
157
158void QQmlAbstractBinding::printBindingLoopError(const QQmlProperty &prop)
159{
160 qmlWarning(me: prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(a: prop.name());
161}
162
163void QQmlAbstractBinding::getPropertyData(
164 const QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
165{
166 Q_ASSERT(propertyData);
167
168 QQmlData *data = QQmlData::get(object: m_target.data(), create: false);
169 Q_ASSERT(data);
170
171 if (Q_UNLIKELY(!data->propertyCache))
172 data->propertyCache = QQmlMetaType::propertyCache(metaObject: m_target->metaObject());
173
174 *propertyData = data->propertyCache->property(index: m_targetIndex.coreIndex());
175 Q_ASSERT(*propertyData);
176
177 if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) {
178 const QMetaObject *valueTypeMetaObject
179 = QQmlMetaType::metaObjectForValueType(type: (*propertyData)->propType());
180 Q_ASSERT(valueTypeMetaObject);
181 QMetaProperty vtProp = valueTypeMetaObject->property(index: m_targetIndex.valueTypeIndex());
182 valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
183 valueTypeData->setPropType(vtProp.metaType());
184 valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
185 }
186}
187
188void QQmlAbstractBinding::updateCanUseAccessor()
189{
190 setCanUseAccessor(true); // Always use accessors, except when:
191 if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(obj: targetObject())) {
192 if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(propertyIndex: m_targetIndex))
193 setCanUseAccessor(false);
194 }
195}
196
197void QQmlAbstractBinding::setTarget(const QQmlProperty &prop)
198{
199 auto pd = QQmlPropertyPrivate::get(p: prop);
200 setTarget(prop.object(), pd->core, valueType: &pd->valueTypeData);
201}
202
203bool QQmlAbstractBinding::setTarget(
204 QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
205{
206 return setTarget(object, coreIndex: core.coreIndex(), coreIsAlias: core.isAlias(),
207 valueTypeIndex: valueType ? valueType->coreIndex() : -1);
208}
209
210static const QQmlPropertyData *getObjectPropertyData(QObject *object, int coreIndex)
211{
212 QQmlData *data = QQmlData::get(object, create: true);
213 if (!data)
214 return nullptr;
215
216 if (!data->propertyCache) {
217 data->propertyCache = QQmlMetaType::propertyCache(object);
218 if (!data->propertyCache)
219 return nullptr;
220 }
221
222 const QQmlPropertyData *propertyData = data->propertyCache->property(index: coreIndex);
223 Q_ASSERT(propertyData);
224 return propertyData;
225}
226
227bool QQmlAbstractBinding::setTarget(
228 QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex)
229{
230 auto invalidate = [this]() {
231 m_target = nullptr;
232 m_targetIndex = QQmlPropertyIndex();
233 return false;
234 };
235
236 if (!object)
237 return invalidate();
238
239 m_target = object;
240
241 for (bool isAlias = coreIsAlias; isAlias;) {
242 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(o: object, coreIndex);
243
244 int aValueTypeIndex;
245 if (!vme->aliasTarget(index: coreIndex, target: &object, coreIndex: &coreIndex, valueTypeIndex: &aValueTypeIndex)) {
246 // can't resolve id (yet)
247 return invalidate();
248 }
249
250 const QQmlPropertyData *propertyData = getObjectPropertyData(object, coreIndex);
251 if (!propertyData)
252 return invalidate();
253
254 if (aValueTypeIndex != -1) {
255 if (propertyData->propType().flags().testFlag(flag: QMetaType::PointerToQObject)) {
256 // deep alias
257 propertyData->readProperty(target: object, property: &object);
258 coreIndex = aValueTypeIndex;
259 valueTypeIndex = -1;
260 propertyData = getObjectPropertyData(object, coreIndex);
261 if (!propertyData)
262 return invalidate();
263 } else {
264 valueTypeIndex = aValueTypeIndex;
265 }
266 }
267
268 m_target = object;
269 isAlias = propertyData->isAlias();
270 coreIndex = propertyData->coreIndex();
271 }
272 m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex);
273
274 QQmlData *data = QQmlData::get(object: m_target.data(), create: true);
275 if (!data->propertyCache)
276 data->propertyCache = QQmlMetaType::propertyCache(metaObject: m_target->metaObject());
277
278 return true;
279}
280
281
282QString QQmlAbstractBinding::expression() const
283{
284 return QLatin1String("<Unknown>");
285}
286
287QT_END_NAMESPACE
288

source code of qtdeclarative/src/qml/qml/qqmlabstractbinding.cpp