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