Support and use parameters in QQmlNotifierEndpoint.
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlboundsignal.cpp
index f8015a5..db689b0 100644 (file)
@@ -49,6 +49,7 @@
 #include "qqml.h"
 #include "qqmlcontext.h"
 #include "qqmlglobal_p.h"
+#include "qqmlrewrite_p.h"
 #include <private/qqmlprofilerservice_p.h>
 #include <private/qv8debugservice_p.h>
 
@@ -59,11 +60,110 @@ Q_DECLARE_METATYPE(QJSValue)
 
 QT_BEGIN_NAMESPACE
 
+static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = {
+    QQmlBoundSignalExpression::expressionIdentifier,
+    QQmlBoundSignalExpression::expressionChanged
+};
+
+QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
+                                                     bool isRewritten, const QString &fileName, int line, int column)
+    : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
+{
+    setNotifyOnValueChanged(false);
+    setContext(ctxt);
+    setScopeObject(scope);
+    m_expression = QString::fromUtf8(expression);
+    m_expressionFunctionValid = false;
+    m_expressionFunctionRewritten = isRewritten;
+    m_fileName = fileName;
+    m_line = line;
+    m_column = column;
+}
+
+QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression,
+                                                     bool isRewritten, const QString &fileName, int line, int column)
+    : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
+{
+    setNotifyOnValueChanged(false);
+    setContext(ctxt);
+    setScopeObject(scope);
+    m_expression = expression;
+    m_expressionFunctionValid = false;
+    m_expressionFunctionRewritten = isRewritten;
+    m_fileName = fileName;
+    m_line = line;
+    m_column = column;
+}
+
+QQmlBoundSignalExpression::~QQmlBoundSignalExpression()
+{
+    qPersistentDispose(m_v8function);
+    qPersistentDispose(m_v8qmlscope);
+}
+
+QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e)
+{
+    QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e);
+    return QLatin1String("\"") + This->m_expression + QLatin1String("\"");
+}
+
+void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *)
+{
+    // bound signals do not notify on change.
+}
+
+// This mirrors code in QQmlExpressionPrivate::value() and v8value().
+// Any change made here should be made there and vice versa.
+void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope)
+{
+    Q_ASSERT (context() && engine());
+    QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
+
+    ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
+    {
+        v8::HandleScope handle_scope;
+        v8::Context::Scope context_scope(ep->v8engine()->context());
+        if (!m_expressionFunctionValid) {
+            bool ok = true;
+            QString code;
+            if (m_expressionFunctionRewritten) {
+                code = m_expression;
+            } else {
+                QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
+                code = rewriteSignalHandler(m_expression, m_functionName, &ok);
+            }
+
+            if (ok)
+                m_v8function = evalFunction(context(), scopeObject(), code, m_fileName, m_line, &m_v8qmlscope);
+
+            if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
+                ep->dereferenceScarceResources();
+                return; // could not evaluate function.  Not valid.
+            }
+
+            setUseSharedContext(false);
+            m_expressionFunctionValid = true;
+        }
+
+        if (secondaryScope) {
+            QObject *restoreSecondaryScope = 0;
+            restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, secondaryScope);
+            QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
+            ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, restoreSecondaryScope);
+        } else {
+            QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
+        }
+    }
+    ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
+}
+
+////////////////////////////////////////////////////////////////////////
+
 class QQmlBoundSignalParameters : public QObject
 {
 Q_OBJECT
 public:
-    QQmlBoundSignalParameters(const QMetaMethod &, QObject * = 0);
+    QQmlBoundSignalParameters(const QMetaMethod &, QQmlAbstractBoundSignal*);
     ~QQmlBoundSignalParameters();
 
     void setValues(void **);
@@ -87,61 +187,64 @@ private:
     QMetaObject *myMetaObject;
 };
 
-static int evaluateIdx = -1;
-
-QQmlAbstractBoundSignal::QQmlAbstractBoundSignal(QObject *parent)
-: QObject(parent)
+QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
+: m_prevSignal(0), m_nextSignal(0)
 {
 }
 
 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
 {
+    removeFromObject();
 }
 
-QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal, 
-                               QObject *parent)
-: m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0)
+void QQmlAbstractBoundSignal::addToObject(QObject *obj)
 {
-    // This is thread safe.  Although it may be updated by two threads, they
-    // will both set it to the same value - so the worst thing that can happen
-    // is that they both do the work to figure it out.  Boo hoo.
-    if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount();
+    Q_ASSERT(!m_prevSignal);
+    Q_ASSERT(obj);
 
-    QQml_setParent_noEvent(this, parent);
-    QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx);
+    QQmlData *data = QQmlData::get(obj, true);
+
+    m_nextSignal = data->signalHandlers;
+    if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
+    m_prevSignal = &data->signalHandlers;
+    data->signalHandlers = this;
 }
 
-QQmlBoundSignal::QQmlBoundSignal(QQmlContext *ctxt, const QString &val, 
-                               QObject *scope, const QMetaMethod &signal,
-                               QObject *parent)
-: m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0)
+void QQmlAbstractBoundSignal::removeFromObject()
 {
-    // This is thread safe.  Although it may be updated by two threads, they
-    // will both set it to the same value - so the worst thing that can happen
-    // is that they both do the work to figure it out.  Boo hoo.
-    if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount();
-
-    QQml_setParent_noEvent(this, parent);
-    QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx);
+    if (m_prevSignal) {
+        *m_prevSignal = m_nextSignal;
+        if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
+        m_prevSignal = 0;
+        m_nextSignal = 0;
+    }
+}
 
