Add missing QT_{BEGIN,END}_NAMESPACE
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlboundsignal.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 "qqmlboundsignal_p.h"
43
44 #include <private/qmetaobject_p.h>
45 #include <private/qmetaobjectbuilder_p.h>
46 #include "qqmlengine_p.h"
47 #include "qqmlexpression_p.h"
48 #include "qqmlcontext_p.h"
49 #include "qqmlmetatype_p.h"
50 #include "qqml.h"
51 #include "qqmlcontext.h"
52 #include "qqmlglobal_p.h"
53 #include "qqmlrewrite_p.h"
54 #include <private/qqmlprofilerservice_p.h>
55 #include <private/qv8debugservice_p.h>
56
57 #include <QtCore/qstringbuilder.h>
58 #include <QtCore/qdebug.h>
59
60 Q_DECLARE_METATYPE(QJSValue)
61
62 QT_BEGIN_NAMESPACE
63
64 static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = {
65     QQmlBoundSignalExpression::expressionIdentifier,
66     QQmlBoundSignalExpression::expressionChanged
67 };
68
69 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
70                                                      bool isRewritten, const QString &fileName, quint16 line, quint16 column)
71     : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
72 {
73     setNotifyOnValueChanged(false);
74     setContext(ctxt);
75     setScopeObject(scope);
76     if (isRewritten)
77         m_expressionUtf8 = expression;
78     else
79         m_expression = QString::fromUtf8(expression);
80     m_expressionFunctionValid = false;
81     m_expressionFunctionRewritten = isRewritten;
82     m_fileName = fileName;
83     m_line = line;
84     m_column = column;
85 }
86
87 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression,
88                                                      bool isRewritten, const QString &fileName, quint16 line, quint16 column)
89     : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
90 {
91     setNotifyOnValueChanged(false);
92     setContext(ctxt);
93     setScopeObject(scope);
94     if (isRewritten)
95         m_expressionUtf8 = expression.toUtf8();
96     else
97         m_expression = expression;
98     m_expressionFunctionValid = false;
99     m_expressionFunctionRewritten = isRewritten;
100     m_fileName = fileName;
101     m_line = line;
102     m_column = column;
103 }
104
105 QQmlBoundSignalExpression::~QQmlBoundSignalExpression()
106 {
107     qPersistentDispose(m_v8function);
108     qPersistentDispose(m_v8qmlscope);
109 }
110
111 QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e)
112 {
113     QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e);
114     return This->sourceFile() + QLatin1Char(':') + QString::number(This->lineNumber());
115 }
116
117 void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *)
118 {
119     // bound signals do not notify on change.
120 }
121
122 QString QQmlBoundSignalExpression::expression() const
123 {
124     if (m_expressionFunctionValid) {
125         Q_ASSERT (context() && engine());
126         v8::HandleScope handle_scope;
127         v8::Context::Scope context_scope(QQmlEnginePrivate::get(engine())->v8engine()->context());
128         return QV8Engine::toStringStatic(m_v8function->ToString());
129     } else if (!m_expressionUtf8.isEmpty()) {
130         return QString::fromUtf8(m_expressionUtf8);
131     } else {
132         return m_expression;
133     }
134 }
135
136 // This mirrors code in QQmlExpressionPrivate::value() and v8value().
137 // Any change made here should be made there and vice versa.
138 void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope)
139 {
140     Q_ASSERT (context() && engine());
141     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
142
143     ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
144     {
145         v8::HandleScope handle_scope;
146         v8::Context::Scope context_scope(ep->v8engine()->context());
147         if (!m_expressionFunctionValid) {
148
149             if (m_expressionFunctionRewritten) {
150                 m_v8function = evalFunction(context(), scopeObject(), QString::fromUtf8(m_expressionUtf8),
151                                             m_fileName, m_line, &m_v8qmlscope);
152                 m_expressionUtf8.clear();
153             } else {
154                 bool ok = true;
155                 QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
156                 const QString &code = rewriteSignalHandler(m_expression, QString()/*no name hint available*/, &ok);
157                 if (ok)
158                     m_v8function = evalFunction(context(), scopeObject(), code, m_fileName, m_line, &m_v8qmlscope);
159                 m_expression.clear();
160             }
161
162             if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
163                 ep->dereferenceScarceResources();
164                 return; // could not evaluate function.  Not valid.
165             }
166
167             setUseSharedContext(false);
168             m_expressionFunctionValid = true;
169         }
170
171         if (secondaryScope) {
172             QObject *restoreSecondaryScope = 0;
173             restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, secondaryScope);
174             QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
175             ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, restoreSecondaryScope);
176         } else {
177             QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
178         }
179     }
180     ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
181 }
182
183 ////////////////////////////////////////////////////////////////////////
184
185 class QQmlBoundSignalParameters : public QObject
186 {
187 Q_OBJECT
188 public:
189     QQmlBoundSignalParameters(const QMetaMethod &, QQmlAbstractBoundSignal*, QQmlEngine*);
190     ~QQmlBoundSignalParameters();
191
192     void setValues(void **);
193     void clearValues();
194
195 private:
196     friend class MetaObject;
197     int metaCall(QMetaObject::Call, int _id, void **);
198     struct MetaObject : public QAbstractDynamicMetaObject {
199         MetaObject(QQmlBoundSignalParameters *b)
200             : parent(b) {}
201
202         int metaCall(QMetaObject::Call c, int id, void **a) { 
203             return parent->metaCall(c, id, a);
204         }
205         QQmlBoundSignalParameters *parent;
206     };
207
208     int *types;
209     void **values;
210     QMetaObject *myMetaObject;
211 };
212
213 QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
214 : m_prevSignal(0), m_nextSignal(0)
215 {
216 }
217
218 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
219 {
220     removeFromObject();
221 }
222
223 void QQmlAbstractBoundSignal::addToObject(QObject *obj)
224 {
225     Q_ASSERT(!m_prevSignal);
226     Q_ASSERT(obj);
227
228     QQmlData *data = QQmlData::get(obj, true);
229
230     m_nextSignal = data->signalHandlers;
231     if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
232     m_prevSignal = &data->signalHandlers;
233     data->signalHandlers = this;
234 }
235
236 void QQmlAbstractBoundSignal::removeFromObject()
237 {
238     if (m_prevSignal) {
239         *m_prevSignal = m_nextSignal;
240         if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
241         m_prevSignal = 0;
242         m_nextSignal = 0;
243     }
244 }
245
246 /*! \internal
247     \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()).
248     This is different from QMetaMethod::methodIndex().
249 */
250 QQmlBoundSignal::QQmlBoundSignal(QObject *scope, int signal, QObject *owner,
251                                  QQmlEngine *engine)
252 : m_expression(0), m_params(0), m_scope(scope), m_index(signal)
253 {
254     setParamsValid(false);
255     setIsEvaluating(false);
256     addToObject(owner);
257     setCallback(QQmlNotifierEndpoint::QQmlBoundSignal);
258
259     /*
260         If this is a cloned method, connect to the 'original'. For example,
261         for the signal 'void aSignal(int parameter = 0)', if the method
262         index refers to 'aSignal()', get the index of 'aSignal(int)'.
263         This ensures that 'parameter' will be available from QML.
264     */
265     if (QQmlData::get(scope, false) && QQmlData::get(scope, false)->propertyCache) {
266         QQmlPropertyCache *cache = QQmlData::get(scope, false)->propertyCache;
267         while (cache->signal(m_index)->isCloned())
268             --m_index;
269     } else {
270         while (QMetaObjectPrivate::signal(scope->metaObject(), m_index).attributes() & QMetaMethod::Cloned)
271             --m_index;
272     }
273
274     QQmlNotifierEndpoint::connect(scope, m_index, engine);
275 }
276
277 QQmlBoundSignal::~QQmlBoundSignal()
278 {
279     m_expression = 0;
280     delete m_params;
281 }
282
283 /*!
284     Returns the signal index in the range returned by QObjectPrivate::signalIndex().
285     This is different from QMetaMethod::methodIndex().
286 */
287 int QQmlBoundSignal::index() const
288 {
289     return m_index;
290 }
291
292 /*!
293     Returns the signal expression.
294 */
295 QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
296 {
297     return m_expression;
298 }
299
300 /*!
301     Sets the signal expression to \a e.  Returns the current signal expression,
302     or null if there is no signal expression.
303
304     The QQmlBoundSignal instance adds a reference to \a e.  The caller
305     assumes ownership of the returned QQmlBoundSignalExpression reference.
306 */
307 QQmlBoundSignalExpressionPointer QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e)
308 {
309     QQmlBoundSignalExpressionPointer rv = m_expression;
310     m_expression = e;
311     if (m_expression) m_expression->setNotifyOnValueChanged(false);
312     return rv;
313 }
314
315 /*!
316     Sets the signal expression to \a e.  Returns the current signal expression,
317     or null if there is no signal expression.
318
319     The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference).  The caller
320     assumes ownership of the returned QQmlBoundSignalExpression reference.
321 */
322 QQmlBoundSignalExpressionPointer QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e)
323 {
324     QQmlBoundSignalExpressionPointer rv = m_expression;
325     m_expression.take(e);
326     if (m_expression) m_expression->setNotifyOnValueChanged(false);
327     return rv;
328 }
329
330 void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
331 {
332     QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e);
333     if (!s->m_expression)
334         return;
335
336     if (QQmlDebugService::isDebuggingEnabled())
337         QV8DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_scope->metaObject(), s->m_index).methodSignature()));
338
339     QQmlHandlingSignalProfiler prof(s->m_expression);
340
341     s->setIsEvaluating(true);
342
343     if (!s->paramsValid()) {
344         QList<QByteArray> names = QQmlPropertyCache::signalParameterNames(*s->m_scope, s->m_index);
345         if (!names.isEmpty()) {
346             QMetaMethod signal = QMetaObjectPrivate::signal(s->m_scope->metaObject(), s->m_index);
347             s->m_params = new QQmlBoundSignalParameters(signal, s, s->m_expression->engine());
348         }
349
350         s->setParamsValid(true);
351     }
352
353     if (s->m_params) s->m_params->setValues(a);
354     if (s->m_expression && s->m_expression->engine()) {
355         s->m_expression->evaluate(s->m_params);
356         if (s->m_expression && s->m_expression->hasError()) {
357             QQmlEngine *engine = s->m_expression->engine();
358             QQmlEnginePrivate::warning(engine, s->m_expression->error(engine));
359         }
360     }
361     if (s->m_params) s->m_params->clearValues();
362
363     s->setIsEvaluating(false);
364 }
365
366 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, 
367                                                      QQmlAbstractBoundSignal *owner,
368                                                      QQmlEngine *engine)
369 : types(0), values(0)
370 {
371     MetaObject *mo = new MetaObject(this);
372
373     // ### Optimize!
374     QMetaObjectBuilder mob;
375     mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
376     mob.setClassName("QQmlBoundSignalParameters");
377
378     QList<QByteArray> paramTypes = method.parameterTypes();
379     QList<QByteArray> paramNames = method.parameterNames();
380     types = new int[paramTypes.count()];
381     for (int ii = 0; ii < paramTypes.count(); ++ii) {
382         const QByteArray &type = paramTypes.at(ii);
383         if (type.isEmpty()) {
384             types[ii] = 0;
385             continue;
386         }
387
388         QByteArray name = paramNames.at(ii);
389         if (name.isEmpty())
390             name = "__qt_anonymous_param_" + QByteArray::number(ii);
391
392         int t = QMetaType::type(type.constData());
393         if (QQmlEnginePrivate::get(engine)->isQObject(t)) {
394             types[ii] = QMetaType::QObjectStar;
395             QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*");
396             prop.setWritable(false);
397         } else {
398             QByteArray propType = type;
399             QMetaType::TypeFlags flags = QMetaType::typeFlags(t);
400             if (flags & QMetaType::IsEnumeration) {
401                 t = QVariant::Int;
402                 propType = "int";
403             } else if (t == QMetaType::UnknownType ||
404                        (t >= int(QMetaType::User) && !(flags & QMetaType::PointerToQObject) &&
405                         t != qMetaTypeId<QJSValue>())) {
406                 //the UserType clause is to catch registered QFlags
407                 QByteArray scope;
408                 QByteArray name;
409                 int scopeIdx = propType.lastIndexOf("::");
410                 if (scopeIdx != -1) {
411                     scope = propType.left(scopeIdx);
412                     name = propType.mid(scopeIdx + 2);
413                 } else {
414                     name = propType;
415                 }
416                 const QMetaObject *meta;
417                 if (scope == "Qt")
418                     meta = &QObject::staticQtMetaObject;
419                 else
420                     meta = owner->scope()->metaObject();
421                 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
422                     QMetaEnum m = meta->enumerator(i);
423                     if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) {
424                         t = QVariant::Int;
425                         propType = "int";
426                         break;
427                     }
428                 }
429             }
430             types[ii] = t;
431             QMetaPropertyBuilder prop = mob.addProperty(name, propType);
432             prop.setWritable(false);
433         }
434     }
435     myMetaObject = mob.toMetaObject();
436     *static_cast<QMetaObject *>(mo) = *myMetaObject;
437
438     d_ptr->metaObject = mo;
439 }
440
441 QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
442 {
443     delete [] types;
444     free(myMetaObject);
445 }
446
447 void QQmlBoundSignalParameters::setValues(void **v)
448 {
449     values = v;
450 }
451
452 void QQmlBoundSignalParameters::clearValues()
453 {
454     values = 0;
455 }
456
457 int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
458 {
459     if (!values)
460         return -1;
461
462     if (c == QMetaObject::ReadProperty && id >= 1) {
463         int t = types[id - 1];
464         void *p = a[0];
465         QMetaType::destruct(t, p);
466         QMetaType::construct(t, p, values[id]);
467         return -1;
468     } else {
469         return qt_metacall(c, id, a);
470     }
471 }
472
473 ////////////////////////////////////////////////////////////////////////
474
475 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(QQmlBoundSignalExpression *o)
476 : o(o)
477 {
478     if (o) o->addref();
479 }
480
481 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(const QQmlBoundSignalExpressionPointer &other)
482 : o(other.o)
483 {
484     if (o) o->addref();
485 }
486
487 QQmlBoundSignalExpressionPointer::~QQmlBoundSignalExpressionPointer()
488 {
489     if (o) o->release();
490 }
491
492 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(const QQmlBoundSignalExpressionPointer &other)
493 {
494     if (other.o) other.o->addref();
495     if (o) o->release();
496     o = other.o;
497     return *this;
498 }
499
500 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(QQmlBoundSignalExpression *other)
501 {
502     if (other) other->addref();
503     if (o) o->release();
504     o = other;
505     return *this;
506 }
507
508 /*!
509 Takes ownership of \a other.  take() does *not* add a reference, as it assumes ownership
510 of the callers reference of other.
511 */
512 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::take(QQmlBoundSignalExpression *other)
513 {
514     if (o) o->release();
515     o = other;
516     return *this;
517 }
518
519 QT_END_NAMESPACE
520
521 #include <qqmlboundsignal.moc>