Change uses of {to,from}Ascii to {to,from}Latin1
[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 &, QQmlAbstractBoundSignal*);
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 QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
191 : m_prevSignal(0), m_nextSignal(0)
192 {
193 }
194
195 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
196 {
197     removeFromObject();
198 }
199
200 void QQmlAbstractBoundSignal::addToObject(QObject *obj)
201 {
202     Q_ASSERT(!m_prevSignal);
203     Q_ASSERT(obj);
204
205     QQmlData *data = QQmlData::get(obj, true);
206
207     m_nextSignal = data->signalHandlers;
208     if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
209     m_prevSignal = &data->signalHandlers;
210     data->signalHandlers = this;
211 }
212
213 void QQmlAbstractBoundSignal::removeFromObject()
214 {
215     if (m_prevSignal) {
216         *m_prevSignal = m_nextSignal;
217         if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
218         m_prevSignal = 0;
219         m_nextSignal = 0;
220     }
221 }
222
223 QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal,
224                                QObject *owner)
225 : m_expression(0), m_params(0), m_scope(scope), m_index(signal.methodIndex())
226 {
227     setParamsValid(false);
228     setIsEvaluating(false);
229     addToObject(owner);
230     callback = &subscriptionCallback;
231     QQmlNotifierEndpoint::connect(scope, m_index);
232 }
233
234 QQmlBoundSignal::~QQmlBoundSignal()
235 {
236     m_expression = 0;
237     delete m_params;
238 }
239
240 int QQmlBoundSignal::index() const
241 {
242     return m_index;
243 }
244
245 /*!
246     Returns the signal expression.
247 */
248 QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
249 {
250     return m_expression;
251 }
252
253 /*!
254     Sets the signal expression to \a e.  Returns the current signal expression,
255     or null if there is no signal expression.
256
257     The QQmlBoundSignal instance adds a reference to \a e.  The caller
258     assumes ownership of the returned QQmlBoundSignalExpression reference.
259 */
260 QQmlBoundSignalExpressionPointer QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e)
261 {
262     QQmlBoundSignalExpressionPointer rv = m_expression;
263     m_expression = e;
264     if (m_expression) m_expression->setNotifyOnValueChanged(false);
265     return rv;
266 }
267
268 /*!
269     Sets the signal expression to \a e.  Returns the current signal expression,
270     or null if there is no signal expression.
271
272     The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference).  The caller
273     assumes ownership of the returned QQmlBoundSignalExpression reference.
274 */
275 QQmlBoundSignalExpressionPointer QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e)
276 {
277     QQmlBoundSignalExpressionPointer rv = m_expression;
278     m_expression.take(e);
279     if (m_expression) m_expression->setNotifyOnValueChanged(false);
280     return rv;
281 }
282
283 void QQmlBoundSignal::subscriptionCallback(QQmlNotifierEndpoint *e, void **a)
284 {
285     QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e);
286     if (!s->m_expression)
287         return;
288
289     if (QQmlDebugService::isDebuggingEnabled())
290         QV8DebugService::instance()->signalEmitted(QString::fromLatin1(s->m_scope->metaObject()->method(s->m_index).methodSignature()));
291
292     QQmlHandlingSignalProfiler prof(*(s->m_scope), s->m_index, s->m_expression);
293
294     s->setIsEvaluating(true);
295
296     if (!s->paramsValid()) {
297         QMetaMethod signal = s->m_scope->metaObject()->method(s->m_index);
298         if (!signal.parameterTypes().isEmpty())
299             s->m_params = new QQmlBoundSignalParameters(signal, s);
300         s->setParamsValid(true);
301     }
302
303     if (s->m_params) s->m_params->setValues(a);
304     if (s->m_expression && s->m_expression->engine()) {
305         s->m_expression->evaluate(s->m_params);
306         if (s->m_expression && s->m_expression->hasError())
307             QQmlEnginePrivate::warning(s->m_expression->engine(), s->m_expression->error());
308     }
309     if (s->m_params) s->m_params->clearValues();
310
311     s->setIsEvaluating(false);
312 }
313
314 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, 
315                                                      QQmlAbstractBoundSignal *owner)
316 : types(0), values(0)
317 {
318     MetaObject *mo = new MetaObject(this);
319
320     // ### Optimize!
321     QMetaObjectBuilder mob;
322     mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
323     mob.setClassName("QQmlBoundSignalParameters");
324
325     QList<QByteArray> paramTypes = method.parameterTypes();
326     QList<QByteArray> paramNames = method.parameterNames();
327     types = new int[paramTypes.count()];
328     for (int ii = 0; ii < paramTypes.count(); ++ii) {
329         const QByteArray &type = paramTypes.at(ii);
330         const QByteArray &name = paramNames.at(ii);
331
332         if (name.isEmpty() || type.isEmpty()) {
333             types[ii] = 0;
334             continue;
335         }
336
337         int t = QMetaType::type(type.constData());
338         if (QQmlMetaType::isQObject(t)) {
339             types[ii] = QMetaType::QObjectStar;
340             QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*");
341             prop.setWritable(false);
342         } else {
343             QByteArray propType = type;
344             QMetaType::TypeFlags flags = QMetaType::typeFlags(t);
345             if (flags & QMetaType::IsEnumeration) {
346                 t = QVariant::Int;
347                 propType = "int";
348             } else if (t == QMetaType::UnknownType ||
349                        (t >= int(QMetaType::User) && !(flags & QMetaType::PointerToQObject) &&
350                         t != qMetaTypeId<QJSValue>())) {
351                 //the UserType clause is to catch registered QFlags
352                 QByteArray scope;
353                 QByteArray name;
354                 int scopeIdx = propType.lastIndexOf("::");
355                 if (scopeIdx != -1) {
356                     scope = propType.left(scopeIdx);
357                     name = propType.mid(scopeIdx + 2);
358                 } else {
359                     name = propType;
360                 }
361                 const QMetaObject *meta;
362                 if (scope == "Qt")
363                     meta = &QObject::staticQtMetaObject;
364                 else
365                     meta = owner->scope()->metaObject();
366                 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
367                     QMetaEnum m = meta->enumerator(i);
368                     if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) {
369                         t = QVariant::Int;
370                         propType = "int";
371                         break;
372                     }
373                 }
374             }
375             types[ii] = t;
376             QMetaPropertyBuilder prop = mob.addProperty(name, propType);
377             prop.setWritable(false);
378         }
379     }
380     myMetaObject = mob.toMetaObject();
381     *static_cast<QMetaObject *>(mo) = *myMetaObject;
382
383     d_ptr->metaObject = mo;
384 }
385
386 QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
387 {
388     delete [] types;
389     free(myMetaObject);
390 }
391
392 void QQmlBoundSignalParameters::setValues(void **v)
393 {
394     values = v;
395 }
396
397 void QQmlBoundSignalParameters::clearValues()
398 {
399     values = 0;
400 }
401
402 int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
403 {
404     if (!values)
405         return -1;
406
407     if (c == QMetaObject::ReadProperty && id >= 1) {
408         int t = types[id - 1];
409         void *p = a[0];
410         QMetaType::destruct(t, p);
411         QMetaType::construct(t, p, values[id]);
412         return -1;
413     } else {
414         return qt_metacall(c, id, a);
415     }
416 }
417
418 ////////////////////////////////////////////////////////////////////////
419
420 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(QQmlBoundSignalExpression *o)
421 : o(o)
422 {
423     if (o) o->addref();
424 }
425
426 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(const QQmlBoundSignalExpressionPointer &other)
427 : o(other.o)
428 {
429     if (o) o->addref();
430 }
431
432 QQmlBoundSignalExpressionPointer::~QQmlBoundSignalExpressionPointer()
433 {
434     if (o) o->release();
435 }
436
437 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(const QQmlBoundSignalExpressionPointer &other)
438 {
439     if (other.o) other.o->addref();
440     if (o) o->release();
441     o = other.o;
442     return *this;
443 }
444
445 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(QQmlBoundSignalExpression *other)
446 {
447     if (other) other->addref();
448     if (o) o->release();
449     o = other;
450     return *this;
451 }
452
453 /*!
454 Takes ownership of \a other.  take() does *not* add a reference, as it assumes ownership
455 of the callers reference of other.
456 */
457 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::take(QQmlBoundSignalExpression *other)
458 {
459     if (o) o->release();
460     o = other;
461     return *this;
462 }
463
464 QT_END_NAMESPACE
465
466 #include <qqmlboundsignal.moc>