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 "qqmlexpression.h"
43 #include "qqmlexpression_p.h"
45 #include "qqmlglobal_p.h"
46 #include "qqmlengine_p.h"
47 #include "qqmlcontext_p.h"
48 #include "qqmlrewrite_p.h"
49 #include "qqmlscriptstring_p.h"
50 #include "qqmlcompiler_p.h"
52 #include <QtCore/qdebug.h>
56 static QQmlJavaScriptExpression::VTable QQmlExpressionPrivate_jsvtable = {
57 QQmlExpressionPrivate::expressionIdentifier,
58 QQmlExpressionPrivate::expressionChanged
61 QQmlExpressionPrivate::QQmlExpressionPrivate()
62 : QQmlJavaScriptExpression(&QQmlExpressionPrivate_jsvtable),
63 expressionFunctionValid(true), expressionFunctionRewritten(false),
68 QQmlExpressionPrivate::~QQmlExpressionPrivate()
70 qPersistentDispose(v8qmlscope);
71 qPersistentDispose(v8function);
74 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me)
78 QQmlAbstractExpression::setContext(ctxt);
80 expressionFunctionValid = false;
81 expressionFunctionRewritten = false;
84 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr,
85 bool isRewritten, QObject *me, const QString &srcUrl,
86 quint16 lineNumber, quint16 columnNumber)
90 column = columnNumber;
94 expressionFunctionValid = false;
95 expressionFunctionRewritten = isRewritten;
97 QQmlAbstractExpression::setContext(ctxt);
101 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QByteArray &expr,
102 bool isRewritten, QObject *me, const QString &srcUrl,
103 quint16 lineNumber, quint16 columnNumber)
107 column = columnNumber;
110 expressionFunctionValid = true;
111 expressionFunctionRewritten = true;
112 v8function = evalFunction(ctxt, me, expr.constData(), expr.length(),
113 srcUrl, lineNumber, &v8qmlscope);
114 setUseSharedContext(false);
116 expressionUtf8 = expr;
118 expression = QString::fromUtf8(expr);
120 expressionFunctionValid = false;
121 expressionFunctionRewritten = isRewritten;
124 QQmlAbstractExpression::setContext(ctxt);
129 QQmlExpressionPrivate::create(QQmlContextData *ctxt, QObject *object,
130 const QString &expr, bool isRewritten,
131 const QString &url, quint16 lineNumber, quint16 columnNumber)
133 return new QQmlExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber,
134 *new QQmlExpressionPrivate);
138 \class QQmlExpression
141 \brief The QQmlExpression class evaluates JavaScript in a QML context.
143 For example, given a file \c main.qml like this:
149 width: 200; height: 200
153 The following code evaluates a JavaScript expression in the context of the
157 QQmlEngine *engine = new QQmlEngine;
158 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
160 QObject *myObject = component.create();
161 QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2");
162 int result = expr->evaluate().toInt(); // result = 400
165 Note that the QtQuick 1 version is called QDeclarativeExpression.
169 Create an invalid QQmlExpression.
171 As the expression will not have an associated QQmlContext, this will be a
172 null expression object and its value will always be an invalid QVariant.
174 QQmlExpression::QQmlExpression()
175 : QObject(*new QQmlExpressionPrivate, 0)
180 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
181 QObject *object, const QString &expr, bool isRewritten,
182 const QString &url, int lineNumber, int columnNumber,
183 QQmlExpressionPrivate &dd)
187 d->init(ctxt, expr, isRewritten, object, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
191 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
192 QObject *object, const QByteArray &expr,
194 const QString &url, int lineNumber, int columnNumber,
195 QQmlExpressionPrivate &dd)
199 d->init(ctxt, expr, isRewritten, object, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
203 Create a QQmlExpression object that is a child of \a parent.
205 The \a script provides the expression to be evaluated, the context to evaluate it in,
206 and the scope object to evaluate it with. If provided, \a ctxt and \a scope will override
207 the context and scope object provided by \a script.
211 QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt, QObject *scope, QObject *parent)
212 : QObject(*new QQmlExpressionPrivate, parent)
215 if (ctxt && !ctxt->isValid())
218 const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
219 if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
222 bool defaultConstruction = true;
223 QQmlContextData *evalCtxtData = QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context);
224 QObject *scopeObject = scope ? scope : scriptPrivate->scope;
226 int id = scriptPrivate->bindingId;
228 QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context);
229 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
230 if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
231 QQmlTypeData *typeData = engine->typeLoader.getType(ctxtdata->url);
234 if (QQmlCompiledData *cdata = typeData->compiledData()) {
235 defaultConstruction = false;
236 d->init(evalCtxtData, cdata->primitives.at(id), true, scopeObject,
237 cdata->name, scriptPrivate->lineNumber, scriptPrivate->columnNumber);
244 if (defaultConstruction)
245 d->init(evalCtxtData, scriptPrivate->script, scopeObject);
249 Create a QQmlExpression object that is a child of \a parent.
251 The \a expression JavaScript will be executed in the \a ctxt QQmlContext.
252 If specified, the \a scope object's properties will also be in scope during
253 the expression's execution.
255 QQmlExpression::QQmlExpression(QQmlContext *ctxt,
257 const QString &expression,
259 : QObject(*new QQmlExpressionPrivate, parent)
262 d->init(QQmlContextData::get(ctxt), expression, scope);
268 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
269 const QString &expression)
270 : QObject(*new QQmlExpressionPrivate, 0)
273 d->init(ctxt, expression, scope);
277 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
278 const QString &expression, QQmlExpressionPrivate &dd)
282 d->init(ctxt, expression, scope);
286 Destroy the QQmlExpression instance.
288 QQmlExpression::~QQmlExpression()
293 Returns the QQmlEngine this expression is associated with, or 0 if there
294 is no association or the QQmlEngine has been destroyed.
296 QQmlEngine *QQmlExpression::engine() const
298 Q_D(const QQmlExpression);
299 return d->context()?d->context()->engine:0;
303 Returns the QQmlContext this expression is associated with, or 0 if there
304 is no association or the QQmlContext has been destroyed.
306 QQmlContext *QQmlExpression::context() const
308 Q_D(const QQmlExpression);
309 QQmlContextData *data = d->context();
310 return data?data->asQQmlContext():0;
314 Returns the expression string.
316 QString QQmlExpression::expression() const
318 Q_D(const QQmlExpression);
319 if (!d->expressionUtf8.isEmpty()) {
320 return QString::fromUtf8(d->expressionUtf8);
322 return d->expression;
327 Set the expression to \a expression.
329 void QQmlExpression::setExpression(const QString &expression)
333 d->resetNotifyOnValueChanged();
334 d->expression = expression;
335 d->expressionUtf8.clear();
336 d->expressionFunctionValid = false;
337 d->expressionFunctionRewritten = false;
338 qPersistentDispose(d->v8function);
339 qPersistentDispose(d->v8qmlscope);
342 // Must be called with a valid handle scope
343 v8::Local<v8::Value> QQmlExpressionPrivate::v8value(bool *isUndefined)
345 if (!expressionFunctionValid) {
348 QQmlRewrite::RewriteBinding rewriteBinding;
349 rewriteBinding.setName(name);
351 if (expressionFunctionRewritten)
354 code = rewriteBinding(expression, &ok);
356 if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
357 setUseSharedContext(false);
358 expressionFunctionValid = true;
361 return evaluate(context(), v8function, isUndefined);
364 QVariant QQmlExpressionPrivate::value(bool *isUndefined)
368 if (!context() || !context()->isValid()) {
369 qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
373 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine());
376 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
379 v8::HandleScope handle_scope;
380 v8::Context::Scope context_scope(ep->v8engine()->context());
381 v8::Local<v8::Value> result = v8value(isUndefined);
382 rv = ep->v8engine()->toVariant(result, -1);
385 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
391 Evaulates the expression, returning the result of the evaluation,
392 or an invalid QVariant if the expression is invalid or has an error.
394 \a valueIsUndefined is set to true if the expression resulted in an
397 \sa hasError(), error()
399 QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
402 return d->value(valueIsUndefined);
406 Returns true if the valueChanged() signal is emitted when the expression's evaluated
409 bool QQmlExpression::notifyOnValueChanged() const
411 Q_D(const QQmlExpression);
412 return d->notifyOnValueChanged();
416 Sets whether the valueChanged() signal is emitted when the
417 expression's evaluated value changes.
419 If \a notifyOnChange is true, the QQmlExpression will
420 monitor properties involved in the expression's evaluation, and emit
421 QQmlExpression::valueChanged() if they have changed. This
422 allows an application to ensure that any value associated with the
423 result of the expression remains up to date.
425 If \a notifyOnChange is false (default), the QQmlExpression
426 will not montitor properties involved in the expression's
427 evaluation, and QQmlExpression::valueChanged() will never be
428 emitted. This is more efficient if an application wants a "one off"
429 evaluation of the expression.
431 void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
434 d->setNotifyOnValueChanged(notifyOnChange);
438 Returns the source file URL for this expression. The source location must
439 have been previously set by calling setSourceLocation().
441 QString QQmlExpression::sourceFile() const
443 Q_D(const QQmlExpression);
448 Returns the source file line number for this expression. The source location
449 must have been previously set by calling setSourceLocation().
451 int QQmlExpression::lineNumber() const
453 Q_D(const QQmlExpression);
454 return qmlSourceCoordinate(d->line);
458 Returns the source file column number for this expression. The source location
459 must have been previously set by calling setSourceLocation().
461 int QQmlExpression::columnNumber() const
463 Q_D(const QQmlExpression);
464 return qmlSourceCoordinate(d->column);
468 Set the location of this expression to \a line of \a url. This information
469 is used by the script engine.
471 void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
475 d->line = qmlSourceCoordinate(line);
476 d->column = qmlSourceCoordinate(column);
480 Returns the expression's scope object, if provided, otherwise 0.
482 In addition to data provided by the expression's QQmlContext, the scope
483 object's properties are also in scope during the expression's evaluation.
485 QObject *QQmlExpression::scopeObject() const
487 Q_D(const QQmlExpression);
488 return d->scopeObject();
492 Returns true if the last call to evaluate() resulted in an error,
495 \sa error(), clearError()
497 bool QQmlExpression::hasError() const
499 Q_D(const QQmlExpression);
500 return d->hasError();
504 Clear any expression errors. Calls to hasError() following this will
507 \sa hasError(), error()
509 void QQmlExpression::clearError()
516 Return any error from the last call to evaluate(). If there was no error,
517 this returns an invalid QQmlError instance.
519 \sa hasError(), clearError()
522 QQmlError QQmlExpression::error() const
524 Q_D(const QQmlExpression);
525 return d->error(engine());
529 \fn void QQmlExpression::valueChanged()
531 Emitted each time the expression value changes from the last time it was
532 evaluated. The expression must have been evaluated at least once (by
533 calling QQmlExpression::evaluate()) before this signal will be emitted.
536 void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e)
538 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
539 This->expressionChanged();
542 void QQmlExpressionPrivate::expressionChanged()
545 emit q->valueChanged();
548 QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e)
550 QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
551 return QLatin1Char('"') + This->expression + QLatin1Char('"');
556 #include <moc_qqmlexpression.cpp>