QmlProfiler: Avoid overhead if profiling is not enabled
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlbinding.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlbinding_p.h"
43 #include "qqmlbinding_p_p.h"
44
45 #include "qqml.h"
46 #include "qqmlcontext.h"
47 #include "qqmlinfo.h"
48 #include "qqmlcompiler_p.h"
49 #include "qqmldata_p.h"
50 #include <private/qqmlprofilerservice_p.h>
51 #include <private/qqmltrace_p.h>
52
53 #include <QVariant>
54 #include <QtCore/qdebug.h>
55
56 QT_BEGIN_NAMESPACE
57
58 QQmlAbstractBinding::QQmlAbstractBinding()
59 : m_prevBinding(0), m_nextBinding(0)
60 {
61 }
62
63 QQmlAbstractBinding::~QQmlAbstractBinding()
64 {
65     Q_ASSERT(m_prevBinding == 0);
66     Q_ASSERT(*m_mePtr == 0);
67 }
68
69 /*!
70 Destroy the binding.  Use this instead of calling delete.
71
72 Bindings are free to implement their own memory management, so the delete operator is not 
73 necessarily safe.  The default implementation clears the binding, removes it from the object
74 and calls delete.
75 */
76 void QQmlAbstractBinding::destroy()
77 {
78     removeFromObject();
79     clear();
80
81     delete this;
82 }
83
84 /*!
85 Add this binding to \a object.
86
87 This transfers ownership of the binding to the object, marks the object's property as
88 being bound.  
89
90 However, it does not enable the binding itself or call update() on it.
91 */
92 void QQmlAbstractBinding::addToObject()
93 {
94     Q_ASSERT(!m_prevBinding);
95
96     QObject *obj = object();
97     Q_ASSERT(obj);
98
99     int index = propertyIndex();
100
101     QQmlData *data = QQmlData::get(obj, true);
102
103     if (index & 0xFF000000) {
104         // Value type
105
106         int coreIndex = index & 0xFFFFFF;
107
108         // Find the value type proxy (if there is one)
109         QQmlValueTypeProxyBinding *proxy = 0;
110         if (data->hasBindingBit(coreIndex)) {
111             QQmlAbstractBinding *b = data->bindings;
112             while (b && b->propertyIndex() != coreIndex)
113                 b = b->m_nextBinding;
114             Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy);
115             proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
116         }
117
118         if (!proxy) {
119             proxy = new QQmlValueTypeProxyBinding(obj, coreIndex);
120
121             Q_ASSERT(proxy->propertyIndex() == coreIndex);
122             Q_ASSERT(proxy->object() == obj);
123
124             proxy->addToObject();
125         }
126
127         m_nextBinding = proxy->m_bindings;
128         if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
129         m_prevBinding = &proxy->m_bindings;
130         proxy->m_bindings = this;
131
132     } else {
133         m_nextBinding = data->bindings;
134         if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
135         m_prevBinding = &data->bindings;
136         data->bindings = this;
137
138         data->setBindingBit(obj, index);
139     }
140 }
141
142 /*!
143 Remove the binding from the object.
144 */
145 void QQmlAbstractBinding::removeFromObject()
146 {
147     if (m_prevBinding) {
148         int index = propertyIndex();
149
150         *m_prevBinding = m_nextBinding;
151         if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding;
152         m_prevBinding = 0;
153         m_nextBinding = 0;
154
155         if (index & 0xFF000000) {
156             // Value type - we don't remove the proxy from the object.  It will sit their happily
157             // doing nothing until it is removed by a write, a binding change or it is reused
158             // to hold more sub-bindings.
159         } else if (QObject *obj = object()) {
160             QQmlData *data = QQmlData::get(obj, false);
161             if (data) data->clearBindingBit(index);
162         }
163     }
164 }
165
166 static void bindingDummyDeleter(QQmlAbstractBinding *)
167 {
168 }
169
170 QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer()
171 {
172     if (m_mePtr.value().isNull())
173         m_mePtr.value() = QSharedPointer<QQmlAbstractBinding>(this, bindingDummyDeleter);
174
175     return m_mePtr.value().toWeakRef();
176 }
177
178 void QQmlAbstractBinding::clear()
179 {
180     if (!m_mePtr.isNull()) {
181         **m_mePtr = 0;
182         m_mePtr = 0;
183     }
184 }
185
186 void QQmlAbstractBinding::retargetBinding(QObject *, int)
187 {
188     qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding.");
189 }
190
191 QString QQmlAbstractBinding::expression() const
192 {
193     return QLatin1String("<Unknown>");
194 }
195
196 void QQmlAbstractBinding::setEnabled(bool enabled, QQmlPropertyPrivate::WriteFlags flags)
197 {
198     if (enabled) update(flags);
199 }
200
201 QQmlBinding::Identifier QQmlBinding::Invalid = -1;
202
203 void QQmlBindingPrivate::refresh()
204 {
205     Q_Q(QQmlBinding);
206     q->update();
207 }
208
209 QQmlBindingPrivate::QQmlBindingPrivate()
210 : updating(false), enabled(false), target(), targetProperty(0)
211 {
212 }
213
214 QQmlBindingPrivate::~QQmlBindingPrivate()
215 {
216 }
217
218 QQmlBinding *
219 QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt,
220                                    const QString &url, int lineNumber, QObject *parent)
221 {
222     if (id < 0)
223         return 0;
224
225     QQmlContextData *ctxtdata = QQmlContextData::get(ctxt);
226
227     QQmlEnginePrivate *engine = QQmlEnginePrivate::get(ctxt->engine());
228     QQmlCompiledData *cdata = 0;
229     QQmlTypeData *typeData = 0;
230     if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
231         typeData = engine->typeLoader.get(ctxtdata->url);
232         cdata = typeData->compiledData();
233     }
234     QQmlBinding *rv = cdata ? new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, url, lineNumber, 0, parent) : 0;
235     if (cdata)
236         cdata->release();
237     if (typeData)
238         typeData->release();
239     return rv;
240 }
241
242 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt, 
243                                          QObject *parent)
244 : QQmlExpression(QQmlContextData::get(ctxt), obj, str, *new QQmlBindingPrivate)
245 {
246     setParent(parent);
247     setNotifyOnValueChanged(true);
248 }
249
250 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt, 
251                                          QObject *parent)
252 : QQmlExpression(ctxt, obj, str, *new QQmlBindingPrivate)
253 {
254     setParent(parent);
255     setNotifyOnValueChanged(true);
256 }
257
258 QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, 
259                                          QQmlContextData *ctxt, 
260                                          const QString &url, int lineNumber, int columnNumber,
261                                          QObject *parent)
262 : QQmlExpression(ctxt, obj, str, isRewritten, url, lineNumber, columnNumber, *new QQmlBindingPrivate)
263 {
264     setParent(parent);
265     setNotifyOnValueChanged(true);
266 }
267
268 /*!  
269     \internal 
270
271     To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>.  
272     For example:
273         v8::Handle<v8::Function> function;
274         new QQmlBinding(&function, scope, ctxt);
275  */
276 QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt, 
277                                          QObject *parent)
278 : QQmlExpression(ctxt, obj, functionPtr, *new QQmlBindingPrivate)
279 {
280     setParent(parent);
281     setNotifyOnValueChanged(true);
282 }
283
284 QQmlBinding::~QQmlBinding()
285 {
286 }
287
288 void QQmlBinding::setTarget(const QQmlProperty &prop)
289 {
290     Q_D(QQmlBinding);
291     d->property = prop;
292     d->target = d->property.object();
293     d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex();
294
295     update();
296 }
297
298 void QQmlBinding::setTarget(QObject *object,
299                                     const QQmlPropertyData &core,
300                                     QQmlContextData *ctxt)
301 {
302     Q_D(QQmlBinding);
303     d->property = QQmlPropertyPrivate::restore(object, core, ctxt);
304     d->target = d->property.object();
305     d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex();
306
307     update();
308 }
309
310 QQmlProperty QQmlBinding::property() const 
311 {
312    Q_D(const QQmlBinding);
313    return d->property; 
314 }
315
316 void QQmlBinding::setEvaluateFlags(EvaluateFlags flags)
317 {
318     Q_D(QQmlBinding);
319     d->setRequiresThisObject(flags & RequiresThisObject);
320 }
321
322 QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const
323 {
324     Q_D(const QQmlBinding);
325     return d->requiresThisObject()?RequiresThisObject:None;
326 }
327
328 void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
329 {
330     Q_D(QQmlBinding);
331
332     if (!d->enabled || !d->context() || !d->context()->isValid()) 
333         return;
334
335     QQmlTrace trace("General Binding Update");
336     trace.addDetail("URL", d->url);
337     trace.addDetail("Line", d->line);
338     trace.addDetail("Column", d->columnNumber);
339
340     if (!d->updating) {
341         QQmlBindingProfiler prof(d->url, d->line, d->column);
342         if (prof.enabled)
343             prof.addDetail(expression());
344         d->updating = true;
345
346         QQmlAbstractExpression::DeleteWatcher watcher(d);
347
348         if (d->property.propertyType() == qMetaTypeId<QQmlBinding *>()) {
349
350             int idx = d->property.index();
351             Q_ASSERT(idx != -1);
352
353             QQmlBinding *t = this;
354             int status = -1;
355             void *a[] = { &t, 0, &status, &flags };
356             QMetaObject::metacall(d->property.object(),
357                                   QMetaObject::WriteProperty,
358                                   idx, a);
359
360         } else {
361             QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->context()->engine);
362             ep->referenceScarceResources(); 
363
364             bool isUndefined = false;
365
366             v8::HandleScope handle_scope;
367             v8::Context::Scope scope(ep->v8engine()->context());
368             v8::Local<v8::Value> result = d->v8value(0, &isUndefined);
369
370             trace.event("writing binding result");
371
372             bool needsErrorData = false;
373             if (!watcher.wasDeleted() && !d->hasError())
374                 needsErrorData = !QQmlPropertyPrivate::writeBinding(d->property, d->context(),
375                                                                             d, result,
376                                                                             isUndefined, flags);
377
378             if (!watcher.wasDeleted()) {
379                
380                 if (needsErrorData) {
381                     QUrl url = QUrl(d->url);
382                     int line = d->line;
383                     if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
384
385                     d->delayedError()->error.setUrl(url);
386                     d->delayedError()->error.setLine(line);
387                     d->delayedError()->error.setColumn(-1);
388                 }
389
390                 if (d->hasError()) {
391                     if (!d->delayedError()->addError(ep)) ep->warning(this->error());
392                 } else {
393                     d->clearError();
394                 }
395
396             }
397
398             ep->dereferenceScarceResources(); 
399         }
400
401         if (!watcher.wasDeleted())
402             d->updating = false;
403     } else {
404         QQmlBindingPrivate::printBindingLoopError(d->property);
405     }
406 }
407
408 void QQmlBindingPrivate::printBindingLoopError(QQmlProperty &prop)
409 {
410     qmlInfo(prop.object()) << QQmlBinding::tr("Binding loop detected for property \"%1\"").arg(prop.name());
411 }
412
413 void QQmlBindingPrivate::expressionChanged()
414 {
415     Q_Q(QQmlBinding);
416     q->update();
417 }
418
419 void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
420 {
421     Q_D(QQmlBinding);
422     d->enabled = e;
423     setNotifyOnValueChanged(e);
424
425     if (e) 
426         update(flags);
427 }
428
429 bool QQmlBinding::enabled() const
430 {
431     Q_D(const QQmlBinding);
432
433     return d->enabled;
434 }
435
436 QString QQmlBinding::expression() const
437 {
438     return QQmlExpression::expression();
439 }
440
441 int QQmlBinding::propertyIndex() const
442 {
443     Q_D(const QQmlBinding);
444     return d->targetProperty;
445 }
446
447 QObject *QQmlBinding::object() const
448 {
449     Q_D(const QQmlBinding);
450     return d->target;
451 }
452
453 void QQmlBinding::retargetBinding(QObject *t, int i)
454 {
455     Q_D(QQmlBinding);
456     d->target = t;
457     d->targetProperty = i;
458 }
459
460 QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index)
461 : m_object(o), m_index(index), m_bindings(0)
462 {
463 }
464
465 QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding()
466 {
467     while (m_bindings) {
468         QQmlAbstractBinding *binding = m_bindings;
469         binding->setEnabled(false, 0);
470         binding->destroy();
471     }
472 }
473
474 void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
475 {
476     if (e) {
477         QQmlAbstractBinding *bindings = m_bindings;
478         recursiveEnable(bindings, flags);
479     } else {
480         QQmlAbstractBinding *bindings = m_bindings;
481         recursiveDisable(bindings);
482     }
483 }
484
485 void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags)
486 {
487     if (!b)
488         return;
489
490     recursiveEnable(b->m_nextBinding, flags);
491
492     if (b)
493         b->setEnabled(true, flags);
494 }
495
496 void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b)
497 {
498     if (!b)
499         return;
500
501     recursiveDisable(b->m_nextBinding);
502
503     if (b)
504         b->setEnabled(false, 0);
505 }
506
507 void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags)
508 {
509 }
510
511 QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex)
512 {
513     QQmlAbstractBinding *binding = m_bindings;
514     
515     while (binding && binding->propertyIndex() != propertyIndex) 
516         binding = binding->m_nextBinding;
517
518     return binding;
519 }
520
521 /*!
522 Removes a collection of bindings, corresponding to the set bits in \a mask.
523 */
524 void QQmlValueTypeProxyBinding::removeBindings(quint32 mask)
525 {
526     QQmlAbstractBinding *binding = m_bindings;
527     while (binding) {
528         if (mask & (1 << (binding->propertyIndex() >> 24))) {
529             QQmlAbstractBinding *remove = binding;
530             binding = remove->m_nextBinding;
531             *remove->m_prevBinding = remove->m_nextBinding;
532             if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
533             remove->m_prevBinding = 0;
534             remove->m_nextBinding = 0;
535             remove->destroy();
536         } else {
537             binding = binding->m_nextBinding;
538         }
539     }
540 }
541
542 int QQmlValueTypeProxyBinding::propertyIndex() const
543 {
544     return m_index;
545 }
546
547 QObject *QQmlValueTypeProxyBinding::object() const
548 {
549     return m_object;
550 }
551
552 QT_END_NAMESPACE