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 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | QQmlAbstractBinding::QQmlAbstractBinding() |
14 | : m_targetIndex(-1) |
15 | { |
16 | Q_ASSERT(!isAddedToObject()); |
17 | } |
18 | |
19 | QQmlAbstractBinding::~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 | /*! |
29 | Add this binding to \a object. |
30 | |
31 | This transfers ownership of the binding to the object, marks the object's property as |
32 | being bound. |
33 | |
34 | However, it does not enable the binding itself or call update() on it. |
35 | */ |
36 | void 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 | /*! |
90 | Remove the binding from the object. |
91 | */ |
92 | void 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 | |
158 | void 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 | |
163 | void 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 | |
188 | void 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 | |
197 | void QQmlAbstractBinding::setTarget(const QQmlProperty &prop) |
198 | { |
199 | auto pd = QQmlPropertyPrivate::get(p: prop); |
200 | setTarget(prop.object(), pd->core, valueType: &pd->valueTypeData); |
201 | } |
202 | |
203 | bool 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 | |
210 | static 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 | |
227 | bool 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 | |
282 | QString QQmlAbstractBinding::expression() const |
283 | { |
284 | return QLatin1String("<Unknown>" ); |
285 | } |
286 | |
287 | QT_END_NAMESPACE |
288 | |