Return earlier if script.context() is invalid
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlexpression.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlexpression.h"
43 #include "qqmlexpression_p.h"
44
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"
50
51 #include <QtCore/qdebug.h>
52
53 QT_BEGIN_NAMESPACE
54
55 static QQmlJavaScriptExpression::VTable QQmlExpressionPrivate_jsvtable = {
56     QQmlExpressionPrivate::expressionIdentifier,
57     QQmlExpressionPrivate::expressionChanged
58 };
59
60 QQmlExpressionPrivate::QQmlExpressionPrivate()
61 : QQmlJavaScriptExpression(&QQmlExpressionPrivate_jsvtable),
62   expressionFunctionValid(true), expressionFunctionRewritten(false),
63   line(-1)
64 {
65 }
66
67 QQmlExpressionPrivate::~QQmlExpressionPrivate()
68 {
69     qPersistentDispose(v8qmlscope);
70     qPersistentDispose(v8function);
71 }
72
73 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me)
74 {
75     expression = expr;
76
77     QQmlAbstractExpression::setContext(ctxt);
78     setScopeObject(me);
79     expressionFunctionValid = false;
80     expressionFunctionRewritten = false;
81 }
82
83 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr,
84                                  bool isRewritten, QObject *me, const QString &srcUrl,
85                                  int lineNumber, int columnNumber)
86 {
87     url = srcUrl;
88     line = lineNumber;
89     column = columnNumber;
90
91     expression = expr;
92
93     expressionFunctionValid = false;
94     expressionFunctionRewritten = isRewritten;
95
96     QQmlAbstractExpression::setContext(ctxt);
97     setScopeObject(me);
98 }
99
100 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QByteArray &expr,
101                                  bool isRewritten, QObject *me, const QString &srcUrl,
102                                  int lineNumber, int columnNumber)
103 {
104     url = srcUrl;
105     line = lineNumber;
106     column = columnNumber;
107
108     if (isRewritten) {
109         expressionFunctionValid = true;
110         expressionFunctionRewritten = true;
111         v8function = evalFunction(ctxt, me, expr.constData(), expr.length(),
112                                   srcUrl, lineNumber, &v8qmlscope);
113         setUseSharedContext(false);
114
115         expressionUtf8 = expr;
116     } else {
117         expression = QString::fromUtf8(expr);
118
119         expressionFunctionValid = false;
120         expressionFunctionRewritten = isRewritten;
121     }
122
123     QQmlAbstractExpression::setContext(ctxt);
124     setScopeObject(me);
125 }
126
127 QQmlExpression *
128 QQmlExpressionPrivate::create(QQmlContextData *ctxt, QObject *object,
129                               const QString &expr, bool isRewritten,
130                               const QString &url, int lineNumber, int columnNumber)
131 {
132     return new QQmlExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber,
133                               *new QQmlExpressionPrivate);
134 }
135
136 /*!
137     \class QQmlExpression
138     \since 5.0
139     \inmodule QtQml
140     \brief The QQmlExpression class evaluates JavaScript in a QML context.
141
142     For example, given a file \c main.qml like this:
143
144     \qml
145     import QtQuick 2.0
146
147     Item {
148         width: 200; height: 200
149     }
150     \endqml
151
152     The following code evaluates a JavaScript expression in the context of the
153     above QML:
154
155     \code
156     QQmlEngine *engine = new QQmlEngine;
157     QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
158
159     QObject *myObject = component.create();
160     QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2");
161     int result = expr->evaluate().toInt();  // result = 400
162     \endcode
163
164     Note that the QtQuick 1 version is called QDeclarativeExpression.
165 */
166
167 /*!
168     Create an invalid QQmlExpression.
169
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.
172  */
173 QQmlExpression::QQmlExpression()
174 : QObject(*new QQmlExpressionPrivate, 0)
175 {
176 }
177
178 /*!  \internal */
179 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
180                                                QObject *object, const QString &expr, bool isRewritten,
181                                                const QString &url, int lineNumber, int columnNumber,
182                                                QQmlExpressionPrivate &dd)
183 : QObject(dd, 0)
184 {
185     Q_D(QQmlExpression);
186     d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
187 }
188
189 /*!  \internal */
190 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
191                                                QObject *object, const QByteArray &expr,
192                                                bool isRewritten,
193                                                const QString &url, int lineNumber, int columnNumber,
194                                                QQmlExpressionPrivate &dd)
195 : QObject(dd, 0)
196 {
197     Q_D(QQmlExpression);
198     d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
199 }
200
201 /*!
202     Create a QQmlExpression object that is a child of \a parent.
203
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.
206
207     This constructor is functionally equivalent to the following, but in most cases
208     is more efficient.
209     \code
210     QQmlExpression expression(script.context(), script.scopeObject(), script.script(), parent);
211     \endcode
212
213     \sa QQmlScriptString
214 */
215 QQmlExpression::QQmlExpression(const QQmlScriptString &script, QObject *parent)
216 : QObject(*new QQmlExpressionPrivate, parent)
217 {
218     Q_D(QQmlExpression);
219
220     if (!script.context()->isValid())
221         return;
222
223     bool defaultConstruction = false;
224
225     int id = script.d.data()->bindingId;
226     if (id < 0) {
227         defaultConstruction = true;
228     } else {
229         QQmlContextData *ctxtdata = QQmlContextData::get(script.context());
230
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();
237         }
238
239         if (cdata)
240             d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(),
241                     cdata->name, script.d.data()->lineNumber, script.d.data()->columnNumber);
242         else
243            defaultConstruction = true;
244
245         if (cdata)
246             cdata->release();
247         if (typeData)
248             typeData->release();
249     }
250
251     if (defaultConstruction)
252         d->init(QQmlContextData::get(script.context()), script.script(), script.scopeObject());
253 }
254
255 /*!
256     Create a QQmlExpression object that is a child of \a parent.
257
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.
261 */
262 QQmlExpression::QQmlExpression(QQmlContext *ctxt,
263                                                QObject *scope,
264                                                const QString &expression,
265                                                QObject *parent)
266 : QObject(*new QQmlExpressionPrivate, parent)
267 {
268     Q_D(QQmlExpression);
269     d->init(QQmlContextData::get(ctxt), expression, scope);
270 }
271
272 /*!
273     \internal
274 */
275 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
276                                                const QString &expression)
277 : QObject(*new QQmlExpressionPrivate, 0)
278 {
279     Q_D(QQmlExpression);
280     d->init(ctxt, expression, scope);
281 }
282
283 /*!  \internal */
284 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
285                                                const QString &expression, QQmlExpressionPrivate &dd)
286 : QObject(dd, 0)
287 {
288     Q_D(QQmlExpression);
289     d->init(ctxt, expression, scope);
290 }
291
292 /*!
293     Destroy the QQmlExpression instance.
294 */
295 QQmlExpression::~QQmlExpression()
296 {
297 }
298
299 /*!
300     Returns the QQmlEngine this expression is associated with, or 0 if there
301     is no association or the QQmlEngine has been destroyed.
302 */
303 QQmlEngine *QQmlExpression::engine() const
304 {
305     Q_D(const QQmlExpression);
306     return d->context()?d->context()->engine:0;
307 }
308
309 /*!
310     Returns the QQmlContext this expression is associated with, or 0 if there
311     is no association or the QQmlContext has been destroyed.
312 */
313 QQmlContext *QQmlExpression::context() const
314 {
315     Q_D(const QQmlExpression);
316     QQmlContextData *data = d->context();
317     return data?data->asQQmlContext():0;
318 }
319
320 /*!
321     Returns the expression string.
322 */
323 QString QQmlExpression::expression() const
324 {
325     Q_D(const QQmlExpression);
326     if (!d->expressionUtf8.isEmpty()) {
327         return QString::fromUtf8(d->expressionUtf8);
328     } else {
329         return d->expression;
330     }
331 }
332
333 /*!
334     Set the expression to \a expression.
335 */
336 void QQmlExpression::setExpression(const QString &expression)
337 {
338     Q_D(QQmlExpression);
339
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);
347 }
348
349 // Must be called with a valid handle scope
350 v8::Local<v8::Value> QQmlExpressionPrivate::v8value(bool *isUndefined)
351 {
352     if (!expressionFunctionValid) {
353         bool ok = true;
354
355         QQmlRewrite::RewriteBinding rewriteBinding;
356         rewriteBinding.setName(name);
357         QString code;
358         if (expressionFunctionRewritten)
359             code = expression;
360         else
361             code = rewriteBinding(expression, &ok);
362
363         if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
364         setUseSharedContext(false);
365         expressionFunctionValid = true;
366     }
367
368     return evaluate(context(), v8function, isUndefined);
369 }
370
371 QVariant QQmlExpressionPrivate::value(bool *isUndefined)
372 {
373     Q_Q(QQmlExpression);
374
375     if (!context() || !context()->isValid()) {
376         qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
377         return QVariant();
378     }
379
380     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine());
381     QVariant rv;
382
383     ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
384
385     {
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*> >());
390     }
391
392     ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
393
394     return rv;
395 }
396
397 /*!
398     Evaulates the expression, returning the result of the evaluation,
399     or an invalid QVariant if the expression is invalid or has an error.
400
401     \a valueIsUndefined is set to true if the expression resulted in an
402     undefined value.
403
404     \sa hasError(), error()
405 */
406 QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
407 {
408     Q_D(QQmlExpression);
409     return d->value(valueIsUndefined);
410 }
411
412 /*!
413 Returns true if the valueChanged() signal is emitted when the expression's evaluated
414 value changes.
415 */
416 bool QQmlExpression::notifyOnValueChanged() const
417 {
418     Q_D(const QQmlExpression);
419     return d->notifyOnValueChanged();
420 }
421
422 /*!
423   Sets whether the valueChanged() signal is emitted when the
424   expression's evaluated value changes.
425
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.
431
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.
437 */
438 void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
439 {
440     Q_D(QQmlExpression);
441     d->setNotifyOnValueChanged(notifyOnChange);
442 }
443
444 /*!
445     Returns the source file URL for this expression.  The source location must
446     have been previously set by calling setSourceLocation().
447 */
448 QString QQmlExpression::sourceFile() const
449 {
450     Q_D(const QQmlExpression);
451     return d->url;
452 }
453
454 /*!
455     Returns the source file line number for this expression.  The source location
456     must have been previously set by calling setSourceLocation().
457 */
458 int QQmlExpression::lineNumber() const
459 {
460     Q_D(const QQmlExpression);
461     return d->line;
462 }
463
464 /*!
465     Returns the source file column number for this expression.  The source location
466     must have been previously set by calling setSourceLocation().
467 */
468 int QQmlExpression::columnNumber() const
469 {
470     Q_D(const QQmlExpression);
471     return d->column;
472 }
473
474 /*!
475     Set the location of this expression to \a line of \a url. This information
476     is used by the script engine.
477 */
478 void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
479 {
480     Q_D(QQmlExpression);
481     d->url = url;
482     d->line = line;
483     d->column = column;
484 }
485
486 /*!
487     Returns the expression's scope object, if provided, otherwise 0.
488
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.
491 */
492 QObject *QQmlExpression::scopeObject() const
493 {
494     Q_D(const QQmlExpression);
495     return d->scopeObject();
496 }
497
498 /*!
499     Returns true if the last call to evaluate() resulted in an error,
500     otherwise false.
501
502     \sa error(), clearError()
503 */
504 bool QQmlExpression::hasError() const
505 {
506     Q_D(const QQmlExpression);
507     return d->hasError();
508 }
509
510 /*!
511     Clear any expression errors.  Calls to hasError() following this will
512     return false.
513
514     \sa hasError(), error()
515 */
516 void QQmlExpression::clearError()
517 {
518     Q_D(QQmlExpression);
519     d->clearError();
520 }
521
522 /*!
523     Return any error from the last call to evaluate().  If there was no error,
524     this returns an invalid QQmlError instance.
525
526     \sa hasError(), clearError()
527 */
528
529 QQmlError QQmlExpression::error() const
530 {
531     Q_D(const QQmlExpression);
532     return d->error();
533 }
534
535 /*!
536     \fn void QQmlExpression::valueChanged()
537
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.
541 */
542
543 void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e)
544 {
545     QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
546     This->expressionChanged();
547 }
548
549 void QQmlExpressionPrivate::expressionChanged()
550 {
551     Q_Q(QQmlExpression);
552     emit q->valueChanged();
553 }
554
555 QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e)
556 {
557     QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
558     return QLatin1String("\"") + This->expression + QLatin1String("\"");
559 }
560
561 QT_END_NAMESPACE
562
563 #include <moc_qqmlexpression.cpp>