-    m_expression = new QQmlExpression(ctxt, scope, val);
+QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal,
+                               QObject *owner)
+: m_expression(0), m_params(0), m_scope(scope), m_index(signal.methodIndex()), m_paramsValid(false), m_isEvaluating(false)
+{
+    addToObject(owner);
+    callback = &subscriptionCallback;
+    QQmlNotifierEndpoint::connect(scope, m_index);
 }
 
 QQmlBoundSignal::~QQmlBoundSignal()
 {
     delete m_expression;
     m_expression = 0;
+    delete m_params;
 }
 
-int QQmlBoundSignal::index() const 
-{ 
-    return m_signal.methodIndex();
+int QQmlBoundSignal::index() const
+{
+    return m_index;
 }
 
 /*!
     Returns the signal expression.
 */
-QQmlExpression *QQmlBoundSignal::expression() const
+QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
 {
     return m_expression;
 }
@@ -153,49 +256,48 @@ QQmlExpression *QQmlBoundSignal::expression() const
     The QQmlBoundSignal instance takes ownership of \a e.  The caller is 
     assumes ownership of the returned QQmlExpression.
 */
-QQmlExpression *QQmlBoundSignal::setExpression(QQmlExpression *e)
+QQmlBoundSignalExpression *QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e)
 {
-    QQmlExpression *rv = m_expression;
+    QQmlBoundSignalExpression *rv = m_expression;
     m_expression = e;
     if (m_expression) m_expression->setNotifyOnValueChanged(false);
     return rv;
 }
 
-int QQmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a)
+void QQmlBoundSignal::subscriptionCallback(QQmlNotifierEndpoint *e, void **a)
 {
-    if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) {
-        if (!m_expression)
-            return -1;
+    QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e);
+    if (!s->m_expression)
+        return;
 
-        if (QQmlDebugService::isDebuggingEnabled())
-            QV8DebugService::instance()->signalEmitted(QString::fromAscii(m_signal.signature()));
+    if (QQmlDebugService::isDebuggingEnabled())
+        QV8DebugService::instance()->signalEmitted(QString::fromAscii(s->m_scope->metaObject()->method(s->m_index).methodSignature()));
 
-        QQmlHandlingSignalProfiler prof(m_signal, m_expression);
+    QQmlHandlingSignalProfiler prof(s->m_scope, s->m_index, s->m_expression);
 
-        m_isEvaluating = true;
-        if (!m_paramsValid) {
-            if (!m_signal.parameterTypes().isEmpty())
-                m_params = new QQmlBoundSignalParameters(m_signal, this);
-            m_paramsValid = true;
-        }
+    s->m_isEvaluating = true;
 
-        if (m_params) m_params->setValues(a);
-        if (m_expression && m_expression->engine()) {
-            QQmlExpressionPrivate::get(m_expression)->value(m_params);
-            if (m_expression && m_expression->hasError())
-                QQmlEnginePrivate::warning(m_expression->engine(), m_expression->error());
-        }
-        if (m_params) m_params->clearValues();
-        m_isEvaluating = false;
-        return -1;
-    } else {
-        return QObject::qt_metacall(c, id, a);
+    if (!s->m_paramsValid) {
+        QMetaMethod signal = s->m_scope->metaObject()->method(s->m_index);
+        if (!signal.parameterTypes().isEmpty())
+            s->m_params = new QQmlBoundSignalParameters(signal, s);
+        s->m_paramsValid = true;
     }
+
+    if (s->m_params) s->m_params->setValues(a);
+    if (s->m_expression && s->m_expression->engine()) {
+        s->m_expression->evaluate(s->m_params);
+        if (s->m_expression && s->m_expression->hasError())
+            QQmlEnginePrivate::warning(s->m_expression->engine(), s->m_expression->error());
+    }
+    if (s->m_params) s->m_params->clearValues();
+
+    s->m_isEvaluating = false;
 }
 
 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, 
-                                                                     QObject *parent)
-: QObject(parent), types(0), values(0)
+                                                     QQmlAbstractBoundSignal *owner)
+: types(0), values(0)
 {
     MetaObject *mo = new MetaObject(this);
 
@@ -216,7 +318,7 @@ QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method,
             continue;
         }
 
-        QVariant::Type t = (QVariant::Type)QMetaType::type(type.constData());
+        int t = QMetaType::type(type.constData());
         if (QQmlMetaType::isQObject(t)) {
             types[ii] = QMetaType::QObjectStar;
             QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*");
@@ -227,8 +329,8 @@ QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method,
             if (flags & QMetaType::IsEnumeration) {
                 t = QVariant::Int;
                 propType = "int";
-            } else if (t == QVariant::Invalid ||
-                       (t >= QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
+            } else if (t == QMetaType::UnknownType ||
+                       (t >= int(QMetaType::User) && !(flags & QMetaType::PointerToQObject) &&
                         t != qMetaTypeId<QJSValue>())) {
                 //the UserType clause is to catch registered QFlags
                 QByteArray scope;
@@ -244,7 +346,7 @@ QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method,
                 if (scope == "Qt")
                     meta = &QObject::staticQtMetaObject;
                 else
-                    meta = parent->parent()->metaObject();   //### assumes parent->parent()
+                    meta = owner->scope()->metaObject();
                 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
                     QMetaEnum m = meta->enumerator(i);
                     if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) {