af63cb122db75b748fafe1d11e7e4ba5737e9245
[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/qmetaobjectbuilder_p.h>
45 #include "qqmlengine_p.h"
46 #include "qqmlexpression_p.h"
47 #include "qqmlcontext_p.h"
48 #include "qqmlmetatype_p.h"
49 #include "qqml.h"
50 #include "qqmlcontext.h"
51 #include "qqmlglobal_p.h"
52 #include "qqmlrewrite_p.h"
53 #include <private/qqmlprofilerservice_p.h>
54 #include <private/qv8debugservice_p.h>
55
56 #include <QtCore/qstringbuilder.h>
57 #include <QtCore/qdebug.h>
58
59 Q_DECLARE_METATYPE(QJSValue)
60
61 QT_BEGIN_NAMESPACE
62
63 static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = {
64     QQmlBoundSignalExpression::expressionIdentifier,
65     QQmlBoundSignalExpression::expressionChanged
66 };
67
68 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
69                                                      bool isRewritten, const QString &fileName, int line, int column)
70     : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
71 {
72     setNotifyOnValueChanged(false);
73     setContext(ctxt);
74     setScopeObject(scope);
75     m_expression = QString::fromUtf8(expression);
76     m_expressionFunctionValid = false;
77     m_expressionFunctionRewritten = isRewritten;
78     m_fileName = fileName;
79     m_line = line;
80     m_column = column;
81 }
82
83 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression,
84                                                      bool isRewritten, const QString &fileName, int line, int column)
85     : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
86 {
87     setNotifyOnValueChanged(false);
88     setContext(ctxt);
89     setScopeObject(scope);
90     m_expression = expression;
91     m_expressionFunctionValid = false;
92     m_expressionFunctionRewritten = isRewritten;
93     m_fileName = fileName;
94     m_line = line;
95     m_column = column;
96 }
97
98 QQmlBoundSignalExpression::~QQmlBoundSignalExpression()
99 {
100     qPersistentDispose(m_v8function);
101     qPersistentDispose(m_v8qmlscope);
102 }
103
104 QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e)
105 {
106     QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e);
107     return QLatin1String("\"") + This->m_expression + QLatin1String("\"");
108 }
109
110 void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *)
111 {
112     // bound signals do not notify on change.
113 }
114
115 // This mirrors code in QQmlExpressionPrivate::value() and v8value().
116 // Any change made here should be made there and vice versa.
117 void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope)
118 {
119     Q_ASSERT (context() && engine());
120     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
121
122     ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
123     {
124         v8::HandleScope handle_scope;
125         v8::Context::Scope context_scope(ep->v8engine()->context());
126         if (!m_expressionFunctionValid) {
127             bool ok = true;
128             QString code;
129             if (m_expressionFunctionRewritten) {
130                 code = m_expression;
131             } else {
132                 QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
133                 code = rewriteSignalHandler(m_expression, m_functionName, &ok);
134             }
135
136             if (ok)
137                 m_v8function = evalFunction(context(), scopeObject(), code, m_fileName, m_line, &m_v8qmlscope);
138
139             if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
140                 ep->dereferenceScarceResources();
141                 return; // could not evaluate function.  Not valid.
142             }
143
144             setUseSharedContext(false);
145             m_expressionFunctionValid = true;
146         }
147
148         if (secondaryScope) {
149             QObject *restoreSecondaryScope = 0;
150             restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, secondaryScope);
151             QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
152             ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, restoreSecondaryScope);
153         } else {
154             QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
155         }
156     }
157     ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
158 }
159
160 ////////////////////////////////////////////////////////////////////////
161
162 class QQmlBoundSignalParameters : public QObject
163 {
164 Q_OBJECT
165 public:
166     QQmlBoundSignalParameters(const QMetaMethod &, QObject * = 0);
167     ~QQmlBoundSignalParameters();
168
169     void setValues(void **);
170     void clearValues();
171
172 private:
173     friend class MetaObject;
174     int metaCall(QMetaObject::Call, int _id, void **);
175     struct MetaObject : public QAbstractDynamicMetaObject {
176         MetaObject(QQmlBoundSignalParameters *b)
177             : parent(b) {}
178
179         int metaCall(QMetaObject::Call c, int id, void **a) { 
180             return parent->metaCall(c, id, a);
181         }
182         QQmlBoundSignalParameters *parent;
183     };
184
185     int *types;
186     void **values;
187     QMetaObject *myMetaObject;
188 };
189
190 static int evaluateIdx = -1;
191
192 QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
193 : m_prevSignal(0), m_nextSignal(0)
194 {
195 }
196
197 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
198 {
199     if (m_prevSignal) {
200         *m_prevSignal = m_nextSignal;
201         if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
202         m_prevSignal = 0;
203         m_nextSignal = 0;
204     }
205 }
206
207 void QQmlAbstractBoundSignal::addToObject()
208 {
209     Q_ASSERT(!m_prevSignal);
210     QObject *obj = object();
211     Q_ASSERT(obj);
212
213     QQmlData *data = QQmlData::get(obj, true);
214
215     m_nextSignal = data->signalHandlers;
216     if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
217     m_prevSignal = &data->signalHandlers;
218     data->signalHandlers = this;
219 }
220
221 QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal,
222                                QObject *owner)
223 : m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0), m_owner(owner)
224 {
225     // This is thread safe.  Although it may be updated by two threads, they
226     // will both set it to the same value - so the worst thing that can happen
227     // is that they both do the work to figure it out.  Boo hoo.
228     if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount();
229
230     QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx);
231 }
232
233 QQmlBoundSignal::~QQmlBoundSignal()
234 {
235     delete m_expression;
236     m_expression = 0;
237 }
238
239 int QQmlBoundSignal::index() const 
240
241     return m_signal.methodIndex();
242 }
243
244 /*!
245     Returns the signal expression.
246 */
247 QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
248 {
249     return m_expression;
250 }
251
252 /*!
253     Sets the signal expression to \a e.  Returns the current signal expression,
254     or null if there is no signal expression.
255
256     The QQmlBoundSignal instance takes ownership of \a e.  The caller is 
257     assumes ownership of the returned QQmlExpression.
258 */
259 QQmlBoundSignalExpression *QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e)
260 {
261     QQmlBoundSignalExpression *rv = m_expression;
262     m_expression = e;
263     if (m_expression) m_expression->setNotifyOnValueChanged(false);
264     return rv;
265 }
266
267 int QQmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a)
268 {
269     if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) {
270         if (!m_expression)
271             return -1;
272
273         if (QQmlDebugService::isDebuggingEnabled())
274             QV8DebugService::instance()->signalEmitted(QString::fromAscii(m_signal.signature()));
275
276         QQmlHandlingSignalProfiler prof(m_signal, m_expression);
277
278         m_isEvaluating = true;
279         if (!m_paramsValid) {
280             if (!m_signal.parameterTypes().isEmpty())
281                 m_params = new QQmlBoundSignalParameters(m_signal, this);
282             m_paramsValid = true;
283         }
284
285         if (m_params) m_params->setValues(a);
286         if (m_expression && m_expression->context() && m_expression->engine()) {
287             m_expression->evaluate(m_params);
288             if (m_expression && m_expression->hasError())
289                 QQmlEnginePrivate::warning(m_expression->engine(), m_expression->error());
290         }
291         if (m_params) m_params->clearValues();
292         m_isEvaluating = false;
293         return -1;
294     } else {
295         return QObject::qt_metacall(c, id, a);
296     }
297 }
298
299 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, 
300                                                                      QObject *parent)
301 : QObject(parent), types(0), values(0)
302 {
303     MetaObject *mo = new MetaObject(this);
304
305     // ### Optimize!
306     QMetaObjectBuilder mob;
307     mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
308     mob.setClassName("QQmlBoundSignalParameters");
309
310     QList<QByteArray> paramTypes = method.parameterTypes();
311     QList<QByteArray> paramNames = method.parameterNames();
312     types = new int[paramTypes.count()];
313     for (int ii = 0; ii < paramTypes.count(); ++ii) {
314         const QByteArray &type = paramTypes.at(ii);
315         const QByteArray &name = paramNames.at(ii);
316
317         if (name.isEmpty() || type.isEmpty()) {
318             types[ii] = 0;
319             continue;
320         }
321
322         QVariant::Type t = (QVariant::Type)QMetaType::type(type.constData());
323         if (QQmlMetaType::isQObject(t)) {
324             types[ii] = QMetaType::QObjectStar;
325             QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*");
326             prop.setWritable(false);
327         } else {
328             QByteArray propType = type;
329             QMetaType::TypeFlags flags = QMetaType::typeFlags(t);
330             if (flags & QMetaType::IsEnumeration) {
331                 t = QVariant::Int;
332                 propType = "int";
333             } else if (t == QVariant::Invalid ||
334                        (t >= QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
335                         t != qMetaTypeId<QJSValue>())) {
336                 //the UserType clause is to catch registered QFlags
337                 QByteArray scope;
338                 QByteArray name;
339                 int scopeIdx = propType.lastIndexOf("::");
340                 if (scopeIdx != -1) {
341                     scope = propType.left(scopeIdx);
342                     name = propType.mid(scopeIdx + 2);
343                 } else {
344                     name = propType;
345                 }
346                 const QMetaObject *meta;
347                 if (scope == "Qt")
348                     meta = &QObject::staticQtMetaObject;
349                 else
350                     meta = static_cast<QQmlBoundSignal*>(parent)->object()->metaObject();
351                 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
352                     QMetaEnum m = meta->enumerator(i);
353                     if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) {
354                         t = QVariant::Int;
355                         propType = "int";
356                         break;
357                     }
358                 }
359             }
360             types[ii] = t;
361             QMetaPropertyBuilder prop = mob.addProperty(name, propType);
362             prop.setWritable(false);
363         }
364     }
365     myMetaObject = mob.toMetaObject();
366     *static_cast<QMetaObject *>(mo) = *myMetaObject;
367
368     d_ptr->metaObject = mo;
369 }
370
371 QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
372 {
373     delete [] types;
374     free(myMetaObject);
375 }
376
377 void QQmlBoundSignalParameters::setValues(void **v)
378 {
379     values = v;
380 }
381
382 void QQmlBoundSignalParameters::clearValues()
383 {
384     values = 0;
385 }
386
387 int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
388 {
389     if (!values)
390         return -1;
391
392     if (c == QMetaObject::ReadProperty && id >= 1) {
393         int t = types[id - 1];
394         void *p = a[0];
395         QMetaType::destruct(t, p);
396         QMetaType::construct(t, p, values[id]);
397         return -1;
398     } else {
399         return qt_metacall(c, id, a);
400     }
401 }
402
403 QT_END_NAMESPACE
404
405 #include <qqmlboundsignal.moc>