1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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>
58 #include <QtCore/qstringbuilder.h>
59 #include <QtCore/qdebug.h>
61 Q_DECLARE_METATYPE(QQmlV8Handle)
65 static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = {
66 QQmlBoundSignalExpression::expressionIdentifier,
67 QQmlBoundSignalExpression::expressionChanged
70 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
71 QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
72 bool isRewritten, const QString &fileName, quint16 line, quint16 column)
73 : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable),
77 m_parameterCountForJS(-1),
80 m_expressionFunctionValid(false),
81 m_expressionFunctionRewritten(isRewritten),
82 m_invalidParameterName(false)
86 m_expressionUtf8 = expression;
88 m_expression = QString::fromUtf8(expression);
91 QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
92 QQmlContextData *ctxt, QObject *scope, const QString &expression,
93 bool isRewritten, const QString &fileName, quint16 line, quint16 column)
94 : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable),
98 m_parameterCountForJS(-1),
101 m_expressionFunctionValid(false),
102 m_expressionFunctionRewritten(isRewritten),
103 m_invalidParameterName(false)
107 m_expressionUtf8 = expression.toUtf8();
109 m_expression = expression;
112 void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope)
114 setNotifyOnValueChanged(false);
116 setScopeObject(scope);
118 Q_ASSERT(m_target && m_index > -1);
119 m_index = QQmlPropertyCache::originalClone(m_target, m_index);
122 QQmlBoundSignalExpression::~QQmlBoundSignalExpression()
124 qPersistentDispose(m_v8function);
125 qPersistentDispose(m_v8qmlscope);
128 QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e)
130 QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e);
131 return This->sourceFile() + QLatin1Char(':') + QString::number(This->lineNumber());
134 void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *)
136 // bound signals do not notify on change.
139 QString QQmlBoundSignalExpression::expression() const
141 if (m_expressionFunctionValid) {
142 Q_ASSERT (context() && engine());
143 v8::HandleScope handle_scope;
144 v8::Context::Scope context_scope(QQmlEnginePrivate::get(engine())->v8engine()->context());
145 return QV8Engine::toStringStatic(m_v8function->ToString());
146 } else if (!m_expressionUtf8.isEmpty()) {
147 return QString::fromUtf8(m_expressionUtf8);
153 // Parts of this function mirror code in QQmlExpressionPrivate::value() and v8value().
154 // Changes made here may need to be made there and vice versa.
155 void QQmlBoundSignalExpression::evaluate(void **a)
157 Q_ASSERT (context() && engine());
159 if (m_invalidParameterName)
162 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
164 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
166 v8::HandleScope handle_scope;
167 v8::Context::Scope context_scope(ep->v8engine()->context());
168 if (!m_expressionFunctionValid) {
170 //TODO: look at using the property cache here (as in the compiler)
171 // for further optimization
172 QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
173 QQmlRewrite::RewriteSignalHandler rewriter;
178 if (m_expressionFunctionRewritten) {
179 expression = QString::fromUtf8(m_expressionUtf8);
181 //if we need parameters, and the rewrite doesn't include them,
182 //create and insert the parameter string now
183 if (m_parameterCountForJS == -1 && signal.parameterCount()) {
184 const QString ¶meters = rewriter.createParameterString(signal.parameterNames(),
185 ep->v8engine()->illegalNames());
186 int index = expression.indexOf(QLatin1Char('('), 1);
187 Q_ASSERT(index > -1);
188 expression.insert(index + 1, parameters);
190 setParameterCountForJS(rewriter.parameterCountForJS());
193 m_expressionUtf8.clear();
195 //expression is still in its original form, so perform a full rewrite
196 expression = rewriter(m_expression, QString()/*no name hint available*/, &ok,
197 signal.parameterNames(),
198 ep->v8engine()->illegalNames());
199 m_expression.clear();
202 if (rewriter.hasParameterError()) {
203 qmlInfo(scopeObject()) << rewriter.parameterError();
204 m_invalidParameterName = true;
205 ep->dereferenceScarceResources();
210 m_v8function = evalFunction(context(), scopeObject(), expression,
211 m_fileName, m_line, &m_v8qmlscope);
214 if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
215 ep->dereferenceScarceResources();
216 return; // could not evaluate function. Not valid.
219 setUseSharedContext(false);
220 m_expressionFunctionValid = true;
223 if (!hasParameterInfo()) {
224 QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
226 QV8Engine *engine = ep->v8engine();
227 QVarLengthArray<int, 9> dummy;
228 //TODO: lookup via signal index rather than method index as an optimization
229 int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex();
230 int *argsTypes = QQmlPropertyCache::methodParameterTypes(m_target, methodIndex, dummy, 0);
231 int argCount = argsTypes ? m_parameterCountForJS : 0;
233 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
235 for (int ii = 0; ii < argCount; ++ii) {
236 int type = argsTypes[ii + 1];
237 //### ideally we would use metaTypeToJS, however it currently gives different results
238 // for several cases (such as QVariant type and QObject-derived types)
239 //args[ii] = engine->metaTypeToJS(type, a[ii + 1]);
240 if (type == QMetaType::QVariant) {
241 args[ii] = engine->fromVariant(*((QVariant *)a[ii + 1]));
242 } else if (type == QMetaType::Int) {
243 //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
244 args[ii] = v8::Integer::New(*reinterpret_cast<const int*>(a[ii + 1]));
245 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
246 args[ii] = reinterpret_cast<QQmlV8Handle *>(a[ii + 1])->toHandle();
247 } else if (ep->isQObject(type)) {
248 if (!*reinterpret_cast<void* const *>(a[ii + 1]))
249 args[ii] = v8::Null();
251 args[ii] = engine->newQObject(*reinterpret_cast<QObject* const *>(a[ii + 1]));
253 args[ii] = engine->fromVariant(QVariant(type, a[ii + 1]));
257 QQmlJavaScriptExpression::evaluate(context(), m_v8function, argCount, args.data(), 0);
260 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
263 ////////////////////////////////////////////////////////////////////////
265 QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
266 : m_prevSignal(0), m_nextSignal(0)
270 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
275 void QQmlAbstractBoundSignal::addToObject(QObject *obj)
277 Q_ASSERT(!m_prevSignal);
280 QQmlData *data = QQmlData::get(obj, true);
282 m_nextSignal = data->signalHandlers;
283 if (m_nextSignal) m_nextSignal->m_prevSignal = &m_nextSignal;
284 m_prevSignal = &data->signalHandlers;
285 data->signalHandlers = this;
288 void QQmlAbstractBoundSignal::removeFromObject()
291 *m_prevSignal = m_nextSignal;
292 if (m_nextSignal) m_nextSignal->m_prevSignal = m_prevSignal;
299 \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()).
300 This is different from QMetaMethod::methodIndex().
302 QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner,
304 : m_expression(0), m_index(signal), m_isEvaluating(false)
307 setCallback(QQmlNotifierEndpoint::QQmlBoundSignal);
310 If this is a cloned method, connect to the 'original'. For example,
311 for the signal 'void aSignal(int parameter = 0)', if the method
312 index refers to 'aSignal()', get the index of 'aSignal(int)'.
313 This ensures that 'parameter' will be available from QML.
315 m_index = QQmlPropertyCache::originalClone(target, m_index);
316 QQmlNotifierEndpoint::connect(target, m_index, engine);
319 QQmlBoundSignal::~QQmlBoundSignal()
325 Returns the signal index in the range returned by QObjectPrivate::signalIndex().
326 This is different from QMetaMethod::methodIndex().
328 int QQmlBoundSignal::index() const
334 Returns the signal expression.
336 QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
342 Sets the signal expression to \a e. Returns the current signal expression,
343 or null if there is no signal expression.
345 The QQmlBoundSignal instance adds a reference to \a e. The caller
346 assumes ownership of the returned QQmlBoundSignalExpression reference.
348 QQmlBoundSignalExpressionPointer QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e)
350 QQmlBoundSignalExpressionPointer rv = m_expression;
352 if (m_expression) m_expression->setNotifyOnValueChanged(false);
357 Sets the signal expression to \a e. Returns the current signal expression,
358 or null if there is no signal expression.
360 The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference). The caller
361 assumes ownership of the returned QQmlBoundSignalExpression reference.
363 QQmlBoundSignalExpressionPointer QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e)
365 QQmlBoundSignalExpressionPointer rv = m_expression;
366 m_expression.take(e);
367 if (m_expression) m_expression->setNotifyOnValueChanged(false);
371 void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
373 QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e);
374 if (!s->m_expression)
377 if (QQmlDebugService::isDebuggingEnabled())
378 QV8DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_expression->target()->metaObject(), s->m_index).methodSignature()));
380 QQmlHandlingSignalProfiler prof(s->m_expression);
382 s->m_isEvaluating = true;
384 if (s->m_expression && s->m_expression->engine()) {
385 s->m_expression->evaluate(a);
386 if (s->m_expression && s->m_expression->hasError()) {
387 QQmlEngine *engine = s->m_expression->engine();
388 QQmlEnginePrivate::warning(engine, s->m_expression->error(engine));
392 s->m_isEvaluating = false;
395 ////////////////////////////////////////////////////////////////////////
397 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(QQmlBoundSignalExpression *o)
403 QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(const QQmlBoundSignalExpressionPointer &other)
409 QQmlBoundSignalExpressionPointer::~QQmlBoundSignalExpressionPointer()
414 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(const QQmlBoundSignalExpressionPointer &other)
416 if (other.o) other.o->addref();
422 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(QQmlBoundSignalExpression *other)
424 if (other) other->addref();
431 Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership
432 of the callers reference of other.
434 QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::take(QQmlBoundSignalExpression *other)