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/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"
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>
57 #include <QtCore/qstringbuilder.h>
58 #include <QtCore/qdebug.h>
60 Q_DECLARE_METATYPE(QJSValue)
64 static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = {
65 QQmlBoundSignalExpression::expressionIdentifier,
66 QQmlBoundSignalExpression::expressionChanged
69 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
70 bool isRewritten, const QString &fileName, quint16 line, quint16 column)
71 : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
73 setNotifyOnValueChanged(false);
75 setScopeObject(scope);
77 m_expressionUtf8 = expression;
79 m_expression = QString::fromUtf8(expression);
80 m_expressionFunctionValid = false;
81 m_expressionFunctionRewritten = isRewritten;
82 m_fileName = fileName;
87 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression,
88 bool isRewritten, const QString &fileName, quint16 line, quint16 column)
89 : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
91 setNotifyOnValueChanged(false);
93 setScopeObject(scope);
95 m_expressionUtf8 = expression.toUtf8();
97 m_expression = expression;
98 m_expressionFunctionValid = false;
99 m_expressionFunctionRewritten = isRewritten;
100 m_fileName = fileName;
105 QQmlBoundSignalExpression::~QQmlBoundSignalExpression()
107 qPersistentDispose(m_v8function);
108 qPersistentDispose(m_v8qmlscope);
111 QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e)
113 QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e);
114 return This->sourceFile() + QLatin1Char(':') + QString::number(This->lineNumber());
117 void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *)
119 // bound signals do not notify on change.
122 QString QQmlBoundSignalExpression::expression() const
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);
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)
140 Q_ASSERT (context() && engine());
141 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
143 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
145 v8::HandleScope handle_scope;
146 v8::Context::Scope context_scope(ep->v8engine()->context());
147 if (!m_expressionFunctionValid) {
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();
155 QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
156 const QString &code = rewriteSignalHandler(m_expression, QString()/*no name hint available*/, &ok);
158 m_v8function = evalFunction(context(), scopeObject(), code, m_fileName, m_line, &m_v8qmlscope);
159 m_expression.clear();
162 if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
163 ep->dereferenceScarceResources();
164 return; // could not evaluate function. Not valid.
167 setUseSharedContext(false);
168 m_expressionFunctionValid = true;
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);
177 QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
180 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
183 ////////////////////////////////////////////////////////////////////////
185 class QQmlBoundSignalParameters : public QObject
189 QQmlBoundSignalParameters(const QMetaMethod &, QQmlAbstractBoundSignal*, QQmlEngine*);
190 ~QQmlBoundSignalParameters();
192 void setValues(void **);
196 friend class MetaObject;
197 int metaCall(QMetaObject::Call, int _id, void **);
198 struct MetaObject : public QAbstractDynamicMetaObject {
199 MetaObject(QQmlBoundSignalParameters *b)
202 int metaCall(QMetaObject::Call c, int id, void **a) {
203 return parent->metaCall(c, id, a);
205 QQmlBoundSignalParameters *parent;
210 QMetaObject *myMetaObject;
213 QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
214 : m_prevSignal(0), m_nextSignal(0)
218 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
223 void QQmlAbstractBoundSignal::addToObject(QObject *obj)
225 Q_ASSERT(!m_prevSignal);
228 QQmlData *data = QQmlData::get(obj, true);
230 m_nextSignal = data->signalHandlers;
231 if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
232 m_prevSignal = &data->signalHandlers;
233 data->signalHandlers = this;
236 void QQmlAbstractBoundSignal::removeFromObject()
239 *m_prevSignal = m_nextSignal;
240 if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
247 \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()).
248 This is different from QMetaMethod::methodIndex().
250 QQmlBoundSignal::QQmlBoundSignal(QObject *scope, int signal, QObject *owner,
252 : m_expression(0), m_params(0), m_scope(scope), m_index(signal)
254 setParamsValid(false);
255 setIsEvaluating(false);
257 setCallback(QQmlNotifierEndpoint::QQmlBoundSignal);
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.
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())
270 while (QMetaObjectPrivate::signal(scope->metaObject(), m_index).attributes() & QMetaMethod::Cloned)
274 QQmlNotifierEndpoint::connect(scope, m_index, engine);
277 QQmlBoundSignal::~QQmlBoundSignal()
284 Returns the signal index in the range returned by QObjectPrivate::signalIndex().
285 This is different from QMetaMethod::methodIndex().
287 int QQmlBoundSignal::index() const
293 Returns the signal expression.
295 QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
301 Sets the signal expression to \a e. Returns the current signal expression,
302 or null if there is no signal expression.
304 The QQmlBoundSignal instance adds a reference to \a e. The caller
305 assumes ownership of the returned QQmlBoundSignalExpression reference.
307 QQmlBoundSignalExpressionPointer QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e)
309 QQmlBoundSignalExpressionPointer rv = m_expression;
311 if (m_expression) m_expression->setNotifyOnValueChanged(false);
316 Sets the signal expression to \a e. Returns the current signal expression,
317 or null if there is no signal expression.
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.
322 QQmlBoundSignalExpressionPointer QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e)
324 QQmlBoundSignalExpressionPointer rv = m_expression;
325 m_expression.take(e);
326 if (m_expression) m_expression->setNotifyOnValueChanged(false);
330 void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
332 QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e);
333 if (!s->m_expression)
336 if (QQmlDebugService::isDebuggingEnabled())
337 QV8DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_scope->metaObject(), s->m_index).methodSignature()));
339 QQmlHandlingSignalProfiler prof(s->m_expression);
341 s->setIsEvaluating(true);
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());
350 s->setParamsValid(true);
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));
361 if (s->m_params) s->m_params->clearValues();
363 s->setIsEvaluating(false);
366 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method,
367 QQmlAbstractBoundSignal *owner,
369 : types(0), values(0)
371 MetaObject *mo = new MetaObject(this);
374 QMetaObjectBuilder mob;
375 mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
376 mob.setClassName("QQmlBoundSignalParameters");
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()) {
388 QByteArray name = paramNames.at(ii);
390 name = "__qt_anonymous_param_" + QByteArray::number(ii);
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);
398 QByteArray propType = type;
399 QMetaType::TypeFlags flags = QMetaType::typeFlags(t);
400 if (flags & QMetaType::IsEnumeration) {
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
409 int scopeIdx = propType.lastIndexOf("::");
410 if (scopeIdx != -1) {
411 scope = propType.left(scopeIdx);
412 name = propType.mid(scopeIdx + 2);
416 const QMetaObject *meta;
418 meta = &QObject::staticQtMetaObject;
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))) {
431 QMetaPropertyBuilder prop = mob.addProperty(name, propType);
432 prop.setWritable(false);
435 myMetaObject = mob.toMetaObject();
436 *static_cast<QMetaObject *>(mo) = *myMetaObject;
438 d_ptr->metaObject = mo;
441 QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
447 void QQmlBoundSignalParameters::setValues(void **v)
452 void QQmlBoundSignalParameters::clearValues()
457 int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
462 if (c == QMetaObject::ReadProperty && id >= 1) {
463 int t = types[id - 1];
465 QMetaType::destruct(t, p);
466 QMetaType::construct(t, p, values[id]);
469 return qt_metacall(c, id, a);
473 ////////////////////////////////////////////////////////////////////////
475 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(QQmlBoundSignalExpression *o)
481 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(const QQmlBoundSignalExpressionPointer &other)
487 QQmlBoundSignalExpressionPointer::~QQmlBoundSignalExpressionPointer()
492 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(const QQmlBoundSignalExpressionPointer &other)
494 if (other.o) other.o->addref();
500 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(QQmlBoundSignalExpression *other)
502 if (other) other->addref();
509 Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership
510 of the callers reference of other.
512 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::take(QQmlBoundSignalExpression *other)
521 #include <qqmlboundsignal.moc>