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 = true;
225 int id = script.d.data()->bindingId;
227 QQmlContextData *ctxtdata = QQmlContextData::get(script.context());
228 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(script.context()->engine());
229 if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
230 QQmlTypeData *typeData = engine->typeLoader.getType(ctxtdata->url);
233 if (QQmlCompiledData *cdata = typeData->compiledData()) {
234 defaultConstruction = false;
235 d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(),
236 cdata->name, script.d.data()->lineNumber, script.d.data()->columnNumber);
243 if (defaultConstruction)
244 d->init(QQmlContextData::get(script.context()), script.script(), script.scopeObject());
248 Create a QQmlExpression object that is a child of \a parent.
250 The \a expression JavaScript will be executed in the \a ctxt QQmlContext.
251 If specified, the \a scope object's properties will also be in scope during
252 the expression's execution.
254 QQmlExpression::QQmlExpression(QQmlContext *ctxt,
256 const QString &expression,
258 : QObject(*new QQmlExpressionPrivate, parent)
261 d->init(QQmlContextData::get(ctxt), expression, scope);
267 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
268 const QString &expression)
269 : QObject(*new QQmlExpressionPrivate, 0)
272 d->init(ctxt, expression, scope);
276 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
277 const QString &expression, QQmlExpressionPrivate &dd)
281 d->init(ctxt, expression, scope);
285 Destroy the QQmlExpression instance.
287 QQmlExpression::~QQmlExpression()
292 Returns the QQmlEngine this expression is associated with, or 0 if there
293 is no association or the QQmlEngine has been destroyed.
295 QQmlEngine *QQmlExpression::engine() const
297 Q_D(const QQmlExpression);
298 return d->context()?d->context()->engine:0;
302 Returns the QQmlContext this expression is associated with, or 0 if there
303 is no association or the QQmlContext has been destroyed.
305 QQmlContext *QQmlExpression::context() const
307 Q_D(const QQmlExpression);
308 QQmlContextData *data = d->context();
309 return data?data->asQQmlContext():0;
313 Returns the expression string.
315 QString QQmlExpression::expression() const
317 Q_D(const QQmlExpression);
318 if (!d->expressionUtf8.isEmpty()) {
319 return QString::fromUtf8(d->expressionUtf8);
321 return d->expression;
326 Set the expression to \a expression.
328 void QQmlExpression::setExpression(const QString &expression)
332 d->resetNotifyOnValueChanged();
333 d->expression = expression;
334 d->expressionUtf8.clear();
335 d->expressionFunctionValid = false;
336 d->expressionFunctionRewritten = false;
337 qPersistentDispose(d->v8function);
338 qPersistentDispose(d->v8qmlscope);
341 // Must be called with a valid handle scope
342 v8::Local<v8::Value> QQmlExpressionPrivate::v8value(bool *isUndefined)
344 if (!expressionFunctionValid) {
347 QQmlRewrite::RewriteBinding rewriteBinding;
348 rewriteBinding.setName(name);
350 if (expressionFunctionRewritten)
353 code = rewriteBinding(expression, &ok);
355 if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
356 setUseSharedContext(false);
357 expressionFunctionValid = true;
360 return evaluate(context(), v8function, isUndefined);
363 QVariant QQmlExpressionPrivate::value(bool *isUndefined)
367 if (!context() || !context()->isValid()) {
368 qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
372 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine());
375 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
378 v8::HandleScope handle_scope;
379 v8::Context::Scope context_scope(ep->v8engine()->context());
380 v8::Local<v8::Value> result = v8value(isUndefined);
381 rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
384 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
390 Evaulates the expression, returning the result of the evaluation,
391 or an invalid QVariant if the expression is invalid or has an error.
393 \a valueIsUndefined is set to true if the expression resulted in an
396 \sa hasError(), error()
398 QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
401 return d->value(valueIsUndefined);
405 Returns true if the valueChanged() signal is emitted when the expression's evaluated
408 bool QQmlExpression::notifyOnValueChanged() const
410 Q_D(const QQmlExpression);
411 return d->notifyOnValueChanged();
415 Sets whether the valueChanged() signal is emitted when the
416 expression's evaluated value changes.
418 If \a notifyOnChange is true, the QQmlExpression will
419 monitor properties involved in the expression's evaluation, and emit
420 QQmlExpression::valueChanged() if they have changed. This
421 allows an application to ensure that any value associated with the
422 result of the expression remains up to date.
424 If \a notifyOnChange is false (default), the QQmlExpression
425 will not montitor properties involved in the expression's
426 evaluation, and QQmlExpression::valueChanged() will never be
427 emitted. This is more efficient if an application wants a "one off"
428 evaluation of the expression.
430 void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
433 d->setNotifyOnValueChanged(notifyOnChange);
437 Returns the source file URL for this expression. The source location must
438 have been previously set by calling setSourceLocation().
440 QString QQmlExpression::sourceFile() const
442 Q_D(const QQmlExpression);
447 Returns the source file line number for this expression. The source location
448 must have been previously set by calling setSourceLocation().
450 int QQmlExpression::lineNumber() const
452 Q_D(const QQmlExpression);
457 Returns the source file column number for this expression. The source location
458 must have been previously set by calling setSourceLocation().
460 int QQmlExpression::columnNumber() const
462 Q_D(const QQmlExpression);
467 Set the location of this expression to \a line of \a url. This information
468 is used by the script engine.
470 void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
479 Returns the expression's scope object, if provided, otherwise 0.
481 In addition to data provided by the expression's QQmlContext, the scope
482 object's properties are also in scope during the expression's evaluation.
484 QObject *QQmlExpression::scopeObject() const
486 Q_D(const QQmlExpression);
487 return d->scopeObject();
491 Returns true if the last call to evaluate() resulted in an error,
494 \sa error(), clearError()
496 bool QQmlExpression::hasError() const
498 Q_D(const QQmlExpression);
499 return d->hasError();
503 Clear any expression errors. Calls to hasError() following this will
506 \sa hasError(), error()
508 void QQmlExpression::clearError()
515 Return any error from the last call to evaluate(). If there was no error,
516 this returns an invalid QQmlError instance.
518 \sa hasError(), clearError()
521 QQmlError QQmlExpression::error() const
523 Q_D(const QQmlExpression);
524 return d->error(engine());
528 \fn void QQmlExpression::valueChanged()
530 Emitted each time the expression value changes from the last time it was
531 evaluated. The expression must have been evaluated at least once (by
532 calling QQmlExpression::evaluate()) before this signal will be emitted.
535 void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e)
537 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
538 This->expressionChanged();
541 void QQmlExpressionPrivate::expressionChanged()
544 emit q->valueChanged();
547 QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e)
549 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
550 return QLatin1Char('"') + This->expression + QLatin1Char('"');
555 #include <moc_qqmlexpression.cpp>