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),
67 QQmlExpressionPrivate::~QQmlExpressionPrivate()
69 qPersistentDispose(v8qmlscope);
70 qPersistentDispose(v8function);
73 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me)
77 QQmlAbstractExpression::setContext(ctxt);
79 expressionFunctionValid = false;
80 expressionFunctionRewritten = false;
83 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr,
84 bool isRewritten, QObject *me, const QString &srcUrl,
85 int lineNumber, int columnNumber)
89 column = columnNumber;
93 expressionFunctionValid = false;
94 expressionFunctionRewritten = isRewritten;
96 QQmlAbstractExpression::setContext(ctxt);
100 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QByteArray &expr,
101 bool isRewritten, QObject *me, const QString &srcUrl,
102 int lineNumber, int columnNumber)
106 column = columnNumber;
109 expressionFunctionValid = true;
110 expressionFunctionRewritten = true;
111 v8function = evalFunction(ctxt, me, expr.constData(), expr.length(),
112 srcUrl, lineNumber, &v8qmlscope);
113 setUseSharedContext(false);
115 expressionUtf8 = expr;
117 expression = QString::fromUtf8(expr);
119 expressionFunctionValid = false;
120 expressionFunctionRewritten = isRewritten;
123 QQmlAbstractExpression::setContext(ctxt);
128 QQmlExpressionPrivate::create(QQmlContextData *ctxt, QObject *object,
129 const QString &expr, bool isRewritten,
130 const QString &url, int lineNumber, int columnNumber)
132 return new QQmlExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber,
133 *new QQmlExpressionPrivate);
137 \class QQmlExpression
140 \brief The QQmlExpression class evaluates JavaScript in a QML context.
142 For example, given a file \c main.qml like this:
148 width: 200; height: 200
152 The following code evaluates a JavaScript expression in the context of the
156 QQmlEngine *engine = new QQmlEngine;
157 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
159 QObject *myObject = component.create();
160 QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2");
161 int result = expr->evaluate().toInt(); // result = 400
164 Note that the QtQuick 1 version is called QDeclarativeExpression.
168 Create an invalid QQmlExpression.
170 As the expression will not have an associated QQmlContext, this will be a
171 null expression object and its value will always be an invalid QVariant.
173 QQmlExpression::QQmlExpression()
174 : QObject(*new QQmlExpressionPrivate, 0)
179 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
180 QObject *object, const QString &expr, bool isRewritten,
181 const QString &url, int lineNumber, int columnNumber,
182 QQmlExpressionPrivate &dd)
186 d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
190 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
191 QObject *object, const QByteArray &expr,
193 const QString &url, int lineNumber, int columnNumber,
194 QQmlExpressionPrivate &dd)
198 d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
202 Create a QQmlExpression object that is a child of \a parent.
204 The \a script provides the expression to be evaluated, the context to evaluate it in,
205 and the scope object to evaluate it with.
207 This constructor is functionally equivalent to the following, but in most cases
210 QQmlExpression expression(script.context(), script.scopeObject(), script.script(), parent);
215 QQmlExpression::QQmlExpression(const QQmlScriptString &script, QObject *parent)
216 : QObject(*new QQmlExpressionPrivate, parent)
220 if (!script.context()->isValid())
223 bool defaultConstruction = false;
225 int id = script.d.data()->bindingId;
227 defaultConstruction = true;
229 QQmlContextData *ctxtdata = QQmlContextData::get(script.context());
231 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(script.context()->engine());
232 QQmlCompiledData *cdata = 0;
233 QQmlTypeData *typeData = 0;
234 if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
235 typeData = engine->typeLoader.get(ctxtdata->url);
236 cdata = typeData->compiledData();
240 d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(),
241 cdata->name, script.d.data()->lineNumber, script.d.data()->columnNumber);
243 defaultConstruction = true;
251 if (defaultConstruction)
252 d->init(QQmlContextData::get(script.context()), script.script(), script.scopeObject());
256 Create a QQmlExpression object that is a child of \a parent.
258 The \a expression JavaScript will be executed in the \a ctxt QQmlContext.
259 If specified, the \a scope object's properties will also be in scope during
260 the expression's execution.
262 QQmlExpression::QQmlExpression(QQmlContext *ctxt,
264 const QString &expression,
266 : QObject(*new QQmlExpressionPrivate, parent)
269 d->init(QQmlContextData::get(ctxt), expression, scope);
275 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
276 const QString &expression)
277 : QObject(*new QQmlExpressionPrivate, 0)
280 d->init(ctxt, expression, scope);
284 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
285 const QString &expression, QQmlExpressionPrivate &dd)
289 d->init(ctxt, expression, scope);
293 Destroy the QQmlExpression instance.
295 QQmlExpression::~QQmlExpression()
300 Returns the QQmlEngine this expression is associated with, or 0 if there
301 is no association or the QQmlEngine has been destroyed.
303 QQmlEngine *QQmlExpression::engine() const
305 Q_D(const QQmlExpression);
306 return d->context()?d->context()->engine:0;
310 Returns the QQmlContext this expression is associated with, or 0 if there
311 is no association or the QQmlContext has been destroyed.
313 QQmlContext *QQmlExpression::context() const
315 Q_D(const QQmlExpression);
316 QQmlContextData *data = d->context();
317 return data?data->asQQmlContext():0;
321 Returns the expression string.
323 QString QQmlExpression::expression() const
325 Q_D(const QQmlExpression);
326 if (!d->expressionUtf8.isEmpty()) {
327 return QString::fromUtf8(d->expressionUtf8);
329 return d->expression;
334 Set the expression to \a expression.
336 void QQmlExpression::setExpression(const QString &expression)
340 d->resetNotifyOnValueChanged();
341 d->expression = expression;
342 d->expressionUtf8.clear();
343 d->expressionFunctionValid = false;
344 d->expressionFunctionRewritten = false;
345 qPersistentDispose(d->v8function);
346 qPersistentDispose(d->v8qmlscope);
349 // Must be called with a valid handle scope
350 v8::Local<v8::Value> QQmlExpressionPrivate::v8value(bool *isUndefined)
352 if (!expressionFunctionValid) {
355 QQmlRewrite::RewriteBinding rewriteBinding;
356 rewriteBinding.setName(name);
358 if (expressionFunctionRewritten)
361 code = rewriteBinding(expression, &ok);
363 if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
364 setUseSharedContext(false);
365 expressionFunctionValid = true;
368 return evaluate(context(), v8function, isUndefined);
371 QVariant QQmlExpressionPrivate::value(bool *isUndefined)
375 if (!context() || !context()->isValid()) {
376 qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
380 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine());
383 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
386 v8::HandleScope handle_scope;
387 v8::Context::Scope context_scope(ep->v8engine()->context());
388 v8::Local<v8::Value> result = v8value(isUndefined);
389 rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
392 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
398 Evaulates the expression, returning the result of the evaluation,
399 or an invalid QVariant if the expression is invalid or has an error.
401 \a valueIsUndefined is set to true if the expression resulted in an
404 \sa hasError(), error()
406 QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
409 return d->value(valueIsUndefined);
413 Returns true if the valueChanged() signal is emitted when the expression's evaluated
416 bool QQmlExpression::notifyOnValueChanged() const
418 Q_D(const QQmlExpression);
419 return d->notifyOnValueChanged();
423 Sets whether the valueChanged() signal is emitted when the
424 expression's evaluated value changes.
426 If \a notifyOnChange is true, the QQmlExpression will
427 monitor properties involved in the expression's evaluation, and emit
428 QQmlExpression::valueChanged() if they have changed. This
429 allows an application to ensure that any value associated with the
430 result of the expression remains up to date.
432 If \a notifyOnChange is false (default), the QQmlExpression
433 will not montitor properties involved in the expression's
434 evaluation, and QQmlExpression::valueChanged() will never be
435 emitted. This is more efficient if an application wants a "one off"
436 evaluation of the expression.
438 void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
441 d->setNotifyOnValueChanged(notifyOnChange);
445 Returns the source file URL for this expression. The source location must
446 have been previously set by calling setSourceLocation().
448 QString QQmlExpression::sourceFile() const
450 Q_D(const QQmlExpression);
455 Returns the source file line number for this expression. The source location
456 must have been previously set by calling setSourceLocation().
458 int QQmlExpression::lineNumber() const
460 Q_D(const QQmlExpression);
465 Returns the source file column number for this expression. The source location
466 must have been previously set by calling setSourceLocation().
468 int QQmlExpression::columnNumber() const
470 Q_D(const QQmlExpression);
475 Set the location of this expression to \a line of \a url. This information
476 is used by the script engine.
478 void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
487 Returns the expression's scope object, if provided, otherwise 0.
489 In addition to data provided by the expression's QQmlContext, the scope
490 object's properties are also in scope during the expression's evaluation.
492 QObject *QQmlExpression::scopeObject() const
494 Q_D(const QQmlExpression);
495 return d->scopeObject();
499 Returns true if the last call to evaluate() resulted in an error,
502 \sa error(), clearError()
504 bool QQmlExpression::hasError() const
506 Q_D(const QQmlExpression);
507 return d->hasError();
511 Clear any expression errors. Calls to hasError() following this will
514 \sa hasError(), error()
516 void QQmlExpression::clearError()
523 Return any error from the last call to evaluate(). If there was no error,
524 this returns an invalid QQmlError instance.
526 \sa hasError(), clearError()
529 QQmlError QQmlExpression::error() const
531 Q_D(const QQmlExpression);
536 \fn void QQmlExpression::valueChanged()
538 Emitted each time the expression value changes from the last time it was
539 evaluated. The expression must have been evaluated at least once (by
540 calling QQmlExpression::evaluate()) before this signal will be emitted.
543 void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e)
545 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
546 This->expressionChanged();
549 void QQmlExpressionPrivate::expressionChanged()
552 emit q->valueChanged();
555 QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e)
557 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
558 return QLatin1String("\"") + This->expression + QLatin1String("\"");
563 #include <moc_qqmlexpression.cpp>