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 "qqmlexpression.h"
43 #include "qqmlexpression_p.h"
45 #include "qqmlengine_p.h"
46 #include "qqmlcontext_p.h"
47 #include "qqmlrewrite_p.h"
48 #include "qqmlscriptstring_p.h"
49 #include "qqmlcompiler_p.h"
51 #include <QtCore/qdebug.h>
55 static QQmlJavaScriptExpression::VTable QQmlExpressionPrivate_jsvtable = {
56 QQmlExpressionPrivate::expressionIdentifier,
57 QQmlExpressionPrivate::expressionChanged
60 QQmlExpressionPrivate::QQmlExpressionPrivate()
61 : QQmlJavaScriptExpression(&QQmlExpressionPrivate_jsvtable),
62 expressionFunctionValid(true), expressionFunctionRewritten(false),
63 extractExpressionFromFunction(false), line(-1), dataRef(0)
67 QQmlExpressionPrivate::~QQmlExpressionPrivate()
69 qPersistentDispose(v8qmlscope);
70 qPersistentDispose(v8function);
71 if (dataRef) dataRef->release();
75 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me)
79 QQmlAbstractExpression::setContext(ctxt);
81 expressionFunctionValid = false;
82 expressionFunctionRewritten = false;
85 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr,
86 bool isRewritten, QObject *me, const QString &srcUrl,
87 int lineNumber, int columnNumber)
91 column = columnNumber;
95 expressionFunctionValid = false;
96 expressionFunctionRewritten = isRewritten;
98 QQmlAbstractExpression::setContext(ctxt);
102 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QByteArray &expr,
103 bool isRewritten, QObject *me, const QString &srcUrl,
104 int lineNumber, int columnNumber)
108 column = columnNumber;
111 expressionFunctionValid = true;
112 expressionFunctionRewritten = true;
113 v8function = evalFunction(ctxt, me, expr.constData(), expr.length(),
114 srcUrl, lineNumber, &v8qmlscope);
115 setUseSharedContext(false);
117 expressionUtf8 = expr;
119 expression = QString::fromUtf8(expr);
121 expressionFunctionValid = false;
122 expressionFunctionRewritten = isRewritten;
125 QQmlAbstractExpression::setContext(ctxt);
130 QQmlExpressionPrivate::create(QQmlContextData *ctxt, QObject *object,
131 const QString &expr, bool isRewritten,
132 const QString &url, int lineNumber, int columnNumber)
134 return new QQmlExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber,
135 *new QQmlExpressionPrivate);
139 \class QQmlExpression
142 \brief The QQmlExpression class evaluates JavaScript in a QML context.
144 For example, given a file \c main.qml like this:
150 width: 200; height: 200
154 The following code evaluates a JavaScript expression in the context of the
158 QQmlEngine *engine = new QQmlEngine;
159 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
161 QObject *myObject = component.create();
162 QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2");
163 int result = expr->evaluate().toInt(); // result = 400
166 Note that the QtQuick 1 version is called QDeclarativeExpression.
170 Create an invalid QQmlExpression.
172 As the expression will not have an associated QQmlContext, this will be a
173 null expression object and its value will always be an invalid QVariant.
175 QQmlExpression::QQmlExpression()
176 : QObject(*new QQmlExpressionPrivate, 0)
181 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
182 QObject *object, const QString &expr, bool isRewritten,
183 const QString &url, int lineNumber, int columnNumber,
184 QQmlExpressionPrivate &dd)
188 d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
192 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
193 QObject *object, const QByteArray &expr,
195 const QString &url, int lineNumber, int columnNumber,
196 QQmlExpressionPrivate &dd)
200 d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
204 Create a QQmlExpression object that is a child of \a parent.
206 The \script provides the expression to be evaluated, the context to evaluate it in,
207 and the scope object to evaluate it with.
209 This constructor is functionally equivalent to the following, but in most cases
212 QQmlExpression expression(script.context(), script.scopeObject(), script.script(), parent);
217 QQmlExpression::QQmlExpression(const QQmlScriptString &script, QObject *parent)
218 : QObject(*new QQmlExpressionPrivate, parent)
221 bool defaultConstruction = false;
223 int id = script.d.data()->bindingId;
225 defaultConstruction = true;
227 QQmlContextData *ctxtdata = QQmlContextData::get(script.context());
229 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(script.context()->engine());
230 QQmlCompiledData *cdata = 0;
231 QQmlTypeData *typeData = 0;
232 if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
233 typeData = engine->typeLoader.get(ctxtdata->url);
234 cdata = typeData->compiledData();
238 d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(),
239 cdata->name, script.d.data()->lineNumber, script.d.data()->columnNumber);
241 defaultConstruction = true;
249 if (defaultConstruction)
250 d->init(QQmlContextData::get(script.context()), script.script(), script.scopeObject());
254 Create a QQmlExpression object that is a child of \a parent.
256 The \a expression JavaScript will be executed in the \a ctxt QQmlContext.
257 If specified, the \a scope object's properties will also be in scope during
258 the expression's execution.
260 QQmlExpression::QQmlExpression(QQmlContext *ctxt,
262 const QString &expression,
264 : QObject(*new QQmlExpressionPrivate, parent)
267 d->init(QQmlContextData::get(ctxt), expression, scope);
273 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
274 const QString &expression)
275 : QObject(*new QQmlExpressionPrivate, 0)
278 d->init(ctxt, expression, scope);
282 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
283 const QString &expression, QQmlExpressionPrivate &dd)
287 d->init(ctxt, expression, scope);
291 Destroy the QQmlExpression instance.
293 QQmlExpression::~QQmlExpression()
298 Returns the QQmlEngine this expression is associated with, or 0 if there
299 is no association or the QQmlEngine has been destroyed.
301 QQmlEngine *QQmlExpression::engine() const
303 Q_D(const QQmlExpression);
304 return d->context()?d->context()->engine:0;
308 Returns the QQmlContext this expression is associated with, or 0 if there
309 is no association or the QQmlContext has been destroyed.
311 QQmlContext *QQmlExpression::context() const
313 Q_D(const QQmlExpression);
314 QQmlContextData *data = d->context();
315 return data?data->asQQmlContext():0;
319 Returns the expression string.
321 QString QQmlExpression::expression() const
323 Q_D(const QQmlExpression);
324 if (d->extractExpressionFromFunction && context()->engine()) {
325 QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(context()->engine());
326 v8::HandleScope handle_scope;
327 v8::Context::Scope scope(v8engine->context());
329 return v8engine->toString(v8::Handle<v8::Value>(d->v8function));
330 } else if (!d->expressionUtf8.isEmpty()) {
331 return QString::fromUtf8(d->expressionUtf8);
333 return d->expression;
338 Set the expression to \a expression.
340 void QQmlExpression::setExpression(const QString &expression)
344 d->resetNotifyOnValueChanged();
345 d->expression = expression;
346 d->expressionUtf8.clear();
347 d->expressionFunctionValid = false;
348 d->expressionFunctionRewritten = false;
349 qPersistentDispose(d->v8function);
350 qPersistentDispose(d->v8qmlscope);
353 // Must be called with a valid handle scope
354 v8::Local<v8::Value> QQmlExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined)
356 if (!expressionFunctionValid) {
359 QQmlRewrite::RewriteBinding rewriteBinding;
360 rewriteBinding.setName(name);
362 if (expressionFunctionRewritten)
365 code = rewriteBinding(expression, &ok);
367 if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
368 setUseSharedContext(false);
369 expressionFunctionValid = true;
373 if (secondaryScope) {
374 v8::Local<v8::Value> result;
375 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
376 QObject *restoreSecondaryScope = 0;
377 restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope);
378 result = evaluate(context(), v8function, isUndefined);
379 ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope);
382 return evaluate(context(), v8function, isUndefined);
386 QVariant QQmlExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined)
390 if (!context() || !context()->isValid()) {
391 qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
395 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine());
398 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
401 v8::HandleScope handle_scope;
402 v8::Context::Scope context_scope(ep->v8engine()->context());
403 v8::Local<v8::Value> result = v8value(secondaryScope, isUndefined);
404 rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
407 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
413 Evaulates the expression, returning the result of the evaluation,
414 or an invalid QVariant if the expression is invalid or has an error.
416 \a valueIsUndefined is set to true if the expression resulted in an
419 \sa hasError(), error()
421 QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
424 return d->value(0, valueIsUndefined);
428 Returns true if the valueChanged() signal is emitted when the expression's evaluated
431 bool QQmlExpression::notifyOnValueChanged() const
433 Q_D(const QQmlExpression);
434 return d->notifyOnValueChanged();
438 Sets whether the valueChanged() signal is emitted when the
439 expression's evaluated value changes.
441 If \a notifyOnChange is true, the QQmlExpression will
442 monitor properties involved in the expression's evaluation, and emit
443 QQmlExpression::valueChanged() if they have changed. This
444 allows an application to ensure that any value associated with the
445 result of the expression remains up to date.
447 If \a notifyOnChange is false (default), the QQmlExpression
448 will not montitor properties involved in the expression's
449 evaluation, and QQmlExpression::valueChanged() will never be
450 emitted. This is more efficient if an application wants a "one off"
451 evaluation of the expression.
453 void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
456 d->setNotifyOnValueChanged(notifyOnChange);
460 Returns the source file URL for this expression. The source location must
461 have been previously set by calling setSourceLocation().
463 QString QQmlExpression::sourceFile() const
465 Q_D(const QQmlExpression);
470 Returns the source file line number for this expression. The source location
471 must have been previously set by calling setSourceLocation().
473 int QQmlExpression::lineNumber() const
475 Q_D(const QQmlExpression);
480 Returns the source file column number for this expression. The source location
481 must have been previously set by calling setSourceLocation().
483 int QQmlExpression::columnNumber() const
485 Q_D(const QQmlExpression);
490 Set the location of this expression to \a line of \a url. This information
491 is used by the script engine.
493 void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
502 Returns the expression's scope object, if provided, otherwise 0.
504 In addition to data provided by the expression's QQmlContext, the scope
505 object's properties are also in scope during the expression's evaluation.
507 QObject *QQmlExpression::scopeObject() const
509 Q_D(const QQmlExpression);
510 return d->scopeObject();
514 Returns true if the last call to evaluate() resulted in an error,
517 \sa error(), clearError()
519 bool QQmlExpression::hasError() const
521 Q_D(const QQmlExpression);
522 return d->hasError();
526 Clear any expression errors. Calls to hasError() following this will
529 \sa hasError(), error()
531 void QQmlExpression::clearError()
538 Return any error from the last call to evaluate(). If there was no error,
539 this returns an invalid QQmlError instance.
541 \sa hasError(), clearError()
544 QQmlError QQmlExpression::error() const
546 Q_D(const QQmlExpression);
551 \fn void QQmlExpression::valueChanged()
553 Emitted each time the expression value changes from the last time it was
554 evaluated. The expression must have been evaluated at least once (by
555 calling QQmlExpression::evaluate()) before this signal will be emitted.
558 void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e)
560 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
561 This->expressionChanged();
564 void QQmlExpressionPrivate::expressionChanged()
567 emit q->valueChanged();
570 QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e)
572 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
573 return QLatin1String("\"") + This->expression + QLatin1String("\"");
578 #include <moc_qqmlexpression.cpp>