1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativebinding_p.h"
43 #include "private/qdeclarativebinding_p_p.h"
44
45 #include "qdeclarative.h"
46 #include "qdeclarativecontext.h"
47 #include "qdeclarativeinfo.h"
48 #include "private/qdeclarativecontext_p.h"
49 #include "private/qdeclarativecompiler_p.h"
50 #include "private/qdeclarativedata_p.h"
51 #include "private/qdeclarativestringconverters_p.h"
52 #include "private/qdeclarativestate_p_p.h"
53 #include "private/qdeclarativedebugtrace_p.h"
54
55 #include <QVariant>
56 #include <QtCore/qdebug.h>
57
58 QT_BEGIN_NAMESPACE
59
QDeclarativeAbstractBinding()60 QDeclarativeAbstractBinding::QDeclarativeAbstractBinding()
61 : m_object(0), m_propertyIndex(-1), m_mePtr(0), m_prevBinding(0), m_nextBinding(0)
62 {
63 }
64
~QDeclarativeAbstractBinding()65 QDeclarativeAbstractBinding::~QDeclarativeAbstractBinding()
66 {
67 Q_ASSERT(m_prevBinding == 0);
68 Q_ASSERT(m_mePtr == 0);
69 }
70
71 /*!
72 Destroy the binding. Use this instead of calling delete.
73
74 Bindings are free to implement their own memory management, so the delete operator is not
75 necessarily safe. The default implementation clears the binding, removes it from the object
76 and calls delete.
77 */
destroy(DestroyMode mode)78 void QDeclarativeAbstractBinding::destroy(DestroyMode mode)
79 {
80 if (mode == DisconnectBinding)
81 disconnect(QDeclarativeAbstractBinding::DisconnectOne);
82
83 removeFromObject();
84 clear();
85
86 delete this;
87 }
88
89 /*!
90 Add this binding to \a object.
91
92 This transfers ownership of the binding to the object, marks the object's property as
93 being bound.
94
95 However, it does not enable the binding itself or call update() on it.
96 */
addToObject(QObject * object,int index)97 void QDeclarativeAbstractBinding::addToObject(QObject *object, int index)
98 {
99 Q_ASSERT(object);
100
101 if (m_object == object && m_propertyIndex == index)
102 return;
103
104 removeFromObject();
105
106 Q_ASSERT(!m_prevBinding);
107
108 m_object = object;
109 m_propertyIndex = index;
110
111 QDeclarativeData *data = QDeclarativeData::get(object, true);
112
113 if (index & 0xFF000000) {
114 // Value type
115
116 int coreIndex = index & 0xFFFFFF;
117
118 // Find the value type proxy (if there is one)
119 QDeclarativeValueTypeProxyBinding *proxy = 0;
120 if (data->hasBindingBit(coreIndex)) {
121 QDeclarativeAbstractBinding *b = data->bindings;
122 while (b && b->propertyIndex() != coreIndex)
123 b = b->m_nextBinding;
124 Q_ASSERT(b && b->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy);
125 proxy = static_cast<QDeclarativeValueTypeProxyBinding *>(b);
126 }
127
128 if (!proxy) {
129 proxy = new QDeclarativeValueTypeProxyBinding(object, coreIndex);
130 proxy->addToObject(object, coreIndex);
131 }
132
133 m_nextBinding = proxy->m_bindings;
134 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
135 m_prevBinding = &proxy->m_bindings;
136 proxy->m_bindings = this;
137
138 } else {
139 m_nextBinding = data->bindings;
140 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
141 m_prevBinding = &data->bindings;
142 data->bindings = this;
143
144 data->setBindingBit(m_object, index);
145 }
146 }
147
148 /*!
149 Remove the binding from the object.
150 */
removeFromObject()151 void QDeclarativeAbstractBinding::removeFromObject()
152 {
153 if (m_prevBinding) {
154 int index = propertyIndex();
155
156 *m_prevBinding = m_nextBinding;
157 if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding;
158 m_prevBinding = 0;
159 m_nextBinding = 0;
160
161 if (index & 0xFF000000) {
162 // Value type - we don't remove the proxy from the object. It will sit their happily
163 // doing nothing until it is removed by a write, a binding change or it is reused
164 // to hold more sub-bindings.
165 } else if (m_object) {
166 QDeclarativeData *data = QDeclarativeData::get(m_object, false);
167 if (data) data->clearBindingBit(index);
168 }
169
170 m_object = 0;
171 m_propertyIndex = -1;
172 }
173 }
174
bindingDummyDeleter(QDeclarativeAbstractBinding *)175 static void bindingDummyDeleter(QDeclarativeAbstractBinding *)
176 {
177 }
178
weakPointer()179 QDeclarativeAbstractBinding::Pointer QDeclarativeAbstractBinding::weakPointer()
180 {
181 if (m_selfPointer.isNull())
182 m_selfPointer = QSharedPointer<QDeclarativeAbstractBinding>(this, bindingDummyDeleter);
183
184 return m_selfPointer.toWeakRef();
185 }
186
clear()187 void QDeclarativeAbstractBinding::clear()
188 {
189 if (m_mePtr) {
190 *m_mePtr = 0;
191 m_mePtr = 0;
192 }
193 }
194
expression() const195 QString QDeclarativeAbstractBinding::expression() const
196 {
197 return QLatin1String("<Unknown>");
198 }
199
object() const200 QObject *QDeclarativeAbstractBinding::object() const
201 {
202 return m_object;
203 }
204
propertyIndex() const205 int QDeclarativeAbstractBinding::propertyIndex() const
206 {
207 return m_propertyIndex;
208 }
209
setEnabled(bool enabled,QDeclarativePropertyPrivate::WriteFlags flags)210 void QDeclarativeAbstractBinding::setEnabled(bool enabled, QDeclarativePropertyPrivate::WriteFlags flags)
211 {
212 if (enabled) update(flags);
213 }
214
215 QDeclarativeBinding::Identifier QDeclarativeBinding::Invalid = -1;
216
refresh()217 void QDeclarativeBindingPrivate::refresh()
218 {
219 Q_Q(QDeclarativeBinding);
220 q->update();
221 }
222
QDeclarativeBindingPrivate()223 QDeclarativeBindingPrivate::QDeclarativeBindingPrivate()
224 : updating(false), enabled(false), deleted(0)
225 {
226 }
227
~QDeclarativeBindingPrivate()228 QDeclarativeBindingPrivate::~QDeclarativeBindingPrivate()
229 {
230 if (deleted) *deleted = true;
231 }
232
QDeclarativeBinding(void * data,QDeclarativeRefCount * rc,QObject * obj,QDeclarativeContextData * ctxt,const QString & url,int lineNumber,QObject * parent)233 QDeclarativeBinding::QDeclarativeBinding(void *data, QDeclarativeRefCount *rc, QObject *obj,
234 QDeclarativeContextData *ctxt, const QString &url, int lineNumber,
235 QObject *parent)
236 : QDeclarativeExpression(ctxt, data, rc, obj, url, lineNumber, *new QDeclarativeBindingPrivate)
237 {
238 setParent(parent);
239 setNotifyOnValueChanged(true);
240 }
241
242 QDeclarativeBinding *
createBinding(Identifier id,QObject * obj,QDeclarativeContext * ctxt,const QString & url,int lineNumber,QObject * parent)243 QDeclarativeBinding::createBinding(Identifier id, QObject *obj, QDeclarativeContext *ctxt,
244 const QString &url, int lineNumber, QObject *parent)
245 {
246 if (id < 0)
247 return 0;
248
249 Q_ASSERT(ctxt);
250 QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(ctxt);
251
252 QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(ctxtdata->engine);
253 QDeclarativeCompiledData *cdata = 0;
254 QDeclarativeTypeData *typeData = 0;
255 if (!ctxtdata->url.isEmpty()) {
256 typeData = engine->typeLoader.get(ctxtdata->url);
257 cdata = typeData->compiledData();
258 }
259 QDeclarativeBinding *rv = cdata ? new QDeclarativeBinding((void*)cdata->datas.at(id).constData(), cdata, obj, ctxtdata, url, lineNumber, parent) : 0;
260 if (cdata)
261 cdata->release();
262 if (typeData)
263 typeData->release();
264 return rv;
265 }
266
QDeclarativeBinding(const QString & str,QObject * obj,QDeclarativeContext * ctxt,QObject * parent)267 QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContext *ctxt,
268 QObject *parent)
269 : QDeclarativeExpression(QDeclarativeContextData::get(ctxt), obj, str, *new QDeclarativeBindingPrivate)
270 {
271 setParent(parent);
272 setNotifyOnValueChanged(true);
273 }
274
QDeclarativeBinding(const QString & str,QObject * obj,QDeclarativeContextData * ctxt,QObject * parent)275 QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContextData *ctxt,
276 QObject *parent)
277 : QDeclarativeExpression(ctxt, obj, str, *new QDeclarativeBindingPrivate)
278 {
279 setParent(parent);
280 setNotifyOnValueChanged(true);
281 }
282
QDeclarativeBinding(const QScriptValue & func,QObject * obj,QDeclarativeContextData * ctxt,QObject * parent)283 QDeclarativeBinding::QDeclarativeBinding(const QScriptValue &func, QObject *obj, QDeclarativeContextData *ctxt, QObject *parent)
284 : QDeclarativeExpression(ctxt, obj, func, *new QDeclarativeBindingPrivate)
285 {
286 setParent(parent);
287 setNotifyOnValueChanged(true);
288 }
289
~QDeclarativeBinding()290 QDeclarativeBinding::~QDeclarativeBinding()
291 {
292 }
293
setTarget(const QDeclarativeProperty & prop)294 void QDeclarativeBinding::setTarget(const QDeclarativeProperty &prop)
295 {
296 Q_D(QDeclarativeBinding);
297 d->property = prop;
298
299 update();
300 }
301
property() const302 QDeclarativeProperty QDeclarativeBinding::property() const
303 {
304 Q_D(const QDeclarativeBinding);
305 return d->property;
306 }
307
setEvaluateFlags(EvaluateFlags flags)308 void QDeclarativeBinding::setEvaluateFlags(EvaluateFlags flags)
309 {
310 Q_D(QDeclarativeBinding);
311 d->setEvaluateFlags(QDeclarativeQtScriptExpression::EvaluateFlags(static_cast<int>(flags)));
312 }
313
evaluateFlags() const314 QDeclarativeBinding::EvaluateFlags QDeclarativeBinding::evaluateFlags() const
315 {
316 Q_D(const QDeclarativeBinding);
317 return QDeclarativeBinding::EvaluateFlags(static_cast<int>(d->evaluateFlags()));
318 }
319
320
321 class QDeclarativeBindingProfiler {
322 public:
QDeclarativeBindingProfiler(QDeclarativeBinding * bind)323 QDeclarativeBindingProfiler(QDeclarativeBinding *bind)
324 {
325 QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding);
326 QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Binding, bind->expression());
327 QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Binding, bind->sourceFile(), bind->lineNumber());
328 }
329
~QDeclarativeBindingProfiler()330 ~QDeclarativeBindingProfiler()
331 {
332 QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding);
333 }
334 };
335
update(QDeclarativePropertyPrivate::WriteFlags flags)336 void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags)
337 {
338 Q_D(QDeclarativeBinding);
339
340 if (!d->enabled || !d->context() || !d->context()->isValid())
341 return;
342
343 if (!d->updating) {
344 QDeclarativeBindingProfiler prof(this);
345 d->updating = true;
346 bool wasDeleted = false;
347 d->deleted = &wasDeleted;
348
349 if (d->property.propertyType() == qMetaTypeId<QDeclarativeBinding *>()) {
350
351 int idx = d->property.index();
352 Q_ASSERT(idx != -1);
353
354 QDeclarativeBinding *t = this;
355 int status = -1;
356 void *a[] = { &t, 0, &status, &flags };
357 QMetaObject::metacall(d->property.object(),
358 QMetaObject::WriteProperty,
359 idx, a);
360
361 if (wasDeleted)
362 return;
363
364 } else {
365 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context()->engine);
366
367 bool isUndefined = false;
368 QVariant value;
369
370 QScriptValue scriptValue = d->scriptValue(0, &isUndefined);
371 if (wasDeleted)
372 return;
373
374 if (d->property.propertyTypeCategory() == QDeclarativeProperty::List) {
375 value = ep->scriptValueToVariant(scriptValue, qMetaTypeId<QList<QObject *> >());
376 } else if (scriptValue.isNull() &&
377 d->property.propertyTypeCategory() == QDeclarativeProperty::Object) {
378 value = QVariant::fromValue((QObject *)0);
379 } else {
380 value = ep->scriptValueToVariant(scriptValue, d->property.propertyType());
381 if (value.userType() == QMetaType::QObjectStar && !qvariant_cast<QObject*>(value)) {
382 // If the object is null, we extract the predicted type. While this isn't
383 // 100% reliable, in many cases it gives us better error messages if we
384 // assign this null-object to an incompatible property
385 int type = ep->objectClass->objectType(scriptValue);
386 QObject *o = 0;
387 value = QVariant(type, (void *)&o);
388 }
389 }
390
391
392 if (d->error.isValid()) {
393
394 } else if (isUndefined && d->property.isResettable()) {
395
396 d->property.reset();
397
398 } else if (isUndefined && d->property.propertyType() == qMetaTypeId<QVariant>()) {
399
400 QDeclarativePropertyPrivate::write(d->property, QVariant(), flags);
401
402 } else if (isUndefined) {
403
404 QUrl url = QUrl(d->url);
405 int line = d->line;
406 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
407
408 d->error.setUrl(url);
409 d->error.setLine(line);
410 d->error.setColumn(-1);
411 d->error.setDescription(QLatin1String("Unable to assign [undefined] to ") +
412 QLatin1String(QMetaType::typeName(d->property.propertyType())) +
413 QLatin1String(" ") + d->property.name());
414
415 } else if (!scriptValue.isRegExp() && scriptValue.isFunction()) {
416
417 QUrl url = QUrl(d->url);
418 int line = d->line;
419 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
420
421 d->error.setUrl(url);
422 d->error.setLine(line);
423 d->error.setColumn(-1);
424 d->error.setDescription(QLatin1String("Unable to assign a function to a property."));
425
426 } else if (d->property.object() &&
427 !QDeclarativePropertyPrivate::write(d->property, value, flags)) {
428
429 if (wasDeleted)
430 return;
431
432 QUrl url = QUrl(d->url);
433 int line = d->line;
434 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
435
436 const char *valueType = 0;
437 if (value.userType() == QVariant::Invalid) valueType = "null";
438 else valueType = QMetaType::typeName(value.userType());
439
440 d->error.setUrl(url);
441 d->error.setLine(line);
442 d->error.setColumn(-1);
443 d->error.setDescription(QLatin1String("Unable to assign ") +
444 QLatin1String(valueType) +
445 QLatin1String(" to ") +
446 QLatin1String(QMetaType::typeName(d->property.propertyType())));
447 }
448
449 if (wasDeleted)
450 return;
451
452 if (d->error.isValid()) {
453 if (!d->addError(ep)) ep->warning(this->error());
454 } else {
455 d->removeError();
456 }
457 }
458
459 d->updating = false;
460 d->deleted = 0;
461 } else {
462 qmlInfo(d->property.object()) << tr("Binding loop detected for property \"%1\"").arg(d->property.name());
463 }
464 }
465
emitValueChanged()466 void QDeclarativeBindingPrivate::emitValueChanged()
467 {
468 Q_Q(QDeclarativeBinding);
469 q->update();
470 }
471
setEnabled(bool e,QDeclarativePropertyPrivate::WriteFlags flags)472 void QDeclarativeBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
473 {
474 Q_D(QDeclarativeBinding);
475 d->enabled = e;
476 setNotifyOnValueChanged(e);
477
478 if (e)
479 update(flags);
480 }
481
enabled() const482 bool QDeclarativeBinding::enabled() const
483 {
484 Q_D(const QDeclarativeBinding);
485
486 return d->enabled;
487 }
488
expression() const489 QString QDeclarativeBinding::expression() const
490 {
491 return QDeclarativeExpression::expression();
492 }
493
disconnect(DisconnectMode disconnectMode)494 void QDeclarativeBinding::disconnect(DisconnectMode disconnectMode)
495 {
496 Q_UNUSED(disconnectMode);
497 setNotifyOnValueChanged(false);
498 }
499
QDeclarativeValueTypeProxyBinding(QObject * o,int index)500 QDeclarativeValueTypeProxyBinding::QDeclarativeValueTypeProxyBinding(QObject *o, int index)
501 : m_object(o), m_index(index), m_bindings(0)
502 {
503 }
504
~QDeclarativeValueTypeProxyBinding()505 QDeclarativeValueTypeProxyBinding::~QDeclarativeValueTypeProxyBinding()
506 {
507 while (m_bindings) {
508 QDeclarativeAbstractBinding *binding = m_bindings;
509 binding->setEnabled(false, 0);
510 binding->destroy();
511 }
512 }
513
setEnabled(bool e,QDeclarativePropertyPrivate::WriteFlags flags)514 void QDeclarativeValueTypeProxyBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
515 {
516 if (e) {
517 QDeclarativeAbstractBinding *bindings = m_bindings;
518 recursiveEnable(bindings, flags);
519 } else {
520 QDeclarativeAbstractBinding *bindings = m_bindings;
521 recursiveDisable(bindings);
522 }
523 }
524
recursiveEnable(QDeclarativeAbstractBinding * b,QDeclarativePropertyPrivate::WriteFlags flags)525 void QDeclarativeValueTypeProxyBinding::recursiveEnable(QDeclarativeAbstractBinding *b, QDeclarativePropertyPrivate::WriteFlags flags)
526 {
527 if (!b)
528 return;
529
530 recursiveEnable(b->m_nextBinding, flags);
531
532 if (b)
533 b->setEnabled(true, flags);
534 }
535
recursiveDisable(QDeclarativeAbstractBinding * b)536 void QDeclarativeValueTypeProxyBinding::recursiveDisable(QDeclarativeAbstractBinding *b)
537 {
538 if (!b)
539 return;
540
541 recursiveDisable(b->m_nextBinding);
542
543 if (b)
544 b->setEnabled(false, 0);
545 }
546
update(QDeclarativePropertyPrivate::WriteFlags)547 void QDeclarativeValueTypeProxyBinding::update(QDeclarativePropertyPrivate::WriteFlags)
548 {
549 }
550
disconnect(DisconnectMode disconnectMode)551 void QDeclarativeValueTypeProxyBinding::disconnect(DisconnectMode disconnectMode)
552 {
553 Q_UNUSED(disconnectMode);
554 // Nothing to do
555 }
556
binding(int propertyIndex)557 QDeclarativeAbstractBinding *QDeclarativeValueTypeProxyBinding::binding(int propertyIndex)
558 {
559 QDeclarativeAbstractBinding *binding = m_bindings;
560
561 while (binding && binding->propertyIndex() != propertyIndex)
562 binding = binding->m_nextBinding;
563
564 return binding;
565 }
566
567 /*!
568 Removes a collection of bindings, corresponding to the set bits in \a mask.
569 */
removeBindings(quint32 mask)570 void QDeclarativeValueTypeProxyBinding::removeBindings(quint32 mask)
571 {
572 QDeclarativeAbstractBinding *binding = m_bindings;
573 while (binding) {
574 if (mask & (1 << (binding->propertyIndex() >> 24))) {
575 QDeclarativeAbstractBinding *remove = binding;
576 binding = remove->m_nextBinding;
577 *remove->m_prevBinding = remove->m_nextBinding;
578 if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
579 remove->m_prevBinding = 0;
580 remove->m_nextBinding = 0;
581 remove->destroy();
582 } else {
583 binding = binding->m_nextBinding;
584 }
585 }
586 }
587
588 QT_END_NAMESPACE
589