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