1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qqmlboundsignal_p.h"
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"
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>
56 #include <QtCore/qstringbuilder.h>
57 #include <QtCore/qdebug.h>
59 Q_DECLARE_METATYPE(QJSValue)
63 static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = {
64 QQmlBoundSignalExpression::expressionIdentifier,
65 QQmlBoundSignalExpression::expressionChanged
68 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
69 bool isRewritten, const QString &fileName, int line, int column)
70 : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
72 setNotifyOnValueChanged(false);
74 setScopeObject(scope);
75 m_expression = QString::fromUtf8(expression);
76 m_expressionFunctionValid = false;
77 m_expressionFunctionRewritten = isRewritten;
78 m_fileName = fileName;
83 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression,
84 bool isRewritten, const QString &fileName, int line, int column)
85 : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
87 setNotifyOnValueChanged(false);
89 setScopeObject(scope);
90 m_expression = expression;
91 m_expressionFunctionValid = false;
92 m_expressionFunctionRewritten = isRewritten;
93 m_fileName = fileName;
98 QQmlBoundSignalExpression::~QQmlBoundSignalExpression()
100 qPersistentDispose(m_v8function);
101 qPersistentDispose(m_v8qmlscope);
104 QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e)
106 QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e);
107 return QLatin1String("\"") + This->m_expression + QLatin1String("\"");
110 void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *)
112 // bound signals do not notify on change.
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)
119 Q_ASSERT (context() && engine());
120 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
122 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
124 v8::HandleScope handle_scope;
125 v8::Context::Scope context_scope(ep->v8engine()->context());
126 if (!m_expressionFunctionValid) {
129 if (m_expressionFunctionRewritten) {
132 QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
133 code = rewriteSignalHandler(m_expression, m_functionName, &ok);
137 m_v8function = evalFunction(context(), scopeObject(), code, m_fileName, m_line, &m_v8qmlscope);
139 if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
140 ep->dereferenceScarceResources();
141 return; // could not evaluate function. Not valid.
144 setUseSharedContext(false);
145 m_expressionFunctionValid = true;
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);
154 QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
157 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
160 ////////////////////////////////////////////////////////////////////////
162 class QQmlBoundSignalParameters : public QObject
166 QQmlBoundSignalParameters(const QMetaMethod &, QObject * = 0);
167 ~QQmlBoundSignalParameters();
169 void setValues(void **);
173 friend class MetaObject;
174 int metaCall(QMetaObject::Call, int _id, void **);
175 struct MetaObject : public QAbstractDynamicMetaObject {
176 MetaObject(QQmlBoundSignalParameters *b)
179 int metaCall(QMetaObject::Call c, int id, void **a) {
180 return parent->metaCall(c, id, a);
182 QQmlBoundSignalParameters *parent;
187 QMetaObject *myMetaObject;
190 static int evaluateIdx = -1;
192 QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
193 : m_prevSignal(0), m_nextSignal(0)
197 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
200 *m_prevSignal = m_nextSignal;
201 if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
207 void QQmlAbstractBoundSignal::addToObject()
209 Q_ASSERT(!m_prevSignal);
210 QObject *obj = object();
213 QQmlData *data = QQmlData::get(obj, true);
215 m_nextSignal = data->signalHandlers;
216 if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
217 m_prevSignal = &data->signalHandlers;
218 data->signalHandlers = this;
221 QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal,
223 : m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0), m_owner(owner)
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();
230 QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx);
233 QQmlBoundSignal::~QQmlBoundSignal()
239 int QQmlBoundSignal::index() const
241 return m_signal.methodIndex();
245 Returns the signal expression.
247 QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
253 Sets the signal expression to \a e. Returns the current signal expression,
254 or null if there is no signal expression.
256 The QQmlBoundSignal instance takes ownership of \a e. The caller is
257 assumes ownership of the returned QQmlExpression.
259 QQmlBoundSignalExpression *QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e)
261 QQmlBoundSignalExpression *rv = m_expression;
263 if (m_expression) m_expression->setNotifyOnValueChanged(false);
267 int QQmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a)
269 if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) {
273 if (QQmlDebugService::isDebuggingEnabled())
274 QV8DebugService::instance()->signalEmitted(QString::fromAscii(m_signal.signature()));
276 QQmlHandlingSignalProfiler prof(m_signal, m_expression);
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;
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());
291 if (m_params) m_params->clearValues();
292 m_isEvaluating = false;
295 return QObject::qt_metacall(c, id, a);
299 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method,
301 : QObject(parent), types(0), values(0)
303 MetaObject *mo = new MetaObject(this);
306 QMetaObjectBuilder mob;
307 mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
308 mob.setClassName("QQmlBoundSignalParameters");
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);
317 if (name.isEmpty() || type.isEmpty()) {
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);
328 QByteArray propType = type;
329 QMetaType::TypeFlags flags = QMetaType::typeFlags(t);
330 if (flags & QMetaType::IsEnumeration) {
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
339 int scopeIdx = propType.lastIndexOf("::");
340 if (scopeIdx != -1) {
341 scope = propType.left(scopeIdx);
342 name = propType.mid(scopeIdx + 2);
346 const QMetaObject *meta;
348 meta = &QObject::staticQtMetaObject;
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))) {
361 QMetaPropertyBuilder prop = mob.addProperty(name, propType);
362 prop.setWritable(false);
365 myMetaObject = mob.toMetaObject();
366 *static_cast<QMetaObject *>(mo) = *myMetaObject;
368 d_ptr->metaObject = mo;
371 QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
377 void QQmlBoundSignalParameters::setValues(void **v)
382 void QQmlBoundSignalParameters::clearValues()
387 int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
392 if (c == QMetaObject::ReadProperty && id >= 1) {
393 int t = types[id - 1];
395 QMetaType::destruct(t, p);
396 QMetaType::construct(t, p, values[id]);
399 return qt_metacall(c, id, a);
405 #include <qqmlboundsignal.moc>